Introducción
En la lección anterior aprendiste qué es el estado y por qué Terraform lo necesita. En esta lección profundizaremos en los archivos que componen el sistema de estado de Terraform: su estructura interna, cómo manipularlos de forma segura y las operaciones avanzadas que puedes realizar sobre ellos. Entender esto te permitirá gestionar tu infraestructura con mayor confianza y resolver problemas complejos.
Estructura Detallada del Archivo terraform.tfstate
El archivo terraform.tfstate es un documento JSON con una estructura bien definida. Analicemos cada parte con un ejemplo completo y realista:
{
"version": 4,
"terraform_version": "1.5.7",
"serial": 12,
"lineage": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"outputs": {
"id_instancia": {
"value": "i-0a1b2c3d4e5f67890",
"type": "string",
"sensitive": false
},
"clave_secreta": {
"value": "s3cr3t0",
"type": "string",
"sensitive": true
}
},
"resources": [
{
"mode": "managed",
"type": "aws_vpc",
"name": "principal",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
"arn": "arn:aws:ec2:us-east-1:123456789012:vpc/vpc-0abc12345",
"cidr_block": "10.0.0.0/16",
"enable_dns_hostnames": true,
"enable_dns_support": true,
"id": "vpc-0abc12345",
"instance_tenancy": "default",
"tags": {
"Name": "VPC-Principal",
"Entorno": "produccion"
}
},
"sensitive_attributes": [],
"private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ=="
}
]
},
{
"mode": "managed",
"type": "aws_instance",
"name": "servidor_web",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
"ami": "ami-0c55b159cbfafe1f0",
"availability_zone": "us-east-1a",
"id": "i-0a1b2c3d4e5f67890",
"instance_type": "t2.micro",
"private_ip": "10.0.1.25",
"public_ip": "54.123.45.67",
"subnet_id": "subnet-0abc12345",
"vpc_security_group_ids": ["sg-0abc12345"],
"tags": {
"Name": "ServidorWeb"
}
},
"sensitive_attributes": [],
"private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==",
"dependencies": [
"aws_vpc.principal"
]
}
]
},
{
"mode": "data",
"type": "aws_ami",
"name": "ubuntu",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"id": "ami-0c55b159cbfafe1f0",
"name": "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"
}
}
]
}
]
}Análisis campo por campo
Campos raíz:
| Campo | Tipo | Descripción |
|---|---|---|
version |
número | Versión del esquema del archivo de estado. Actualmente es 4. Si Terraform actualiza su formato interno, este número aumenta |
terraform_version |
string | Versión exacta de Terraform que generó el estado. Útil para diagnóstico |
serial |
número | Contador que se incrementa con cada escritura al estado. Ayuda a detectar conflictos en sistemas distribuidos |
lineage |
UUID | Identificador único generado la primera vez que se crea el estado. Permanece constante para toda la vida del estado. Impide mezclar estados de proyectos diferentes |
outputs |
objeto | Mapa de los valores de salida definidos con el bloque output |
resources |
array | Lista de todos los recursos (gestionados y de datos) que Terraform conoce |
Dentro de cada recurso:
| Campo | Descripción |
|---|---|
mode |
managed para recursos creados con resource {}, data para fuentes de datos |
type |
Tipo de recurso, como aws_instance o aws_vpc |
name |
Nombre lógico del recurso en tu código HCL |
provider |
Identificador completo del proveedor, incluyendo el namespace del Registry |
instances |
Array con una entrada por instancia (una si no usas count/for_each) |
attributes |
Todos los atributos del recurso tras ser creado/actualizado |
sensitive_attributes |
Lista de atributos marcados como sensibles (sus valores se ocultan en la salida pero SÍ se guardan en el estado) |
private |
Datos internos en Base64 que el proveedor usa para su funcionamiento interno |
dependencies |
Lista de otros recursos de los que depende este recurso |
Importante sobre sensitive_attributes: Los valores sensibles (como contraseñas o tokens) sí se guardan en texto plano en el archivo de estado. Por eso es fundamental proteger el acceso al archivo de estado, especialmente cuando se usa un backend remoto.
El Archivo terraform.tfstate.backup
Cada vez que Terraform modifica el estado, guarda automáticamente una copia del estado anterior con el nombre terraform.tfstate.backup.
mi-proyecto/ ├── main.tf ├── terraform.tfstate ← Estado actual └── terraform.tfstate.backup ← Estado anterior (copia de seguridad automática)
¿Cuándo es útil el backup?
Si una operación de terraform apply falla a mitad de camino y el estado queda en un estado inconsistente, puedes restaurar el backup manualmente:
Sin embargo, esto solo funciona con estado local. Con backends remotos, los sistemas de versionado del backend (como S3 con versioning activado) proporcionan una funcionalidad equivalente y más robusta.
Limitaciones del backup:
- Solo guarda el estado inmediatamente anterior. Si haces dos
applyconsecutivos, el backup del primer apply se sobreescribe. - No sustituye a una estrategia de respaldo real (veremos esto más adelante en esta lección).
El Archivo .terraform.lock.hcl
Cuando ejecutas terraform init, Terraform crea (o actualiza) un archivo llamado .terraform.lock.hcl. Este archivo es diferente del estado: no registra recursos, sino las versiones exactas de los proveedores que se usaron.
# .terraform.lock.hcl
# Este archivo fue generado automáticamente por "terraform init".
# Está destinado a ser incluido en el control de versiones.
provider "registry.terraform.io/hashicorp/aws" {
version = "5.23.0"
constraints = "~> 5.0"
hashes = [
"h1:AbCdEfGhIjKlMnOpQrStUvWxYz0123456789abcdef=",
"zh:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
]
}
provider "registry.terraform.io/hashicorp/local" {
version = "2.4.0"
constraints = "~> 2.0"
hashes = [
"h1:XyZ123abcDEF456ghiJKL789mnoPQR012stuVWX345=",
]
}¿Para qué sirve el lock file?
- Reproducibilidad: Garantiza que todos los miembros del equipo usen exactamente la misma versión del proveedor, evitando el problema de "funciona en mi máquina".
- Seguridad: Los hashes criptográficos verifican que los binarios descargados no han sido alterados.
- Actualizaciones controladas: Para actualizar un proveedor, debes ejecutar
terraform init -upgradeexplícitamente.
Gestión del lock file
# Actualizar todas las versiones de proveedores (respetando las constraints)
terraform init -upgrade
# Añadir hashes para múltiples plataformas (útil en CI/CD)
terraform providers lock -platform=linux_amd64 -platform=darwin_amd64El archivo .terraform.lock.hcl DEBE incluirse en el control de versiones (git). Es una buena práctica incluirlo para garantizar consistencia entre entornos.
Manipulación del Estado: terraform state mv
El comando terraform state mv mueve recursos dentro del estado, o entre estados. Es uno de los comandos más potentes y también más peligrosos si se usa incorrectamente.
Sintaxis
Casos de uso comunes
Caso 1: Renombrar un recurso en el código
Imagina que tienes este recurso y quieres renombrarlo:
# Antes (en tu código HCL)
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}Si simplemente cambias el nombre en el código a servidor_web y ejecutas terraform plan, Terraform pensará que debes destruir web y crear servidor_web (una instancia completamente nueva). Para evitar esto:
# Primero, mueve el recurso en el estado
terraform state mv aws_instance.web aws_instance.servidor_web
# Luego, actualiza tu código HCL
# resource "aws_instance" "web" { ... } ← elimina esto
# resource "aws_instance" "servidor_web" { ... } ← añade esto
# Ahora terraform plan no mostrará cambios destructivos
terraform planCaso 2: Mover un recurso a un módulo
Cuando refactorizas código y mueves recursos a módulos:
# Mover un recurso al módulo "red"
terraform state mv aws_vpc.principal module.red.aws_vpc.principalCaso 3: Mover un recurso entre workspaces o proyectos
# Extraer el estado de un recurso a un archivo temporal
terraform state mv -state-out=../otro-proyecto/terraform.tfstate \
aws_instance.servidor_web \
aws_instance.servidor_webPrecauciones con terraform state mv
- Siempre haz un backup antes de usar
state mv:terraform state pull > backup_estado_$(date +%Y%m%d_%H%M%S).tfstate - Verifica el resultado con
terraform state listyterraform plandespués de la operación. - En entornos con estado remoto,
state mvopera directamente sobre el backend; los cambios son inmediatos.
Manipulación del Estado: terraform state rm
El comando terraform state rm elimina un recurso del estado de Terraform sin destruir el recurso real en la nube. Esto le dice a Terraform: "deja de gestionar este recurso".
Sintaxis
Casos de uso
Caso 1: Dejar de gestionar un recurso
Tienes una base de datos que quieres excluir de la gestión de Terraform (quizás porque la vas a gestionar manualmente o con otra herramienta):
# Elimina del estado sin destruir el recurso en AWS
terraform state rm aws_db_instance.base_datos_legadoDespués de esto:
- El recurso sigue existiendo en AWS.
- Si ejecutas
terraform plan, Terraform ya no lo mencionará. - Si tienes el bloque
resourceen el código, Terraform intentará crear un nuevo recurso (ten cuidado: también elimina el bloqueresourcedel código si no quieres que se cree uno nuevo).
Caso 2: Eliminar múltiples recursos a la vez
# Eliminar todos los recursos de un módulo del estado
terraform state rm 'module.red'
# Eliminar un recurso con for_each (la dirección incluye la clave)
terraform state rm 'aws_instance.servidores["web-01"]'Caso 3: Corregir un estado corrupto
Si un recurso en el estado apunta a un recurso que ya no existe en la nube y los intentos de refresh fallan:
Importar Infraestructura Existente con terraform import
Frecuentemente te encontrarás con infraestructura que fue creada manualmente (antes de usar Terraform) y necesitas incorporarla a la gestión de Terraform. Para esto existe terraform import.
¿Qué hace terraform import?
terraform import hace lo opuesto a terraform destroy: en lugar de eliminar un recurso real y borrarlo del estado, toma un recurso real existente y lo añade al estado sin modificarlo.
Flujo de trabajo para importar infraestructura
Paso 1: Escribe el bloque resource en tu código HCL
Terraform necesita saber en qué bloque "encajar" el recurso importado:
# main.tf
resource "aws_instance" "servidor_existente" {
# Terraform rellenará los atributos desde la nube
# pero necesita que el bloque exista
ami = "ami-0c55b159cbfafe1f0" # Deberás descubrirlo
instance_type = "t2.micro" # Deberás descubrirlo
}Paso 2: Ejecuta terraform import
# Sintaxis: terraform import DIRECCIÓN_RECURSO ID_REAL
terraform import aws_instance.servidor_existente i-0a1b2c3d4e5f67890Salida esperada:
aws_instance.servidor_existente: Importing from ID "i-0a1b2c3d4e5f67890"... aws_instance.servidor_existente: Import prepared! Prepared aws_instance for import aws_instance.servidor_existente: Refreshing state... [id=i-0a1b2c3d4e5f67890] Import successful! The resources that were imported are shown above. These resources are now in your Terraform state and will henceforth be managed by Terraform.
Paso 3: Ejecuta terraform plan para verificar
Lo ideal es que el plan no muestre ningún cambio. Si muestra cambios, significa que tu bloque resource no refleja exactamente el estado actual del recurso. Deberás ajustar el código HCL hasta que plan diga "No changes".
Paso 4: Verificar el recurso importado en el estado
Importación en bloque con for_each (Terraform >= 1.5)
A partir de Terraform 1.5, existe un bloque nativo import que permite importar múltiples recursos de forma declarativa:
# imports.tf
import {
to = aws_instance.servidor_existente
id = "i-0a1b2c3d4e5f67890"
}
import {
to = aws_security_group.sg_web
id = "sg-0abc12345"
}# Terraform generará automáticamente el código HCL
terraform plan -generate-config-out=recursos_importados.tfEsto genera un archivo recursos_importados.tf con el código HCL completo basado en los atributos reales de los recursos. Es mucho más eficiente que escribir el código manualmente.
Estrategias de Respaldo del Estado
El estado de Terraform es crítico. Perderlo significa perder el control sobre tu infraestructura. Aquí hay estrategias para protegerlo:
Estrategia 1: Versionado en S3
Cuando usas S3 como backend, activa el versionado del bucket:
# El bucket S3 con versionado activado
resource "aws_s3_bucket" "terraform_state" {
bucket = "mi-empresa-terraform-state"
}
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
# También activa el cifrado en reposo
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
}
}
}Estrategia 2: Respaldo manual antes de operaciones críticas
# Antes de cualquier operación arriesgada, exporta el estado actual
terraform state pull > backups/estado_$(date +%Y%m%d_%H%M%S).tfstate
# Para restaurar si algo sale mal
terraform state push backups/estado_20240115_103000.tfstateEstrategia 3: Terraform Cloud
Terraform Cloud mantiene un historial completo de todos los estados y permite hacer rollback a cualquier versión anterior desde la interfaz web.
Qué NO Hacer con el Estado
Esta sección es tan importante como todas las anteriores. Cometer estos errores puede resultar en la pérdida del control sobre tu infraestructura:
❌ NO edites el archivo terraform.tfstate manualmente
El archivo JSON tiene una estructura muy específica y cualquier error de formato o valor incorrecto puede corromperlo completamente. Usa siempre los comandos oficiales de Terraform.
❌ NO elimines el archivo terraform.tfstate
Si eliminas el estado, Terraform "olvidará" todos los recursos que gestionaba. La próxima vez que ejecutes apply, intentará crearlos de nuevo, lo que puede resultar en duplicados o errores si los recursos ya existen.
❌ NO guardes el estado en el repositorio de git
El estado puede contener valores sensibles (contraseñas, tokens, claves privadas) en texto plano. Nunca lo incluyas en git. Usa siempre un backend remoto con cifrado para entornos de equipo.
❌ NO compartas el mismo estado entre proyectos no relacionados
Cada proyecto de Terraform debe tener su propio archivo de estado. Mezclar recursos de proyectos diferentes en el mismo estado crea dependencias innecesarias y hace que las operaciones de plan y apply sean más lentas y peligrosas.
Ejercicio Práctico: Reorganizar Recursos en el Estado
Objetivo
Practicar el uso de terraform state mv para simular una refactorización donde movemos un recurso a un módulo.
Configuración inicial
# main.tf (configuración inicial sin módulos)
terraform {
required_providers {
local = {
source = "hashicorp/local"
version = "~> 2.0"
}
}
}
resource "local_file" "config_app" {
content = "app_name=MiAplicacion\nversion=1.0"
filename = "${path.module}/config_app.txt"
}
resource "local_file" "config_bd" {
content = "host=localhost\nport=5432\ndb=miapp"
filename = "${path.module}/config_bd.txt"
}Paso 1: Aplica la configuración inicial
Salida esperada:
Paso 2: Crear la estructura de módulo
Crea la carpeta y archivo del módulo:
# modulos/servidor/main.tf
variable "nombre_app" {
type = string
}
resource "local_file" "config_app" {
content = "app_name=${var.nombre_app}\nversion=1.0"
filename = "${path.module}/../../config_app.txt"
}Paso 3: Actualiza main.tf para usar el módulo
# main.tf actualizado
terraform {
required_providers {
local = {
source = "hashicorp/local"
version = "~> 2.0"
}
}
}
module "servidor" {
source = "./modulos/servidor"
nombre_app = "MiAplicacion"
}
resource "local_file" "config_bd" {
content = "host=localhost\nport=5432\ndb=miapp"
filename = "${path.module}/config_bd.txt"
}Paso 4: Sin mover el estado, observa el plan destructivo
Terraform querrá DESTRUIR local_file.config_app y CREAR module.servidor.local_file.config_app. Esto es lo que queremos evitar.
Paso 5: Mueve el recurso en el estado
# Haz un backup primero
terraform state pull > backup_antes_mv.tfstate
# Mueve el recurso
terraform state mv local_file.config_app module.servidor.local_file.config_appSalida esperada:
Move "local_file.config_app" to "module.servidor.local_file.config_app" Successfully moved 1 object(s).
Paso 6: Verifica que el plan ya no destruye nada
Salida esperada:
Paso 7: Verifica el nuevo estado
Salida esperada:
La refactorización se completó sin destruir ningún recurso real.
Errores Comunes al Manipular el Estado
Error 1: Dirección de recurso incorrecta en state mv
# Error: la dirección no existe en el estado
terraform state mv aws_instance.web_servers aws_instance.servidores_webError: Invalid target address Cannot move to aws_instance.servidores_web: there is no resource with that type and name at the given path.
Solución: Usa terraform state list para obtener las direcciones exactas antes de ejecutar state mv.
Error 2: Intentar importar sin el bloque resource
Error: resource address "aws_instance.nuevo" does not exist in the configuration. Before importing this resource, please create its configuration in the root module.
Solución: Siempre escribe el bloque resource en tu código HCL antes de ejecutar terraform import.
Error 3: El lock file genera conflictos entre plataformas
Error: Failed to install provider The local package for registry.terraform.io/hashicorp/aws 5.23.0 doesn't match any of the checksums previously recorded in the dependency lock file.
Solución: Ejecuta terraform providers lock con las plataformas necesarias:
Error 4: state rm accidental
Si eliminaste un recurso del estado por error y el recurso aún existe en la nube:
# Recupera el recurso importándolo de nuevo
terraform import aws_instance.servidor_eliminado_por_error i-0a1b2c3d4e5f67890Resumen
En esta lección has profundizado en los archivos que componen el sistema de estado de Terraform:
- El archivo
terraform.tfstatetiene una estructura JSON con campos críticos:version,serial,lineage,outputsyresources. Cada recurso registra sumode,type,name,attributesydependencies. - El archivo
terraform.tfstate.backupguarda el estado inmediatamente anterior y puede usarse para recuperaciones de emergencia. - El archivo
.terraform.lock.hclregistra las versiones exactas de los proveedores y debe incluirse en git para garantizar reproducibilidad. terraform state mvpermite mover y renombrar recursos en el estado, fundamental para refactorizaciones sin tiempo de inactividad.terraform state rmelimina recursos del estado sin destruirlos en la nube.terraform importincorpora infraestructura existente a la gestión de Terraform.- Nunca debes editar el estado manualmente ni incluirlo en git.
En la siguiente lección (03-03-remote-state.md) aprenderás a configurar backends remotos (S3, Azure Blob, Terraform Cloud) para que tu estado sea accesible por todo el equipo, esté protegido y soporte bloqueos para evitar conflictos de escritura concurrente.
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
