Introducción

Trabajar con Terraform implica inevitablemente encontrarse con errores. Lejos de ser una señal de incompetencia, saber interpretar, diagnosticar y resolver errores es una de las habilidades más valiosas que puede desarrollar un profesional de infraestructura como código. En este módulo aprenderás a leer los mensajes de error de Terraform con confianza, entender sus categorías y aplicar soluciones sistemáticas.


  1. Cómo Leer los Mensajes de Error de Terraform

Terraform produce mensajes de error estructurados y, en la mayoría de los casos, bastante informativos. Sin embargo, para un principiante pueden resultar intimidantes. Veamos cómo descomponerlos.

Estructura de un mensaje de error típico

╷
│ Error: Unsupported argument
│
│   on main.tf line 12, in resource "aws_instance" "web":
│   12:   instance_typo = "t2.micro"
│
│ An argument named "instance_typo" is not expected here.
╵

Los componentes son:

  • Tipo de error: Error: Unsupported argument — el nombre de la categoría del error.
  • Localización: on main.tf line 12 — el archivo y la línea donde Terraform detectó el problema.
  • Contexto: el bloque de código afectado con la línea resaltada.
  • Descripción: el mensaje explicativo del problema.

Errores múltiples

Terraform puede mostrar varios errores a la vez. Léelos de arriba hacia abajo, ya que a veces el primer error causa los siguientes en cascada. Corregir el primero puede hacer desaparecer los demás.

Errores de providers (errores de API)

Los errores provenientes de AWS, Azure o GCP tienen un aspecto diferente porque son respuestas HTTP traducidas:

╷
│ Error: creating EC2 Instance: InvalidParameterValue:
│        The requested configuration is currently not supported.
│        Please check the documentation for supported configurations.
│        status code: 400, request id: abc123-def456
│
│   with aws_instance.web,
│   on main.tf line 5, in resource "aws_instance" "web":
│    5: resource "aws_instance" "web" {
╵

Aquí el mensaje viene de la API de AWS. El campo status code y request id son útiles para buscar en la documentación o reportar a soporte.


  1. Categorías de Errores

2.1 Errores de Sintaxis HCL

Son los más fáciles de identificar y corregir. Ocurren cuando el código no sigue las reglas gramaticales del lenguaje HCL.

Causas comunes:

  • Falta de comillas en cadenas de texto
  • Llaves {} o corchetes [] sin cerrar
  • Comas al final de bloques (como si fuera JSON pero HCL no lo requiere)
  • Uso incorrecto de interpolación ${}

Ejemplo de error:

# Código incorrecto
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0
  instance_type = "t2.micro"
}
╷
│ Error: Invalid quoted template delimiter
│
│   on main.tf line 3, in resource "aws_instance" "web":
│    3:   ami           = "ami-0c55b159cbfafe1f0
│
│ The output value must end with a closing quote mark (").
╵

Solución: Cerrar la cadena con ".

2.2 Errores de Configuración del Provider

Ocurren cuando el bloque provider tiene parámetros incorrectos, faltantes o incompatibles con la versión usada.

Causas comunes:

  • Región no especificada cuando es obligatoria
  • Credenciales incorrectas o ausentes
  • Versión del provider incompatible con la configuración usada
  • Alias de provider incorrecto

2.3 Errores de la API del Proveedor (AWS, Azure, GCP)

Son errores que devuelve el servicio en la nube cuando Terraform intenta crear, modificar o eliminar un recurso.

Causas comunes:

  • Permisos insuficientes (IAM/RBAC)
  • Cuotas o límites de servicio alcanzados
  • El recurso ya existe con el mismo nombre
  • Parámetros con valores inválidos para esa región o servicio
  • Dependencias no satisfechas en el proveedor de nube

2.4 Errores de Estado

Relacionados con el archivo de estado (terraform.tfstate), que es la fuente de verdad de Terraform sobre la infraestructura gestionada.

Causas comunes:

  • Estado bloqueado por otra operación
  • Estado corrupto o modificado manualmente
  • Estado desincronizado con la infraestructura real
  • Backend de estado inaccesible

2.5 Errores de Dependencias Circulares

Ocurren cuando dos o más recursos se referencian mutuamente, creando un ciclo que Terraform no puede resolver.

Ejemplo:

# Configuración con ciclo (INCORRECTO)
resource "aws_security_group" "sg_a" {
  name = "sg-a"
  # sg_a depende de sg_b
  ingress {
    security_groups = [aws_security_group.sg_b.id]
  }
}

resource "aws_security_group" "sg_b" {
  name = "sg-b"
  # sg_b depende de sg_a -> CICLO
  ingress {
    security_groups = [aws_security_group.sg_a.id]
  }
}

  1. Tabla de Errores Más Comunes

Error Causa Solución
Error: No configuration files Se ejecuta terraform en un directorio sin archivos .tf Navegar al directorio correcto o crear archivos de configuración
Error acquiring the state lock Otro proceso (o un proceso fallido previo) tiene el lock del estado Verificar que no hay otra operación en curso; usar terraform force-unlock con precaución
Error: Unsupported argument Se usa un argumento que no existe en esa versión del recurso/provider Revisar la documentación del provider y corregir el nombre del argumento
Error: Reference to undeclared resource Se referencia un recurso (tipo.nombre) que no está definido en la configuración Definir el recurso que falta o corregir el nombre de la referencia
Error: Cycle (dependency cycle) Dos o más recursos se referencian mutuamente Reestructurar las dependencias usando depends_on de forma unidireccional o refactorizar
Error: Provider configuration not present Se hace referencia a un provider con alias que no está configurado Añadir el bloque provider correspondiente con el alias correcto
Error: Invalid count argument El valor de count no es un número entero conocido en el plan Usar count con valores deterministas; evitar referencias a valores desconocidos en tiempo de plan
ResourceAlreadyExists El recurso ya existe en el proveedor de nube con ese nombre/ID Importar el recurso con terraform import o usar un nombre diferente
AccessDenied / AuthorizationError Las credenciales usadas no tienen permisos suficientes Revisar y ampliar los permisos IAM/RBAC del usuario o rol de servicio

  1. Diagnóstico de Errores Paso a Paso

Cuando te encuentres con un error, sigue este proceso sistemático:

Paso 1: Lee el error completo No te quedes con la primera línea. Lee todo el mensaje, incluyendo el archivo, la línea y la descripción completa.

Paso 2: Identifica la categoría ¿Es un error de sintaxis, de provider, de estado, de API? Esto te orienta hacia la solución.

Paso 3: Localiza el archivo y la línea Ve directamente al código que Terraform señala. La mayoría de los errores de sintaxis y configuración tienen una ubicación exacta.

Paso 4: Busca el mensaje exacto Copia el tipo de error (p. ej., Error: Unsupported argument) y búscalo en:

  • La documentación oficial: registry.terraform.io
  • GitHub Issues del provider
  • Stack Overflow con la etiqueta terraform

Paso 5: Aplica y verifica Aplica la corrección y ejecuta terraform validate antes de volver a ejecutar terraform plan o terraform apply.


  1. Interpretar Mensajes de Error Complejos con Ejemplos

Ejemplo 1: Error de referencia con módulos

╷
│ Error: Reference to undeclared resource
│
│   on main.tf line 22, in resource "aws_lb_target_group_attachment" "web":
│   22:   target_id = aws_instance.app_server.id
│
│ A managed resource "aws_instance" "app_server" has not been declared
│ in the root module.
╵

Interpretación: En main.tf, línea 22, se referencia aws_instance.app_server pero ese recurso no existe en el módulo raíz. Puede que el recurso esté en un módulo hijo y habría que referenciarlo como module.nombre_modulo.recurso.

Ejemplo 2: Error de versión de provider

╷
│ Error: Unsupported argument
│
│   on main.tf line 8, in resource "aws_s3_bucket" "example":
│    8:   acl = "private"
│
│ An argument named "acl" is not expected here. Did you mean to use
│ an "aws_s3_bucket_acl" resource? See the documentation for more
│ information.
╵

Interpretación: En versiones recientes del provider AWS (v4+), el argumento acl fue separado del recurso aws_s3_bucket en su propio recurso aws_s3_bucket_acl. Este error indica que es necesario refactorizar la configuración.

Solución:

# Antes (provider AWS v3)
resource "aws_s3_bucket" "example" {
  bucket = "mi-bucket-ejemplo"
  acl    = "private"
}

# Después (provider AWS v4+)
resource "aws_s3_bucket" "example" {
  bucket = "mi-bucket-ejemplo"
  # Ya no se pone acl aquí
}

# Recurso separado para el ACL
resource "aws_s3_bucket_acl" "example" {
  bucket = aws_s3_bucket.example.id
  acl    = "private"
}

  1. Reproducir y Depurar Errores en Local

Usar un entorno mínimo de reproducción

Cuando encuentres un error difícil, crea una configuración mínima que lo reproduzca:

# reproduccion_error.tf - Configuración mínima para reproducir el problema
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# Solo el recurso que falla, con la configuración mínima
resource "aws_instance" "test" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  # El parámetro que causa el problema
  instance_typo = "value"  # <-- error intencional para reproducir
}

Validar sin conectarse al cloud

# Valida la sintaxis y las referencias sin contactar la API del provider
terraform validate

Ver el plan detallado

# Genera el plan y lo guarda en formato JSON para análisis
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan | jq '.'

  1. Ejercicio Práctico: Identificar y Corregir Errores

A continuación tienes una configuración de Terraform con múltiples errores. Tu tarea es identificarlos todos y corregirlos.

Configuración con errores:

# main.tf - ESTA CONFIGURACIÓN TIENE ERRORES, NO USAR TAL CUAL

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  # Error 1: Falta la región obligatoria
}

# Error 2: Nombre de argumento incorrecto
resource "aws_vpc" "main" {
  cidr_blocks = "10.0.0.0/16"  # El argumento correcto es cidr_block (sin 's')

  tags = {
    Name = "main-vpc"
  }
}

resource "aws_subnet" "public" {
  vpc_id     = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"

  # Error 3: Referencia a un recurso que no existe
  availability_zone = aws_availability_zones.available.names[0]

  tags = {
    Name = "public-subnet"
  }
}

# Error 4: Ciclo de dependencia
resource "aws_security_group" "web" {
  name   = "web-sg"
  vpc_id = aws_vpc.main.id

  ingress {
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    # web depende de db para el ingreso
    security_groups = [aws_security_group.db.id]
  }
}

resource "aws_security_group" "db" {
  name   = "db-sg"
  vpc_id = aws_vpc.main.id

  ingress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    # db depende de web -> CICLO
    security_groups = [aws_security_group.web.id]
  }
}

resource "aws_instance" "web" {
  # Error 5: AMI sin especificar (cadena vacía)
  ami           = ""
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.public.id

  tags = {
    Name = "web-server
  }
  # Error 6: La llave de tags no está cerrada correctamente (falta ")
}

Tu tarea:

  1. Lista todos los errores que encuentres (hay al menos 6).
  2. Para cada error, explica por qué es un error.
  3. Escribe la versión corregida completa.

  1. Solución Detallada

Lista de errores encontrados:

Error 1 — Falta la región en el provider (línea 13)

# INCORRECTO
provider "aws" {
  # Sin región
}

# CORRECTO
provider "aws" {
  region = "us-east-1"
}

El provider de AWS requiere una región. Sin ella, Terraform no sabe contra qué endpoint de AWS hacer las llamadas.

Error 2 — Argumento incorrecto en aws_vpc (línea 18)

# INCORRECTO
resource "aws_vpc" "main" {
  cidr_blocks = "10.0.0.0/16"  # Plural no existe
}

# CORRECTO
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"  # Singular
}

El argumento se llama cidr_block (singular). Usar cidr_blocks genera Error: Unsupported argument.

Error 3 — Referencia a recurso inexistente (línea 28)

# INCORRECTO - aws_availability_zones no es un recurso gestionado
availability_zone = aws_availability_zones.available.names[0]

# CORRECTO - Es un data source, se accede con data.
# Primero hay que declararlo:
data "aws_availability_zones" "available" {
  state = "available"
}
# Luego referenciarlo:
availability_zone = data.aws_availability_zones.available.names[0]

Los data sources se acceden con el prefijo data., no directamente como recursos gestionados.

Error 4 — Ciclo de dependencia entre security groups (líneas 36-56)

# INCORRECTO - ciclo: web -> db -> web
# CORRECTO - romper el ciclo usando reglas separadas

resource "aws_security_group" "web" {
  name   = "web-sg"
  vpc_id = aws_vpc.main.id
  # Sin referencia a db aquí
}

resource "aws_security_group" "db" {
  name   = "db-sg"
  vpc_id = aws_vpc.main.id
  # Solo db depende de web, no al revés
  ingress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [aws_security_group.web.id]
  }
}

# La regla de web se añade por separado, después de crear ambos grupos
resource "aws_security_group_rule" "web_from_db" {
  type                     = "ingress"
  from_port                = 80
  to_port                  = 80
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.db.id
  security_group_id        = aws_security_group.web.id
}

Error 5 — AMI vacía (línea 60)

# INCORRECTO
ami = ""

# CORRECTO - usar una AMI real o un data source
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

resource "aws_instance" "web" {
  ami = data.aws_ami.amazon_linux.id
  # ...
}

Error 6 — Cadena sin cerrar en tags (línea 64)

# INCORRECTO
tags = {
  Name = "web-server    # Falta la comilla de cierre
}

# CORRECTO
tags = {
  Name = "web-server"
}

Configuración completamente corregida:

# main.tf - VERSIÓN CORREGIDA

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# Corrección 1: Región añadida
provider "aws" {
  region = "us-east-1"
}

# Data sources necesarios
data "aws_availability_zones" "available" {
  state = "available"
}

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

# Corrección 2: cidr_block (singular)
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "main-vpc"
  }
}

# Corrección 3: data.aws_availability_zones con prefijo data.
resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = data.aws_availability_zones.available.names[0]

  tags = {
    Name = "public-subnet"
  }
}

# Corrección 4: Security groups sin ciclo
resource "aws_security_group" "web" {
  name   = "web-sg"
  vpc_id = aws_vpc.main.id
}

resource "aws_security_group" "db" {
  name   = "db-sg"
  vpc_id = aws_vpc.main.id

  ingress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [aws_security_group.web.id]
  }
}

resource "aws_security_group_rule" "web_ingress_http" {
  type                     = "ingress"
  from_port                = 80
  to_port                  = 80
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.db.id
  security_group_id        = aws_security_group.web.id
}

# Correcciones 5 y 6: AMI con data source y cadena correctamente cerrada
resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.public.id

  vpc_security_group_ids = [aws_security_group.web.id]

  tags = {
    Name = "web-server"
  }
}

Errores Comunes Adicionales y Consejos

Consejo 1: Siempre ejecuta terraform validate antes de terraform plan

# Valida la sintaxis sin necesitar credenciales de nube
terraform validate

# Salida esperada cuando todo está bien:
# Success! The configuration is valid.

Consejo 2: Usa terraform fmt para evitar errores de formato

# Formatea automáticamente todos los archivos .tf del directorio
terraform fmt

# Para ver qué cambiaría sin aplicarlo
terraform fmt -check -diff

Consejo 3: Errores de versión de provider frecuentes

Si ves errores inesperados con argumentos que "deberían funcionar", verifica la versión del provider:

# Siempre especifica versiones para evitar sorpresas
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"  # Permite 5.x pero no 6.x
    }
  }
}

Resumen

En esta lección has aprendido:

  • Cómo estructuran los mensajes de error de Terraform: tipo de error, localización exacta en el código y descripción.
  • Las cinco grandes categorías de errores: sintaxis HCL, configuración del provider, errores de API de la nube, errores de estado y dependencias circulares.
  • Los errores más comunes y sus soluciones directas, resumidos en una tabla de referencia rápida.
  • Un proceso sistemático de diagnóstico de 5 pasos aplicable a cualquier error.
  • Cómo interpretar errores complejos como los cambios de API entre versiones de providers.
  • Cómo resolver una configuración con múltiples errores de forma metódica.

En la siguiente lección profundizaremos en las herramientas de depuración de Terraform, especialmente la variable TF_LOG y el comando terraform console, que te permitirán inspeccionar el comportamiento interno de Terraform cuando los mensajes de error no son suficientes.

© Copyright 2026. Todos los derechos reservados