Introducción
Antes de escribir tu primera configuración real, es fundamental entender el modelo mental con el que trabaja Terraform. Sin este fundamento conceptual, los comandos y los archivos de configuración parecen arbitrarios. Con él, todo cobra sentido inmediatamente.
En esta lección aprenderás los cuatro pilares conceptuales de Terraform (proveedores, recursos, estado y el ciclo plan/apply), los comandos que usarás a diario, y la estructura de archivos que Terraform crea en tu directorio de trabajo.
- Proveedores (Providers): La Puerta de Acceso a la Infraestructura
¿Qué es un proveedor?
Un proveedor es un plugin que Terraform usa para comunicarse con una plataforma externa. Cada proveedor expone un conjunto de recursos y fuentes de datos que Terraform puede gestionar.
Piensa en los proveedores como "adaptadores": Terraform habla un lenguaje universal (HCL), y los proveedores hacen de traductores entre ese lenguaje y las APIs específicas de cada plataforma.
¿De dónde vienen los proveedores?
Los proveedores se distribuyen a través del Terraform Registry (registry.terraform.io). Existen tres categorías:
- Oficial: Desarrollados y mantenidos por HashiCorp (AWS, Azure, GCP, Kubernetes...)
- Partner: Desarrollados y mantenidos por empresas que se han asociado con HashiCorp (Datadog, MongoDB, PagerDuty...)
- Community: Desarrollados y mantenidos por la comunidad (cualquier persona puede publicar un proveedor)
Ejemplos de proveedores populares
| Proveedor | Identificador | Qué gestiona |
|---|---|---|
| AWS | hashicorp/aws |
Todos los servicios de Amazon Web Services |
| Azure | hashicorp/azurerm |
Todos los servicios de Microsoft Azure |
| Google Cloud | hashicorp/google |
Todos los servicios de Google Cloud Platform |
| Kubernetes | hashicorp/kubernetes |
Clústeres y recursos de Kubernetes |
| GitHub | integrations/github |
Repositorios, equipos, permisos en GitHub |
| Datadog | DataDog/datadog |
Monitores, dashboards, alertas en Datadog |
| HashiCorp Local | hashicorp/local |
Archivos en el sistema de archivos local |
| Random | hashicorp/random |
Generación de valores aleatorios |
| Time | hashicorp/time |
Recursos de tiempo (útil para esperas) |
Cómo se declara un proveedor
# En un archivo .tf (normalmente providers.tf o main.tf)
terraform {
required_providers {
# El bloque "aws" define el proveedor de AWS
aws = {
# "source" indica dónde descargar el proveedor
# Formato: <namespace>/<nombre>
source = "hashicorp/aws"
# "version" restringe qué versiones del proveedor se pueden usar
# ~> 5.0 significa: cualquier versión >= 5.0.0 y < 6.0.0
version = "~> 5.0"
}
}
}
# El bloque "provider" configura el proveedor
# (credenciales, región, etc.)
provider "aws" {
region = "eu-west-1" # Región de AWS donde se crearán los recursos
}Cada proveedor tiene su propia configuración. El proveedor de AWS necesita región y credenciales. El proveedor local (para archivos locales) no necesita ninguna configuración especial porque no accede a ningún servicio externo.
- Recursos (Resources): La Unidad Básica de Infraestructura
¿Qué es un recurso?
Un recurso representa un componente de infraestructura que Terraform crea, gestiona y destruye. Es el elemento más fundamental en Terraform.
Ejemplos de recursos:
- Una instancia EC2 en AWS
- Un grupo de recursos en Azure
- Una tabla en DynamoDB
- Un registro DNS en Route53
- Un archivo en el sistema de archivos local
- Un repositorio en GitHub
Anatomía de un bloque de recurso
resource "tipo_del_recurso" "nombre_local" {
# Argumentos que configuran el recurso
argumento1 = "valor1"
argumento2 = "valor2"
}Desglose de cada parte:
resource: palabra clave que indica que este bloque define un recurso"tipo_del_recurso": cadena en formato<proveedor>_<tipo>que identifica exactamente qué tipo de infraestructura crear (por ejemplo,aws_instance,azurerm_resource_group,local_file)"nombre_local": un nombre que tú eliges, usado para referenciar este recurso desde otros lugares del código. Solo existe dentro de Terraform, no se usa para nombrar el recurso en la plataforma externa.- El bloque
{ }contiene los argumentos que configuran el recurso. Qué argumentos están disponibles y cuáles son obligatorios depende de cada tipo de recurso.
Ejemplo práctico comentado
# Este recurso crea una instancia EC2 en AWS
# "aws_instance" es el tipo → le dice a Terraform que use el proveedor "aws"
# "servidor_web" es el nombre local → podemos usar aws_instance.servidor_web
# para referenciarlo desde otros recursos
resource "aws_instance" "servidor_web" {
# ami: Amazon Machine Image (imagen del sistema operativo)
# ami-0c55b159cbfafe1f0 es Ubuntu 20.04 en us-east-1
ami = "ami-0c55b159cbfafe1f0"
# instance_type: el tipo de instancia determina CPU y RAM
# t2.micro es el más pequeño y entra en el free tier de AWS
instance_type = "t2.micro"
# tags: etiquetas que se aplican al recurso en AWS
# Útiles para organizar y filtrar recursos
tags = {
Name = "mi-servidor-web"
Environment = "desarrollo"
ManagedBy = "terraform"
}
}Referenciando recursos entre sí
Los recursos pueden referenciarse entre sí usando la sintaxis <tipo>.<nombre>.<atributo>:
resource "aws_vpc" "red_principal" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "subred_publica" {
# Aquí referenciamos el VPC creado arriba
# aws_vpc.red_principal.id → el ID del VPC, asignado por AWS tras crearlo
vpc_id = aws_vpc.red_principal.id
cidr_block = "10.0.1.0/24"
}Terraform detecta automáticamente esta dependencia y crea el VPC antes que la subred.
- Estado (State): El Mapa de lo que Existe
¿Qué es el estado de Terraform?
El estado (state) es el mecanismo que Terraform usa para rastrear qué infraestructura existe en el mundo real. Se almacena en un archivo llamado terraform.tfstate en formato JSON.
El estado es el puente entre tu configuración (lo que quieres) y la realidad (lo que existe). Sin el estado, Terraform no sabría:
- Qué recursos ya ha creado
- Cuáles son los IDs reales asignados por el proveedor (como el ID de una instancia EC2)
- Qué atributos tienen los recursos para que otros recursos puedan referenciarlos
El ciclo de vida del estado
┌─────────────┐ ┌────────────────┐ ┌──────────────────┐ │ Configuración│ │ Estado │ │ Infraestructura │ │ (.tf) │──plan──▶│ (tfstate) │──apply──▶│ Real (AWS, etc.) │ │ │ │ │ │ │ │ "Quiero X" │ │ "Terraform │ │ "X existe con │ │ │ │ creó X con │ │ ID=abc123" │ │ │ │ ID=abc123" │ │ │ └─────────────┘ └────────────────┘ └──────────────────┘
Por qué el estado es importante
Considera este escenario: creas una instancia EC2 con Terraform. AWS le asigna automáticamente el ID i-0123456789abcdef0. Si luego quieres modificar esa instancia (cambiar el tipo), Terraform necesita saber que el recurso llamado aws_instance.servidor_web en tu código corresponde a la instancia con ID i-0123456789abcdef0 en AWS. Esa información está en el estado.
El archivo terraform.tfstate
{
"version": 4,
"terraform_version": "1.9.0",
"serial": 3,
"lineage": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "aws_instance",
"name": "servidor_web",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
"id": "i-0123456789abcdef0",
"ami": "ami-0c55b159cbfafe1f0",
"instance_type": "t2.micro",
"tags": {
"Name": "mi-servidor-web"
}
}
}
]
}
]
}Advertencia importante: Nunca edites el archivo
terraform.tfstatemanualmente. Si necesitas modificar el estado, usa los comandosterraform stateque veremos más adelante. Editar el archivo a mano puede dejarlo en un estado inconsistente y corromper tu infraestructura.
- Plan vs Apply: La Diferencia Entre Previsualizar y Ejecutar
terraform plan
terraform plan lee tu configuración y el estado actual, calcula qué cambios son necesarios y los muestra, pero no ejecuta nada. Es completamente seguro ejecutarlo en cualquier momento.
La salida del plan usa un sistema de símbolos:
| Símbolo | Significado | Color |
|---|---|---|
+ |
El recurso será creado | Verde |
- |
El recurso será destruido | Rojo |
~ |
El recurso será modificado en su lugar | Amarillo |
-/+ |
El recurso será destruido y recreado | Rojo/Verde |
<= |
Los datos serán leídos (data sources) | Cyan |
Ejemplo de salida de terraform plan:
Terraform will perform the following actions:
# aws_instance.servidor_web will be created
+ resource "aws_instance" "servidor_web" {
+ ami = "ami-0c55b159cbfafe1f0"
+ id = (known after apply)
+ instance_type = "t2.micro"
+ tags = {
+ "Environment" = "desarrollo"
+ "Name" = "mi-servidor-web"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.El (known after apply) aparece en atributos que solo se conocen después de crear el recurso (como el ID que asigna AWS).
terraform apply
terraform apply ejecuta los cambios planificados. Por defecto, muestra el plan y pide confirmación antes de proceder:
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 "yes" exactamente
Para automatización (en CI/CD), puedes usar el flag -auto-approve para omitir la confirmación:
Consejo: Nunca uses
-auto-approveen entornos de producción a menos que tengas muy claro lo que va a ocurrir. El paso de confirmación es una salvaguarda importante.
- Comandos Principales de Terraform
Tabla resumen de comandos
| Comando | Descripción | Cuándo usarlo | Ejemplo |
|---|---|---|---|
terraform init |
Inicializa el directorio de trabajo: descarga proveedores y módulos | Siempre primero, y cuando cambias proveedores o módulos | terraform init |
terraform plan |
Muestra qué cambios se van a realizar sin ejecutarlos | Antes de cualquier apply para revisar los cambios | terraform plan |
terraform apply |
Aplica los cambios planificados a la infraestructura real | Cuando has revisado el plan y quieres ejecutar los cambios | terraform apply |
terraform destroy |
Elimina todos los recursos gestionados por Terraform | Para limpiar entornos de prueba o cuando ya no necesitas la infraestructura | terraform destroy |
terraform show |
Muestra el estado actual en formato legible | Para inspeccionar los recursos existentes y sus atributos | terraform show |
terraform state list |
Lista todos los recursos en el estado | Para ver qué recursos gestiona Terraform | terraform state list |
terraform state show |
Muestra los atributos de un recurso específico | Para ver los detalles de un recurso concreto | terraform state show aws_instance.web |
terraform output |
Muestra los valores de las variables de salida | Después de apply, para ver las IPs, URLs, etc. | terraform output |
terraform validate |
Valida la sintaxis de los archivos de configuración | Para verificar que el código HCL es sintácticamente correcto | terraform validate |
terraform fmt |
Formatea los archivos .tf con el estilo estándar | Antes de commitear el código | terraform fmt |
terraform refresh |
Actualiza el estado con los valores reales del proveedor | Cuando alguien modificó la infraestructura fuera de Terraform | terraform refresh |
terraform import |
Importa recursos existentes al estado de Terraform | Para adoptar infraestructura creada manualmente | terraform import aws_instance.web i-abc123 |
Detalles de los comandos más importantes
terraform init
# Forma básica
terraform init
# Reinicializar sin preguntar (útil en CI/CD)
terraform init -input=false
# Actualizar los proveedores a las últimas versiones compatibles
terraform init -upgrade
# Usar un backend específico (para estado remoto)
terraform init -backend-config="bucket=mi-bucket-tfstate"Lo que hace terraform init:
- Lee los bloques
terraform {}yrequired_providers {}del código - Descarga los proveedores necesarios al directorio
.terraform/providers/ - Descarga los módulos externos al directorio
.terraform/modules/ - Crea (o migra) el backend de estado
- Genera el archivo
.terraform.lock.hclcon los hashes exactos de los proveedores
terraform plan
# Plan básico
terraform plan
# Guardar el plan en un archivo para aplicarlo después
# (garantiza que se aplica exactamente lo que se vio)
terraform plan -out=mi-plan.tfplan
# Aplicar un plan guardado
terraform apply mi-plan.tfplan
# Plan con variables
terraform plan -var="region=us-east-1" -var="instancias=3"
# Plan con archivo de variables
terraform plan -var-file="produccion.tfvars"terraform destroy
# Destruir todos los recursos (pide confirmación)
terraform destroy
# Destruir sin pedir confirmación (¡usar con mucho cuidado!)
terraform destroy -auto-approve
# Destruir solo un recurso específico
terraform destroy -target=aws_instance.servidor_web
- El Directorio .terraform y su Contenido
Cuando ejecutas terraform init, Terraform crea un directorio oculto llamado .terraform en tu directorio de trabajo. Este directorio contiene:
.terraform/
├── providers/
│ └── registry.terraform.io/
│ └── hashicorp/
│ └── aws/
│ └── 5.0.1/
│ └── linux_amd64/
│ └── terraform-provider-aws_v5.0.1_x5
└── modules/
└── (módulos descargados, si los hay)El archivo .terraform.lock.hcl
Junto con el directorio .terraform, terraform init también crea (o actualiza) el archivo .terraform.lock.hcl:
# Este archivo es generado automáticamente por terraform init
# NO lo edites manualmente
# SÍ debes commitarlo al control de versiones
provider "registry.terraform.io/hashicorp/aws" {
# La versión exacta que se instaló
version = "5.0.1"
# Restricciones de versión declaradas en tu código
constraints = "~> 5.0"
# Hashes criptográficos para verificar la integridad del proveedor
# en diferentes plataformas
hashes = [
"h1:xyz123...",
"zh:abc456...",
]
}El propósito del lockfile es garantizar que todos los miembros del equipo y los pipelines de CI/CD usen exactamente la misma versión del proveedor, incluso si hay nuevas versiones disponibles.
Regla de oro: Agrega
.terraform/a tu.gitignore(no commitear los binarios de proveedores), pero sí commitea.terraform.lock.hcl(garantiza reproducibilidad del entorno).
- El Grafo de Dependencias
Terraform construye automáticamente un grafo de dependencias dirigido acíclico (DAG) entre los recursos. Este grafo determina en qué orden crear, modificar o destruir los recursos.
Dependencias implícitas
Cuando un recurso referencia un atributo de otro recurso, Terraform detecta automáticamente la dependencia:
resource "aws_vpc" "principal" {
cidr_block = "10.0.0.0/16"
}
# Este recurso depende implícitamente de aws_vpc.principal
# porque usa aws_vpc.principal.id
resource "aws_subnet" "subred" {
vpc_id = aws_vpc.principal.id # ← dependencia implícita
cidr_block = "10.0.1.0/24"
}
# Este recurso depende de aws_subnet.subred
resource "aws_instance" "servidor" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = aws_subnet.subred.id # ← dependencia implícita
}Terraform creará estos recursos en orden: VPC → Subred → Instancia.
Dependencias explícitas
A veces dos recursos están relacionados lógicamente pero Terraform no puede detectar la dependencia automáticamente. En ese caso usas depends_on:
resource "aws_s3_bucket" "datos" {
bucket = "mi-bucket-de-datos"
}
resource "aws_instance" "procesador" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
# Este servidor necesita que el bucket exista antes de iniciarse
# pero no referencia ningún atributo del bucket directamente
depends_on = [aws_s3_bucket.datos]
}Ver el grafo de dependencias
# Generar el grafo en formato DOT (lenguaje de grafos)
terraform graph
# Visualizar el grafo (requiere graphviz instalado)
terraform graph | dot -Tsvg > grafo.svg
- Ejercicio Práctico: Identificar los Comandos Correctos
Enunciado
Para cada uno de los siguientes escenarios, indica qué comando o comandos de Terraform deberías usar y explica por qué.
Escenarios
Escenario A: Acabas de clonar un repositorio de Terraform de tu empresa por primera vez y quieres empezar a trabajar.
Escenario B: Has modificado el tipo de instancia de un servidor de t2.micro a t2.small en el archivo .tf y quieres ver exactamente qué va a cambiar antes de aplicarlo.
Escenario C: Tu compañero menciona que cambió manualmente el nombre de un bucket S3 desde la consola web de AWS. Quieres que el estado de Terraform refleje ese cambio.
Escenario D: Necesitas destruir únicamente un recurso específico (una base de datos de prueba) sin tocar el resto de la infraestructura.
Escenario E: Quieres ver qué recursos gestiona actualmente Terraform y cuáles son sus IDs reales.
Escenario F: Antes de commitear tus cambios, quieres asegurarte de que el código HCL no tiene errores de sintaxis y está bien formateado.
Solución
Escenario A:
Siempre debes ejecutar terraform init antes de cualquier otra operación en un directorio de trabajo nuevo o clonado.
Escenario B:
terraform plan es la herramienta perfecta para previsualizar cambios. Mostrará que el recurso será modificado (~) con el cambio de t2.micro a t2.small.
Escenario C:
terraform refresh # Actualiza el estado con los valores reales
# O en versiones modernas de Terraform:
terraform apply -refresh-onlyrefresh actualiza el estado con los valores actuales del proveedor sin crear ni modificar recursos.
Escenario D:
El flag -target permite destruir solo el recurso especificado. El resto de la infraestructura no se ve afectada.
Escenario E:
terraform state list # Lista todos los recursos
terraform state show <nombre_del_recurso> # Detalle de uno específicoEscenario F:
- Errores Comunes de Principiantes
"Olvidar ejecutar terraform init"
Error: Error: Inconsistent dependency lock file
Causa: Has agregado o cambiado un proveedor pero no has ejecutado terraform init.
"Ejecutar terraform apply sin revisar el plan"
Un principiante ejecuta terraform apply directamente sin revisar el plan. El apply muestra el plan y pide confirmación, pero muchos escriben yes sin leerlo. Esto puede resultar en la destrucción accidental de recursos.
Buena práctica: Separa siempre el plan del apply:
terraform plan -out=plan.tfplan # Guardar el plan
# ... revisar el plan cuidadosamente ...
terraform apply plan.tfplan # Aplicar exactamente ese plan"Modificar terraform.tfstate manualmente"
El estado es un archivo JSON y es tentador editarlo directamente. Nunca lo hagas. Usa los comandos terraform state:
# Mover un recurso dentro del estado (renombrarlo)
terraform state mv aws_instance.viejo_nombre aws_instance.nuevo_nombre
# Eliminar un recurso del estado sin destruirlo
terraform state rm aws_instance.servidor
# Importar un recurso existente al estado
terraform import aws_instance.servidor i-0123456789abcdef0"Commitear el archivo terraform.tfstate al repositorio"
El archivo de estado puede contener información sensible (contraseñas, claves API). No debes commitearlo en repositorios públicos. Usa siempre un backend remoto (como S3, Terraform Cloud, etc.) para el estado.
En tu .gitignore:
"No entender la diferencia entre plan y apply"
Algunos principiantes piensan que terraform plan es solo un paso opcional. En realidad, siempre debes revisar el plan antes de aplicar, especialmente en producción. Un plan te puede mostrar que vas a destruir un recurso que no querías destruir.
Resumen y Preparación para el Siguiente Tema
En esta lección has aprendido los cuatro pilares conceptuales de Terraform:
- Proveedores: Plugins que conectan Terraform con plataformas externas (AWS, Azure, GitHub...). Se declaran en el bloque
required_providersy se configuran con el bloqueprovider. - Recursos: La unidad básica de infraestructura. Se declaran con el bloque
resource "tipo" "nombre"y representan un componente concreto (un servidor, una base de datos, un archivo). - Estado: El archivo
terraform.tfstateque mantiene el mapa entre tu configuración y la infraestructura real. Es el corazón de Terraform y nunca debe editarse manualmente. - Plan vs Apply:
terraform planmuestra qué va a cambiar sin ejecutar nada.terraform applyejecuta los cambios. Siempre revisa el plan antes de aplicar.
También has aprendido los comandos principales (init, plan, apply, destroy, show, state, validate, fmt) y cuándo usar cada uno, así como la estructura de archivos que Terraform crea en tu directorio de trabajo.
En la siguiente lección pondrás todo esto en práctica creando tu primera configuración real de Terraform: crearás archivos en tu sistema local usando el proveedor hashicorp/local, sin necesidad de credenciales de nube, y ejecutarás el ciclo completo init → plan → apply → show → destroy.
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
