Introducción
El aprovisionamiento de infraestructura es uno de los pilares fundamentales del trabajo con Terraform. Antes de poder crear cualquier recurso en la nube, necesitas entender qué significa exactamente aprovisionar, cómo funciona el flujo de trabajo de Terraform y qué herramientas tienes disponibles para controlar ese proceso.
En este módulo aprenderás desde los conceptos más básicos hasta técnicas avanzadas como el aprovisionamiento parcial y la visualización del grafo de dependencias.
¿Qué Significa Aprovisionar Infraestructura?
Aprovisionar (o provisionar) infraestructura significa crear, configurar y preparar recursos de tecnología para que estén listos para su uso. Esto incluye:
- Crear servidores virtuales
- Configurar redes y subredes
- Establecer reglas de seguridad
- Crear bases de datos
- Asignar almacenamiento
- Configurar permisos y roles
Antes de herramientas como Terraform, este proceso era mayoritariamente manual: un administrador de sistemas iniciaba sesión en una consola web, hacía clic en varios menús y configuraba cada recurso de forma individual. Este enfoque tiene varios problemas:
- No es reproducible: dos entornos configurados manualmente nunca son exactamente iguales
- No está documentado: los cambios no quedan registrados automáticamente
- Es propenso a errores humanos: un clic incorrecto puede tener consecuencias graves
- No escala bien: gestionar cientos de recursos manualmente es inviable
Terraform resuelve todos estos problemas mediante la Infraestructura como Código (IaC).
Diferencia entre Aprovisionamiento Declarativo vs Imperativo
Existen dos grandes paradigmas para aprovisionar infraestructura:
Aprovisionamiento Imperativo
En el modelo imperativo, describes cómo llegar al estado deseado, paso a paso.
# Ejemplo imperativo (pseudocódigo de un script de shell)
aws ec2 create-vpc --cidr-block 10.0.0.0/16
VPC_ID=$(aws ec2 describe-vpcs --query "Vpcs[0].VpcId" --output text)
aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.1.0/24
SUBNET_ID=$(aws ec2 describe-subnets --query "Subnets[0].SubnetId" --output text)
aws ec2 run-instances --image-id ami-12345678 --subnet-id $SUBNET_IDProblemas del enfoque imperativo:
- Debes manejar el estado tú mismo
- Si el script falla a mitad, puede dejar la infraestructura en un estado inconsistente
- No puedes ejecutarlo dos veces sin modificarlo
Aprovisionamiento Declarativo
En el modelo declarativo, describes qué quieres que exista, y la herramienta determina cómo llegar ahí.
# Ejemplo declarativo con Terraform
resource "aws_vpc" "mi_vpc" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "mi_subred" {
vpc_id = aws_vpc.mi_vpc.id
cidr_block = "10.0.1.0/24"
}
resource "aws_instance" "mi_servidor" {
ami = "ami-12345678"
subnet_id = aws_subnet.mi_subred.id
}Ventajas del enfoque declarativo:
- Terraform maneja el estado automáticamente
- Puedes ejecutar el mismo código múltiples veces sin problemas (idempotente)
- Terraform calcula automáticamente el orden de creación de recursos
Tabla Comparativa
| Característica | Imperativo | Declarativo (Terraform) |
|---|---|---|
| Defines | Cómo hacerlo | Qué quieres |
| Gestión del estado | Manual | Automática |
| Idempotencia | Difícil de lograr | Incorporada |
| Reproducibilidad | Baja | Alta |
| Curva de aprendizaje | Más familiar al inicio | Requiere nuevo paradigma |
| Ejemplos | Scripts Bash, Ansible en modo push | Terraform, CloudFormation |
El Flujo de Trabajo de Aprovisionamiento con Terraform
El flujo de trabajo estándar de Terraform sigue estos pasos:
Cada paso tiene un propósito específico:
- Escribir código HCL: Defines los recursos que quieres crear
terraform init: Inicializa el directorio, descarga providers y módulosterraform plan: Muestra qué cambios se realizarán sin ejecutarlosterraform apply: Aplica los cambios al entorno realterraform destroy: Elimina todos los recursos gestionados
Planificación: terraform plan y Su Salida Explicada
terraform plan es uno de los comandos más importantes de Terraform. Analiza el código HCL, lo compara con el estado actual (archivo .tfstate) y genera un plan de ejecución detallado.
Ejecución básica
Guardar el plan en un archivo (recomendado en CI/CD)
# Guardar el plan para aplicarlo exactamente igual después
terraform plan -out=mi-plan.tfplan
# Luego aplicar ese plan exacto
terraform apply mi-plan.tfplanEjemplo de salida de terraform plan
Supongamos que tenemos este código:
# main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "ejemplo" {
bucket = "mi-bucket-terraform-ejemplo-2024"
tags = {
Environment = "desarrollo"
Team = "devops"
}
}Al ejecutar terraform plan, veremos una salida similar a esta:
Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_s3_bucket.ejemplo will be created
+ resource "aws_s3_bucket" "ejemplo" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = "mi-bucket-terraform-ejemplo-2024"
+ bucket_domain_name = (known after apply)
+ force_destroy = false
+ id = (known after apply)
+ object_lock_enabled = (known after apply)
+ region = (known after apply)
+ request_payer = (known after apply)
+ tags = {
+ "Environment" = "desarrollo"
+ "Team" = "devops"
}
+ tags_all = {
+ "Environment" = "desarrollo"
+ "Team" = "devops"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.Explicación de cada parte de la salida:
+ create: El símbolo+indica que este recurso será creado# aws_s3_bucket.ejemplo will be created: Identifica exactamente qué recurso se afectará- Los valores con
(known after apply)son atributos que AWS asignará automáticamente y que Terraform no puede predecir antes de la creación Plan: 1 to add, 0 to change, 0 to destroy: Resumen del plan
Tabla: Símbolos en el Plan de Terraform
Esta tabla es esencial para interpretar cualquier plan de Terraform:
| Símbolo | Significado | Descripción |
|---|---|---|
+ |
Create | El recurso se creará por primera vez |
- |
Destroy | El recurso será eliminado |
~ |
Update in-place | El recurso se modificará sin recrearlo (solo cambian atributos) |
-/+ |
Replace | El recurso debe ser destruido y recreado (cambio en atributo inmutable) |
<= |
Read | Data source que será leído (no crea infraestructura) |
! |
Tainted | El recurso está marcado para ser recreado en el próximo apply |
Ejemplo visual de cada símbolo
# Recurso nuevo (+)
+ resource "aws_instance" "web" {
+ ami = "ami-12345678"
}
# Actualización in-place (~)
~ resource "aws_instance" "web" {
~ tags = {
~ "Name" = "servidor-v1" -> "servidor-v2"
}
}
# Destrucción y recreación (-/+)
-/+ resource "aws_instance" "web" {
~ ami = "ami-12345678" -> "ami-87654321" # forces replacement
}
# Destrucción (-)
- resource "aws_instance" "web" {
- ami = "ami-12345678"
}Advertencia importante: Los recursos marcados con
-/+(replace) implican tiempo de inactividad. Un servidor EC2 marcado para reemplazo será destruido y recreado, lo que puede interrumpir el servicio. Siempre revisa cuidadosamente estos casos antes de aplicar.
Aplicación: terraform apply y Confirmación
Una vez revisado el plan, terraform apply ejecuta los cambios reales.
Ejecución interactiva
Terraform mostrará el plan completo y pedirá confirmación:
Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes
Debes escribir exactamente yes (no y ni si). Cualquier otra respuesta cancela la operación.
Aplicación automática (sin confirmación)
Útil en pipelines de CI/CD:
Precaución: Usa
-auto-approvesolo en entornos automatizados y controlados. En entornos de producción, siempre revisa el plan manualmente antes de aplicar.
Aplicar un plan guardado
La forma más segura en producción: primero genera y revisa el plan, luego aplícalo exactamente:
# Paso 1: Generar y guardar el plan
terraform plan -out=produccion.tfplan
# Paso 2: Revisar el plan (puedes compartirlo con tu equipo)
terraform show produccion.tfplan
# Paso 3: Aplicar exactamente ese plan
terraform apply produccion.tfplanDestrucción Controlada: terraform destroy
terraform destroy elimina todos los recursos gestionados por Terraform en el directorio actual.
Al igual que apply, pedirá confirmación antes de ejecutarse:
Do you really want to destroy all resources? Terraform will destroy all your managed infrastructure, as shown above. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes
Destrucción sin confirmación
¿Cuándo usar terraform destroy?
- Eliminar entornos de desarrollo o prueba después de usarlos
- Limpiar infraestructura temporal de demos o experimentos
- Reducir costos eliminando recursos no necesarios
Importante:
terraform destroyno elimina el archivo de estado (.tfstate). Solo elimina la infraestructura real. El archivo de estado queda vacío después de la destrucción.
Aprovisionamiento Parcial: El Flag -target
En ocasiones, necesitas aplicar cambios solo a un recurso específico sin afectar el resto de la infraestructura. Para esto existe el flag -target.
Sintaxis
# Plan solo para un recurso específico
terraform plan -target=aws_instance.mi_servidor
# Apply solo para un recurso específico
terraform apply -target=aws_instance.mi_servidor
# Apply para un recurso dentro de un módulo
terraform apply -target=module.red.aws_vpc.principal
# Apply para múltiples recursos (múltiples flags -target)
terraform apply -target=aws_instance.web -target=aws_security_group.web_sgEjemplo práctico
Imagina que tienes este código y solo quieres crear el bucket S3 sin tocar el servidor EC2:
resource "aws_s3_bucket" "logs" {
bucket = "mis-logs-aplicacion"
}
resource "aws_instance" "app" {
ami = "ami-12345678"
instance_type = "t3.micro"
}Cuándo usar -target y cuándo evitarlo
Casos de uso válidos:
- Resolver problemas de orden de creación cuando hay dependencias circulares
- Recuperarse de un error en un recurso específico
- Importar un recurso existente y luego gestionarlo gradualmente
Cuándo evitarlo:
- Como práctica habitual (sugiere que el código tiene problemas de diseño)
- Para omitir recursos "molestos" sistemáticamente
- En lugar de refactorizar el código correctamente
Advertencia de Terraform: Cuando usas
-target, Terraform te muestra esta advertencia:"Resource targeting is in effect. Note that the -target option is not suitable for routine use, and is provided only for exceptional circumstances."Esto es intencional:-targetes una herramienta de emergencia, no una práctica regular.
Orden de Creación de Recursos: El Grafo de Dependencias
Terraform analiza automáticamente las dependencias entre recursos y los crea en el orden correcto. Existen dos tipos de dependencias:
Dependencias Implícitas
Se crean automáticamente cuando un recurso referencia el atributo de otro:
resource "aws_vpc" "principal" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "publica" {
# Al referenciar aws_vpc.principal.id, Terraform sabe que debe
# crear la VPC ANTES que la subred
vpc_id = aws_vpc.principal.id
cidr_block = "10.0.1.0/24"
}Dependencias Explícitas con depends_on
Cuando la dependencia no es evidente por referencias directas:
resource "aws_iam_role_policy" "politica" {
name = "mi-politica"
role = aws_iam_role.rol.id
policy = data.aws_iam_policy_document.documento.json
}
resource "aws_instance" "app" {
ami = "ami-12345678"
instance_type = "t3.micro"
# La instancia necesita que la política IAM exista primero,
# aunque no la referencia directamente en sus atributos
depends_on = [aws_iam_role_policy.politica]
}terraform graph y Cómo Visualizarlo
terraform graph genera una representación del grafo de dependencias en formato DOT (lenguaje de Graphviz).
# Generar el grafo en formato DOT
terraform graph
# Guardar el grafo en un archivo
terraform graph > mi-infraestructura.dotVisualizar el grafo con Graphviz
# Instalar Graphviz (Ubuntu/Debian)
sudo apt-get install graphviz
# Convertir el archivo DOT a imagen PNG
terraform graph | dot -Tpng > grafo.png
# Convertir a SVG (mejor calidad para zooms)
terraform graph | dot -Tsvg > grafo.svgVisualización online
También puedes usar herramientas online como Blast Radius o simplemente pegar el contenido DOT en WebGraphviz.
Ejemplo de salida de terraform graph
digraph {
compound = "true"
newrank = "true"
subgraph "root" {
"[root] aws_instance.app (expand)" [label = "aws_instance.app", shape = "box"]
"[root] aws_security_group.web (expand)" [label = "aws_security_group.web", shape = "box"]
"[root] aws_vpc.principal (expand)" [label = "aws_vpc.principal", shape = "box"]
"[root] aws_instance.app (expand)" -> "[root] aws_security_group.web (expand)"
"[root] aws_security_group.web (expand)" -> "[root] aws_vpc.principal (expand)"
}
}Este grafo nos dice que:
- Primero se crea
aws_vpc.principal - Luego
aws_security_group.web(depende de la VPC) - Finalmente
aws_instance.app(depende del Security Group)
Paralelismo en Terraform: El Flag -parallelism
Por defecto, Terraform crea hasta 10 recursos en paralelo cuando no hay dependencias entre ellos. Puedes ajustar este valor:
# Aumentar el paralelismo (más velocidad, más carga en la API)
terraform apply -parallelism=20
# Reducir el paralelismo (útil cuando la API tiene rate limiting)
terraform apply -parallelism=2
# Deshabilitar el paralelismo (1 recurso a la vez)
terraform apply -parallelism=1Cuándo ajustar el paralelismo
Aumentar:
- Tienes muchos recursos independientes y la aplicación tarda mucho
- Tu proveedor de nube soporta alta concurrencia
Reducir:
- Encuentras errores de "Rate limit exceeded" de la API del proveedor
- Estás depurando un problema y quieres ver los recursos crearse uno a uno
- Tienes recursos que, aunque Terraform no los vea como dependientes, en realidad sí lo son
Ejercicio Práctico: Interpretar un Plan de Terraform Complejo
Analiza el siguiente plan de Terraform y responde las preguntas al final:
Terraform will perform the following actions:
# aws_instance.web_server will be created
+ resource "aws_instance" "web_server" {
+ ami = "ami-0c55b159cbfafe1f0"
+ instance_type = "t3.micro"
+ subnet_id = (known after apply)
+ tags = {
+ "Environment" = "staging"
+ "Name" = "web-server-staging"
}
+ vpc_security_group_ids = (known after apply)
}
# aws_security_group.web_sg will be created
+ resource "aws_security_group" "web_sg" {
+ description = "Security group para el servidor web"
+ name = "web-sg-staging"
+ vpc_id = (known after apply)
}
# aws_s3_bucket.logs will be destroyed
- resource "aws_s3_bucket" "logs" {
- bucket = "mi-app-logs-produccion"
- tags = {}
}
# aws_db_instance.database will be updated in-place
~ resource "aws_db_instance" "database" {
id = "mi-base-datos"
~ backup_retention_period = 7 -> 14
# (otros atributos sin cambios)
}
# aws_elasticache_cluster.cache must be replaced
-/+ resource "aws_elasticache_cluster" "cache" {
~ id = "mi-cache" -> (known after apply)
~ num_cache_nodes = 1 -> 3
+ parameter_group_name = "default.redis6.x" # forces replacement
}
Plan: 2 to add, 1 to change, 1 to destroy.Preguntas:
- ¿Cuántos recursos se van a crear?
- ¿Qué recurso se va a eliminar y por qué podría ser peligroso?
- ¿Qué cambio se hará en la base de datos?
- ¿Por qué el cluster de ElastiCache necesita ser reemplazado?
- ¿El resumen del plan es correcto? ¿Por qué o por qué no?
Solución con Análisis Detallado
Respuesta 1: Recursos a crear
Se crearán 2 recursos:
aws_instance.web_server: Un servidor EC2 con AMIami-0c55b159cbfafe1f0de tipot3.microaws_security_group.web_sg: Un security group llamadoweb-sg-staging
Nota importante: Ambos tienen atributos con (known after apply). Por ejemplo, aws_instance.web_server tiene vpc_security_group_ids = (known after apply), lo que indica que este valor se asignará automáticamente por AWS o se tomará de otro recurso.
Respuesta 2: Recurso a eliminar
Se eliminará aws_s3_bucket.logs, el bucket S3 llamado mi-app-logs-produccion.
¿Por qué es peligroso? El nombre del bucket incluye "produccion" y podría contener logs importantes de la aplicación. Antes de destruir un bucket S3, deberías verificar:
- ¿El bucket está vacío? (Terraform no puede destruir buckets con contenido por defecto)
- ¿Los logs son necesarios para auditoría o cumplimiento legal?
- ¿Por qué se eliminó este recurso del código HCL?
Respuesta 3: Cambio en la base de datos
La aws_db_instance.database se actualizará in-place (símbolo ~), lo que significa que la base de datos no se recreará. El único cambio es:
backup_retention_period: de 7 días → 14 días
Este es un cambio seguro y sin tiempo de inactividad en RDS.
Respuesta 4: Reemplazo del cluster ElastiCache
El cluster aws_elasticache_cluster.cache necesita ser reemplazado (símbolo -/+) porque se está cambiando el atributo parameter_group_name. En AWS ElastiCache, el grupo de parámetros es inmutable: no se puede cambiar en un cluster existente. Terraform debe destruir el cluster actual y crear uno nuevo.
Implicaciones:
- El cluster estará temporalmente inaccesible (tiempo de inactividad)
- Se pierden todos los datos en caché (normal para un caché)
num_cache_nodescambia de 1 a 3, lo que indica que también se está escalando
Respuesta 5: ¿El resumen es correcto?
El resumen dice: Plan: 2 to add, 1 to change, 1 to destroy
Analicemos:
- 2 to add: Correcto (web_server + web_sg)
- 1 to change: Correcto (database actualización in-place)
- 1 to destroy: ¡Incorrecto! El resumen cuenta solo 1 destrucción (el bucket S3), pero el reemplazo del cluster ElastiCache implica también una destrucción seguida de una creación.
En realidad, si contamos operaciones reales:
- 3 to add: 2 nuevos + 1 por el reemplazo de ElastiCache
- 1 to change: la database
- 2 to destroy: el bucket S3 + el cluster ElastiCache existente
Terraform agrupa el reemplazo (-/+) como una sola operación en el resumen, pero en la ejecución real implica destrucción y creación.
Errores Comunes en el Aprovisionamiento
Error 1: Conflicto de nombre de recurso ya existente
Error: creating S3 Bucket (mi-bucket): BucketAlreadyExists: The requested bucket name is not available
Solución: Si el bucket ya existe en AWS pero no en el estado de Terraform, usa terraform import:
Error 2: Dependencias circulares
Solución: Revisa las referencias entre recursos y elimina la referencia circular. Usa depends_on solo cuando sea necesario.
Error 3: Permisos insuficientes
Error: error creating EC2 Instance: UnauthorizedOperation: You are not authorized to perform this operation.
Solución: Verifica que las credenciales de AWS tienen los permisos IAM necesarios. En desarrollo, el rol debe tener las políticas apropiadas.
Error 4: Intento de modificar un atributo inmutable
Error: modifying RDS DB Instance (mi-bd): InvalidParameterCombination: Cannot modify the engine version to 8.0.28 (current version: 5.7.44)
Solución: Algunos atributos de recursos AWS no se pueden modificar en caliente. Terraform puede requerir recrear el recurso. Verifica la documentación del recurso para saber qué atributos son inmutables.
Error 5: Estado desincronizado
Solución: Alguien modificó la infraestructura manualmente fuera de Terraform. Usa terraform refresh para sincronizar el estado, o terraform import para importar recursos existentes.
Consejos Adicionales
-
Siempre revisa el plan antes de aplicar: Nunca ejecutes
terraform apply -auto-approveen producción sin haber revisado el plan previamente. -
Usa
-outpara planes importantes: Guardar el plan garantiza que aplicarás exactamente lo que revisaste, sin sorpresas. -
Entiende los valores
(known after apply): No es un error, es Terraform diciéndote que ese valor será determinado por el proveedor en el momento de la creación. -
Los reemplazos son peligrosos en producción: Un
-/+en un servidor o base de datos implica tiempo de inactividad. Planifica mantenimientos para estos cambios. -
El grafo de dependencias es tu amigo: Cuando algo falla por orden incorrecto, visualiza el grafo para entender qué depende de qué.
Resumen y Próximos Pasos
En este módulo has aprendido los fundamentos del aprovisionamiento con Terraform:
- La diferencia entre el modelo declarativo (Terraform) y el imperativo (scripts)
- El flujo de trabajo completo:
init→plan→apply→destroy - Cómo interpretar la salida de
terraform plany sus símbolos (+,-,~,-/+) - El flag
-targetpara aprovisionamiento parcial y cuándo usarlo - Cómo Terraform gestiona las dependencias mediante el grafo de recursos
- La herramienta
terraform graphpara visualizar dependencias - El control del paralelismo con
-parallelism - Los errores más comunes y cómo resolverlos
Con estos fundamentos sólidos, en los próximos módulos aplicarás estos conocimientos a proveedores de nube reales. Comenzaremos con AWS en el siguiente módulo, donde verás cómo configurar el provider, autenticarte y crear infraestructura real paso a paso.
Tarea de preparación: Antes del siguiente módulo, asegúrate de tener una cuenta de AWS con credenciales configuradas (
aws configure) o variables de entorno configuradas. También instala la AWS CLI si no la tienes.
Curso de Terraform
Módulo 1: Introducción a Terraform
- ¿Qué es Terraform?
- Instalando Terraform
- Conceptos Básicos de Terraform
- Primera Configuración de Terraform
Módulo 2: Lenguaje de Configuración de Terraform
Módulo 3: Gestión del Estado
Módulo 4: Módulos de Terraform
Módulo 5: Aprovisionamiento de Recursos
- Conceptos Básicos de Aprovisionamiento
- Aprovisionamiento de Recursos AWS
- Aprovisionamiento de Recursos Azure
- Aprovisionamiento de Recursos GCP
Módulo 6: Funcionalidades Avanzadas de Terraform
Módulo 7: Mejores Prácticas de Terraform
- Organización del Código
- Control de Versiones
- Pruebas del Código de Terraform
- Mejores Prácticas de Seguridad
Módulo 8: Terraform en CI/CD
- Integración de Terraform con CI/CD
- Automatización de Terraform con Jenkins
- Uso de Terraform con GitHub Actions
- Terraform Cloud y Enterprise
