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.


  1. 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.


  1. 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.


  1. 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.tfstate manualmente. Si necesitas modificar el estado, usa los comandos terraform state que veremos más adelante. Editar el archivo a mano puede dejarlo en un estado inconsistente y corromper tu infraestructura.


  1. 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:

# En pipelines de CI/CD donde no hay interacción humana
terraform apply -auto-approve

Consejo: Nunca uses -auto-approve en entornos de producción a menos que tengas muy claro lo que va a ocurrir. El paso de confirmación es una salvaguarda importante.


  1. 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:

  1. Lee los bloques terraform {} y required_providers {} del código
  2. Descarga los proveedores necesarios al directorio .terraform/providers/
  3. Descarga los módulos externos al directorio .terraform/modules/
  4. Crea (o migra) el backend de estado
  5. Genera el archivo .terraform.lock.hcl con 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

  1. 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).


  1. 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

  1. 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:

terraform init  # Descarga los proveedores y módulos necesarios

Siempre debes ejecutar terraform init antes de cualquier otra operación en un directorio de trabajo nuevo o clonado.

Escenario B:

terraform plan  # Muestra los cambios sin ejecutarlos

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-only

refresh actualiza el estado con los valores actuales del proveedor sin crear ni modificar recursos.

Escenario D:

terraform destroy -target=aws_db_instance.base_de_datos_prueba

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ífico

Escenario F:

terraform validate  # Verifica la sintaxis
terraform fmt       # Formatea el código con el estilo estándar

  1. 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.

# Solución
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:

# Terraform
.terraform/
*.tfstate
*.tfstate.backup
*.tfplan
.terraform.lock.hcl.lock

"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_providers y se configuran con el bloque provider.
  • 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.tfstate que 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 plan muestra qué va a cambiar sin ejecutar nada. terraform apply ejecuta 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.

© Copyright 2026. Todos los derechos reservados