El estado local de Terraform puede perderse, sobrescribirse entre operadores o exponer información sensible si permanece en una laptop o se incorpora por error al repositorio.
Un backend remoto centraliza el state, permite aplicar controles de acceso y, cuando ofrece locking, evita que dos procesos modifiquen simultáneamente la misma infraestructura.
Esta guía utiliza Amazon S3 como ejemplo e incluye bloqueo mediante archivo .tflock, cifrado, versionado y migración segura desde un estado local.
Cuándo conviene usar un backend remoto
Resulta especialmente útil en:
- Equipos que comparten infraestructura como código.
- Infraestructura cloud administrada desde CI/CD.
- Ambientes con cambios frecuentes.
- Proyectos ejecutados desde distintos equipos.
- Sistemas que requieren recuperación y auditoría del estado.
Terraform recomienda el estado remoto cuando varias personas trabajan sobre los mismos recursos. Los backends compatibles también pueden bloquear el estado durante operaciones que podrían modificarlo. Estado remoto en Terraform.
Qué contiene el state
Terraform utiliza el state para relacionar cada recurso declarado con su objeto real, además de almacenar dependencias, identificadores y atributos devueltos por los proveedores.
Ese archivo puede contener:
- Direcciones e identificadores internos.
- Contraseñas iniciales.
- Tokens o claves generadas por recursos.
- Cadenas de conexión.
- Atributos marcados como sensibles.
Marcar una variable como sensitive oculta su valor en algunas salidas, pero no impide necesariamente que se guarde en el state o en un plan. Por eso ambos deben tratarse como información sensible. Gestión de datos sensibles.
Arquitectura recomendada
El flujo conecta:
- Código Terraform versionado.
terraform init.- Backend S3 con cifrado y versionado.
- Archivo
.tflockpara impedir concurrencia. - Plan guardado y revisado.
- Apply controlado.
- Actualización del state remoto.
El locking evita escrituras simultáneas. El versionado cumple otra función: permite recuperar una versión anterior ante una eliminación o actualización accidental.
Requisitos previos
Necesitas:
- Terraform instalado.
- Una versión compatible con
use_lockfile. - Un bucket S3 creado previamente.
- Versionado habilitado en ese bucket.
- Cifrado en reposo.
- Bloqueo de acceso público.
- Una identidad IAM con permisos mínimos.
- Credenciales obtenidas mediante variables de entorno, perfiles o roles.
- Acceso exclusivo al state local durante la migración.
Comprueba la versión:
terraform versionEl backend no crea el bucket. Debe aprovisionarse antes, mediante un proceso de bootstrap independiente o una plataforma administrativa.
Paso 1: preparar el bucket
El bucket debe contar, como mínimo, con:
- Versionado habilitado.
- Cifrado SSE-S3 o SSE-KMS.
- Acceso público bloqueado.
- Política IAM limitada al prefijo del entorno.
- Monitoreo y registro cuando el nivel de criticidad lo requiera.

HashiCorp recomienda expresamente habilitar S3 Bucket Versioning para recuperar el state ante eliminaciones accidentales o errores humanos. Backend S3 de Terraform.
No uses el mismo objeto key para proyectos o entornos diferentes.
Paso 2: declarar el backend
Crea backend.tf:
terraform {
backend "s3" {
bucket = "mi-organizacion-tfstate-prod"
key = "web/terraform.tfstate"
region = "us-east-1"
encrypt = true
use_lockfile = true
}
}Los parámetros principales son:
bucket: bucket existente donde se almacena el state.key: ruta del objeto dentro del bucket.region: región del bucket.encrypt: solicita cifrado del state y del archivo de lock.use_lockfile: habilita el locking mediante<key>.tflock.
El locking de S3 es opcional y debe habilitarse explícitamente. El mecanismo anterior basado en DynamoDB está deprecado y será eliminado en una versión futura. State locking del backend S3.
Si utilizas una clave KMS administrada por la organización:
terraform {
backend "s3" {
bucket = "mi-organizacion-tfstate-prod"
key = "web/terraform.tfstate"
region = "us-east-1"
encrypt = true
kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/TU_KEY_ID"
use_lockfile = true
}
}La identidad necesitará también permisos como kms:Encrypt, kms:Decrypt y kms:GenerateDataKey.
Paso 3: configurar permisos mínimos
Para un state sin workspaces, Terraform necesita normalmente:
s3:ListBucketlimitado al prefijo correspondiente.s3:GetObjectys3:PutObjectsobre el objeto de state.s3:GetObject,s3:PutObjectys3:DeleteObjectsobre el archivo.tflock.
Terraform no necesita s3:DeleteObject sobre el state para su operación normal. Sí lo necesita sobre .tflock, porque debe eliminar el bloqueo al finalizar. Permisos requeridos por el backend S3.
No concedas acceso general a todos los states de producción cuando cada equipo solo necesita administrar un prefijo.
Paso 4: proporcionar credenciales
No incluyas claves de acceso dentro de backend.tf:
# No hacer esto
access_key = "..."
secret_key = "..."Utiliza perfiles, variables de entorno de corta duración o roles asumidos:
export AWS_PROFILE=infra-produccion
export AWS_REGION=us-east-1Las credenciales entregadas directamente mediante la configuración del backend o ciertos usos de -backend-config pueden quedar registradas dentro de .terraform/ y de archivos de plan. HashiCorp recomienda utilizar variables de entorno o archivos de credenciales estándar. Credenciales del backend S3.
Paso 5: inicializar un proyecto nuevo
Si el proyecto todavía no tiene state:
terraform initinit:
- Configura el backend.
- Descarga proveedores y módulos.
- Crea
.terraform/. - Genera o actualiza
.terraform.lock.hcl.
terraform init es idempotente y puede ejecutarse nuevamente cuando cambian módulos, proveedores o la configuración del backend. Inicialización de Terraform.
Versiona .terraform.lock.hcl: permite que el equipo utilice las mismas versiones seleccionadas de los proveedores.
Paso 6: migrar un state local existente
Si ya existe terraform.tfstate, detén temporalmente las ejecuciones del equipo. No permitas planes ni applies mientras se realiza la migración.
Primero crea una copia protegida fuera del repositorio:
cp terraform.tfstate ../terraform.tfstate.pre-migration
chmod 600 ../terraform.tfstate.pre-migrationDespués agrega el bloque del backend y ejecuta:
terraform init -migrate-stateRevisa la pregunta de confirmación antes de aceptar la copia al backend.
-migrate-state intenta trasladar el estado existente. En cambio, -reconfigure descarta la configuración anterior del backend y no migra automáticamente el state. Opciones de inicialización del backend.
No uses -force-copy durante la primera migración: elimina las confirmaciones interactivas y aumenta el riesgo de copiar el state equivocado.
Paso 7: verificar la migración
Comprueba que Terraform puede leer el state remoto:
terraform state listCompara los recursos mostrados con los esperados. Después genera un plan:
terraform plan -lock-timeout=5mUna migración correcta no debería proponer crear nuevamente todos los recursos existentes.
Confirma también en S3:
- Que existe el objeto definido por
key. - Que el objeto tiene cifrado.
- Que el bucket conserva versiones.
- Que las políticas públicas están bloqueadas.
- Que el archivo
.tflockaparece temporalmente durante operaciones con lock.
Conserva la copia local solo hasta completar estas verificaciones. Después elimínala mediante el procedimiento seguro definido por la organización.
Paso 8: validar formato y configuración
terraform fmt -check -recursive
terraform validatefmt -check devuelve un código distinto de cero cuando encuentra archivos con formato no canónico. validate comprueba sintaxis, tipos y consistencia interna, pero no valida credenciales, APIs del proveedor ni recursos remotos. Formato y validación.
Para validar un módulo sin conectarse al backend:
terraform init -backend=false
terraform validatePaso 9: generar un plan revisable
terraform plan \
-lock-timeout=5m \
-out=tfplanMuestra el contenido legible:
terraform show -no-color tfplanRevisa especialmente:
- Recursos con
destroy. - Reemplazos representados como destrucción y creación.
- Cambios de red, IAM, almacenamiento o bases de datos.
- Valores conocidos únicamente después del apply.
- Diferencias causadas por cambios fuera de Terraform.
- Cantidad total de altas, modificaciones y bajas.
Un plan guardado puede contener secretos en texto claro, incluso cuando las variables estén marcadas como sensibles. No lo subas al repositorio ni lo publiques como log abierto. Seguridad de los planes guardados.
Si CI conserva tfplan, debe hacerlo como un artefacto privado, cifrado, con acceso restringido y retención breve.
Paso 10: aplicar exactamente el plan revisado
Cuando la revisión haya sido aprobada:
terraform apply \
-lock-timeout=5m \
tfplanAl recibir un archivo guardado, terraform apply ejecuta sus acciones sin solicitar una confirmación adicional. Pasar tfplan se interpreta como la aprobación del plan. Modo de plan guardado.
Por esta razón:
- No apliques un plan que no hayas inspeccionado.
- No reemplaces el archivo después de la aprobación.
- Limita quién puede ejecutar el job de apply.
- Conserva la relación entre commit, plan aprobado y ejecución.
- No uses
-lock=falsepara evitar una espera.
Paso 11: comprobar el resultado
Revisa las salidas:
terraform outputGenera un nuevo plan:
terraform plan \
-lock-timeout=5m \
-detailed-exitcodeLos códigos son útiles en automatización:
0: no hay cambios.1: ocurrió un error.2: todavía existen cambios pendientes.
Comprueba además:
- Resultado del pipeline.
- Estado de los recursos críticos.
- Versión nueva del state en S3.
- Ausencia de un
.tflockabandonado. - Logs de auditoría del proveedor.
Separación de entornos
Para producción y staging, prioriza directorios raíz, objetos de state, permisos y, cuando corresponda, cuentas cloud diferentes:
environments/
├── staging/
│ ├── backend.tf
│ └── main.tf
└── production/
├── backend.tf
└── main.tfCada entorno debe usar un key diferente:
staging/web/terraform.tfstate
production/web/terraform.tfstateLos workspaces de Terraform CLI equivalen principalmente a mantener estados separados dentro del mismo directorio. No son una frontera fuerte de seguridad ni una buena herramienta para separar sistemas que requieren distintas credenciales y controles de acceso. Limitaciones de los workspaces.
Archivos que deben excluirse del repositorio
Incluye reglas como estas en .gitignore:
.terraform/
*.tfstate
*.tfstate.*
.terraform.tfstate.lock.info
tfplan
*.tfplan
plan.txt
*.tfvars
!example.tfvarsDebes versionar:
*.tf
.terraform.lock.hcl
example.tfvarsHashiCorp recomienda excluir states, backups, planes guardados, .terraform/ y archivos de variables con secretos, pero conservar .terraform.lock.hcl. Guía de estilo de Terraform.
Problemas frecuentes
El backend no inicializa
Comprueba:
- Nombre y región del bucket.
- Credenciales o rol activo.
- Acceso de red hacia S3.
- Permisos sobre el prefijo.
- Permisos sobre
.tflock. - Compatibilidad de la versión de Terraform.
Usa registros detallados solo de forma temporal:
TF_LOG=INFO terraform initLos logs pueden contener información sensible. No los publiques sin revisarlos.
Error al adquirir el lock
No uses inmediatamente terraform force-unlock. Primero confirma:
- Que no exista otro plan o apply activo.
- Que el pipeline anterior haya terminado.
- Que el archivo
.tflockno pertenezca a una operación válida. - Que la identidad tenga permiso para eliminar el lock.
force-unlock debe utilizarse únicamente con el identificador correcto y después de descartar una ejecución concurrente.
El plan recrea todos los recursos después de migrar
Detén el apply. Terraform probablemente está leyendo otro state, workspace o key.
Comprueba:
terraform workspace show
terraform state listRevisa el backend efectivo almacenado en .terraform/ y repite la migración solo después de identificar el state correcto.
El plan muestra reemplazos inesperados
Busca qué atributo provoca el reemplazo y consulta la documentación del recurso. No asumas que el cambio es inocuo: un reemplazo puede implicar pérdida de datos, cambio de dirección o indisponibilidad.
El locking no funciona
Confirma que el backend contenga:
use_lockfile = truey que IAM permita GetObject, PutObject y DeleteObject sobre:
web/terraform.tfstate.tflockBuenas prácticas para producción
- Habilita versionado y cifrado del bucket.
- Activa
use_lockfile. - Aplica privilegios mínimos sobre el state y
.tflock. - Usa roles y credenciales temporales.
- Separa states y permisos por entorno.
- Versiona
.terraform.lock.hcl. - Conserva planes únicamente como artefactos protegidos.
- Exige revisión de pares para producción.
- Revisa todos los reemplazos y destrucciones.
- Evita
-lock=falseyforce-unlocksalvo diagnóstico confirmado. - Relaciona cada apply con un commit y un plan aprobado.
- Prueba periódicamente el procedimiento de recuperación desde versiones de S3.
Un backend remoto reduce el riesgo operativo, pero no vuelve seguro al state por sí solo. La protección real depende de combinar locking, versionado, cifrado, permisos mínimos y un flujo donde se aplique exactamente el plan revisado.