TL;DR — Resumen Rápido
Configura backends remotos de Terraform en S3, Azure Blob y GCS. Aprende bloqueo de estado, migración, workspaces, seguridad y referencias entre proyectos.
Terraform mantiene un archivo de estado — un registro JSON de cada recurso que gestiona — para mapear tu configuración HCL a la infraestructura real. Cuando trabajas solo, un archivo de estado local es suficiente. En el momento en que un segundo ingeniero o un pipeline de CI ejecuta terraform apply contra la misma infraestructura, el estado local se convierte en un riesgo compartido sin bloqueo, sin versionado y sin control de acceso. Esta guía cubre todos los aspectos de los backends remotos de Terraform: elegir el proveedor correcto, configurar el bloqueo de estado, migrar desde estado local, manipular el estado con comandos CLI, proteger datos sensibles y configurar referencias entre proyectos.
Prerrequisitos
- Terraform CLI v1.6 o posterior instalado localmente
- Una cuenta AWS con permisos para crear buckets S3 y tablas DynamoDB (para ejemplos AWS)
- Azure CLI autenticado contra una suscripción (para ejemplos con Azure Blob)
- Familiaridad básica con bloques de recursos Terraform y el flujo
terraform apply - Git para versionar tus configuraciones Terraform (nunca confirmes
terraform.tfstate)
Por Qué el Estado Local Es Peligroso para Equipos
El backend local de Terraform escribe terraform.tfstate en el directorio actual. Esto crea tres problemas críticos en cuanto más de una persona o proceso toca la misma infraestructura:
Sin bloqueo. Dos ingenieros ejecutan terraform apply al mismo tiempo. El segundo apply lee estado desactualizado, calcula un plan contra datos obsoletos y luego escribe su estado encima del estado del primer apply. Los recursos creados por el primer apply quedan huérfanos — existen en la nube pero son invisibles para Terraform.
Sin compartición. Cada ingeniero tiene su propia copia del estado en su portátil. Los cambios hechos por una persona son invisibles para los demás hasta que intercambian manualmente los archivos de estado.
Eliminación accidental. Un rm -rf en el directorio incorrecto, un fallo de disco o un portátil perdido borra el archivo de estado por completo. Sin él, Terraform no sabe qué gestiona e intentará recrear todos los recursos.
Configurar un Backend S3 (AWS)
El backend S3 es la opción más común para equipos en AWS. Combina un bucket S3 (para almacenamiento duradero y versionado) con una tabla DynamoDB (para bloqueo distribuido).
Crear la infraestructura del backend
# Bucket S3 para almacenamiento de estado
aws s3api create-bucket \
--bucket acme-terraform-state \
--region us-east-1
# Habilitar versionado para recuperar versiones anteriores
aws s3api put-bucket-versioning \
--bucket acme-terraform-state \
--versioning-configuration Status=Enabled
# Bloquear todo acceso público
aws s3api put-public-access-block \
--bucket acme-terraform-state \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,\
BlockPublicPolicy=true,RestrictPublicBuckets=true
# Tabla DynamoDB para bloqueo de estado
aws dynamodb create-table \
--table-name terraform-state-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
Configurar el bloque backend
terraform {
backend "s3" {
bucket = "acme-terraform-state"
key = "networking/prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-state-locks"
encrypt = true
}
}
La key es la ruta del objeto S3. Usa una convención de nomenclatura consistente — <proyecto>/<entorno>/terraform.tfstate — para que todos los equipos puedan encontrar los archivos de estado de los demás.
Configurar un Backend Azure Blob Storage
Para equipos centrados en Azure, usa una cuenta de almacenamiento Azure. Azure Blob Storage proporciona bloqueo nativo mediante lease de blob — no se necesita ningún recurso de bloqueo separado.
az group create --name rg-terraform-state --location eastus
az storage account create \
--name acmetfstate2026 \
--resource-group rg-terraform-state \
--sku Standard_LRS \
--encryption-services blob \
--min-tls-version TLS1_2
az storage container create \
--name tfstate \
--account-name acmetfstate2026
terraform {
backend "azurerm" {
resource_group_name = "rg-terraform-state"
storage_account_name = "acmetfstate2026"
container_name = "tfstate"
key = "networking/prod/terraform.tfstate"
}
}
Bloqueo de Estado y Consistencia
Cuando Terraform inicia cualquier operación que modifica estado (plan, apply, destroy), intenta adquirir un bloqueo. Con el backend S3, DynamoDB proporciona el bloqueo. Cualquier proceso concurrente que intente adquirir el mismo bloqueo recibe un error de bloqueo con información sobre quién lo tiene.
Para liberar un bloqueo dejado por un proceso caído:
terraform force-unlock a3f2b1
Nunca uses force-unlock mientras otra operación esté activa. Solo usa este comando cuando estés seguro de que el proceso que tenía el bloqueo ha terminado.
Migrar desde Estado Local a Remoto
Migración inicial
Después de agregar un bloque backend a tu configuración por primera vez, ejecuta:
terraform init -migrate-state
Terraform detecta el nuevo backend, te solicita que copies el estado local existente y lo escribe en la ubicación remota. Se guarda una copia de seguridad como terraform.tfstate.backup.
Cambiar entre backends remotos
Para mover estado de un backend remoto a otro (por ejemplo, de S3 a Terraform Cloud):
- Actualiza el bloque backend al nuevo destino
- Ejecuta
terraform init -migrate-state - Confirma el prompt de migración
Comandos de Estado de Terraform
# Listar todos los recursos rastreados en el estado
terraform state list
# Mostrar atributos completos de un recurso específico
terraform state show aws_instance.web
# Renombrar un recurso (después de refactorizar HCL)
terraform state mv aws_instance.old_name aws_instance.new_name
# Mover un recurso a otro archivo de estado
terraform state mv \
-state-out=../other-project/terraform.tfstate \
aws_vpc.main aws_vpc.main
# Eliminar un recurso del estado sin destruirlo en la nube
terraform state rm aws_instance.legacy_server
# Descargar estado como JSON
terraform state pull > current-state.json
# Reemplazar estado con un archivo modificado (peligroso)
terraform state push modified-state.json
Seguridad del Archivo de Estado
Los archivos de estado de Terraform contienen cada valor de atributo de cada recurso — incluyendo contraseñas maestras de base de datos, claves TLS privadas y tokens de API. Aunque marques una variable u output como sensitive = true, el valor aún aparece en el JSON del estado en texto plano.
Cifrado en reposo. Habilita cifrado en el backend de almacenamiento:
# S3: enforce SSE-KMS con una clave gestionada por el cliente
aws s3api put-bucket-encryption \
--bucket acme-terraform-state \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:us-east-1:123456789:key/abc-123"
}
}]
}'
IAM con mínimo privilegio. Otorga a los pipelines de CI solo los permisos que necesitan sobre el bucket S3 y la tabla DynamoDB.
Workspaces para Separación de Entornos
Los workspaces de Terraform crean archivos de estado aislados dentro de la misma configuración de backend:
terraform workspace new dev
terraform workspace new staging
terraform workspace new production
terraform workspace list
terraform workspace select production
terraform apply
Referencia el workspace actual en la configuración para variar tamaños o conteos de recursos por entorno:
locals {
env_config = {
dev = { instance_type = "t3.micro", min_size = 1 }
staging = { instance_type = "t3.small", min_size = 2 }
production = { instance_type = "t3.medium", min_size = 3 }
}
}
resource "aws_instance" "web" {
instance_type = local.env_config[terraform.workspace].instance_type
}
Referencias Entre Proyectos con terraform_remote_state
El data source terraform_remote_state permite que un proyecto Terraform lea outputs del archivo de estado de otro proyecto sin duplicar declaraciones de recursos:
# El proyecto de networking expone su ID de VPC
output "vpc_id" {
value = aws_vpc.main.id
}
# El proyecto de aplicación lee el ID de VPC del estado de networking
data "terraform_remote_state" "networking" {
backend = "s3"
config = {
bucket = "acme-terraform-state"
key = "networking/prod/terraform.tfstate"
region = "us-east-1"
}
}
resource "aws_instance" "app" {
subnet_id = data.terraform_remote_state.networking.outputs.vpc_id
}
Configuración Parcial de Backend para CI/CD
Los bloques backend no pueden usar interpolación de variables. Usa configuración parcial con flags -backend-config:
# main.tf — sin valores especificados
terraform {
backend "s3" {}
}
# El pipeline CI pasa los valores en el momento del init
terraform init \
-backend-config="bucket=acme-terraform-state" \
-backend-config="key=networking/prod/terraform.tfstate" \
-backend-config="region=us-east-1" \
-backend-config="dynamodb_table=terraform-state-locks"
Comparativa de Backends
| Característica | S3 + DynamoDB | Azure Blob | GCS | Terraform Cloud | Consul |
|---|---|---|---|---|---|
| Bloqueo de estado | Tabla DynamoDB | Lease blob nativo | Bloqueo de objeto nativo | Integrado | KV nativo |
| Cifrado en reposo | SSE-S3 / SSE-KMS | Claves Azure | Claves Google | AES-256 integrado | TLS manual |
| Versionado | Versionado S3 | Versionado blob | Versionado de objeto | Historial integrado | Snapshots manuales |
| Costo (uso bajo) | ~$1/mes | ~$1/mes | ~$1/mes | Gratis (500 recursos) | Ops self-hosted |
| Control de acceso | Políticas IAM | Azure RBAC | GCP IAM | Teams + SSO | Tokens ACL |
| Complejidad | Media (2 recursos) | Media (3 recursos) | Baja (1 bucket) | Baja (SaaS) | Alta (clúster) |
| Ideal para | Equipos AWS | Equipos Azure | Equipos GCP | Multi-cloud + políticas | On-premises |
Escenario Real Multi-Entorno
Tu equipo gestiona 300+ recursos AWS en dev, staging y producción. Los ingenieros han estado confirmando terraform.tfstate en Git. Un miércoles por la mañana, dos ingenieros aplican cambios simultáneamente a la VPC de producción — uno agrega una subred privada, el otro modifica un grupo de seguridad. El segundo apply lee el estado anterior, computa un plan que ignora la nueva subred y sobreescribe el estado. La subred existe en AWS pero es invisible para Terraform: un recurso fantasma que genera diffs confusos en cada plan futuro.
La arquitectura objetivo usa un backend S3 con bloqueo DynamoDB y claves separadas por entorno:
# Inicialización por entorno con configuración parcial
terraform init \
-backend-config="bucket=acme-terraform-state" \
-backend-config="key=acme/production/terraform.tfstate" \
-backend-config="region=us-east-1" \
-backend-config="dynamodb_table=terraform-state-locks"
Cada entorno tiene su propio archivo de estado bajo una clave distinta. El bloqueo DynamoDB impide applies concurrentes. El versionado S3 permite revertir estado corrupto.
Errores Comunes y Casos Límite
Los datos sensibles en el estado siempre están en texto plano. Aunque uses sensitive = true en outputs, los valores aparecen en el JSON de estado. Trata el archivo de estado como un almacén de secretos y restringe el acceso en consecuencia.
Los bloques backend no pueden usar variables ni locals. Solo se admiten valores literales y flags -backend-config. Intentar referenciar var.region dentro de un bloque backend falla en tiempo de parseo.
Colisiones de claves de workspace. Si la clave de backend no incluye el nombre del workspace, todos los workspaces comparten un archivo de estado.
Deriva de estado por cambios externos. Recursos modificados a través de la consola AWS o Azure Portal no se reflejan en el estado hasta que ejecutas terraform refresh o Terraform lo hace durante el plan.
Fallos de apply parciales. Si un apply falla a mitad de camino, el estado refleja lo que se creó exitosamente. No elimines ni restaures el estado en esta situación — simplemente vuelve a ejecutar terraform apply.
Solución de Problemas
Errores de adquisición de bloqueo
Error: Error acquiring the state lock
Causa: Otro proceso tiene el bloqueo o un proceso anterior se cayó sin liberarlo.
Solución: Verifica que ningún otro apply o plan esté activo. Si el bloqueo está obsoleto, ejecuta terraform force-unlock <LOCK_ID>.
Acceso denegado en operaciones de estado
Error: Failed to load state: AccessDenied: Access Denied
Causa: La identidad IAM ejecutora carece de s3:GetObject, s3:PutObject o dynamodb:PutItem.
Solución: Audita la política IAM adjunta al rol CI y agrega los permisos faltantes.
Corrupción del archivo de estado
Síntomas: terraform plan muestra que todos los recursos serán recreados aunque existan en la nube.
Solución: Restaura una versión anterior del estado desde el versionado S3 y usa terraform state push para aplicarla.
Resumen
- El estado local es inseguro para equipos: sin bloqueo, sin compartición y una sola eliminación borra el conocimiento de la infraestructura
- S3 + DynamoDB, Azure Blob, GCS y Terraform Cloud proporcionan backends remotos duraderos y con bloqueo
- El bloqueo de estado previene modificaciones concurrentes y corrupción
- Migra de estado local a remoto con
terraform init -migrate-state - Usa
terraform state list,show,mvyrmpara inspeccionar y refactorizar el estado sin ejecutar applies - Trata los archivos de estado como secretos — habilita cifrado en reposo y restringe el acceso IAM
- Usa workspaces o módulos raíz separados con backends independientes para aislar entornos
terraform_remote_statepermite compartir datos entre proyectos; desacopla con SSM o Vault para menor dependencia- La configuración parcial de backend con
-backend-configmantiene los secretos fuera del control de versiones