Todo el mundo sabe hacer terraform init y terraform apply. El problema empieza cuando hay cinco personas tocando la misma infraestructura, tres entornos que mantener y alguien que ejecuta terraform destroy pensando que está en staging. En ese momento descubres que aprender Terraform y usar Terraform en producción son dos cosas completamente distintas.
Este artículo no es para los que están empezando con Terraform. Es para los que llevan meses usándolo y empiezan a notar que su estructura no escala, que el state da problemas o que nadie del equipo sabe exactamente qué va a pasar antes de un apply.
El problema de escalar Terraform sin estructura
En proyectos pequeños, Terraform es intuitivo. Un directorio, unos ficheros .tf, un state local y listo. El problema es que ese enfoque no escala. Cuando el proyecto crece, aparecen los síntomas: módulos duplicados en varios repositorios, state files que nadie sabe dónde están, conflictos cuando dos personas hacen apply al mismo tiempo y nadie que entienda del todo qué hay desplegado.
La solución no es más disciplina. Es estructura. Estas son las tres áreas donde los proyectos de Terraform en producción se rompen con más frecuencia.
1. Cómo estructurar módulos que realmente se reutilizan
Un módulo de Terraform bien diseñado es como una función bien diseñada: hace una cosa, la hace bien y tiene una interfaz clara. Un módulo mal diseñado es un bloque de 800 líneas de HCL con 60 variables donde nadie sabe qué es obligatorio y qué es opcional.
La regla del módulo de propósito único
Cada módulo debe encapsular un recurso o un conjunto cohesivo de recursos relacionados. Un módulo de EKS crea el cluster, los node groups y los roles IAM necesarios para el cluster. No crea la VPC. No crea las políticas de seguridad de la aplicación. Eso va en módulos separados.
La señal de que un módulo está haciendo demasiado: cuando tienes que leer 200 líneas de variables para entender qué hace. Si no puedes explicar el módulo en una frase, está haciendo demasiadas cosas.
Versiona tus módulos
El error más frecuente que vemos: módulos referenciados sin versión fija.
Versionado de módulos: mal vs bien
source = "git::https://github.com/org/modules.git//eks" — cualquier cambio en el módulo upstream rompe todos los entornos que lo usan, sin previo aviso.source = "git::https://github.com/org/modules.git//eks?ref=v2.1.0" — producción usa v2.1.0, staging puede probar v2.2.0 antes de promocionar.source = "hashicorp/consul/aws" version = "~> 0.1" — para módulos del Terraform Registry, constraints de versión semántica.La interfaz del módulo importa más que la implementación
Las variables de un módulo son su API pública. Trátalas como tal: documenta cada variable con descripción y tipo, define valores por defecto sensatos para lo que tenga sentido y agrupa las variables obligatorias al principio del fichero variables.tf.
Los outputs también son parte de la interfaz. Expón lo que otros módulos van a necesitar: ARNs, IDs de recursos, endpoints. No expongas detalles internos de implementación.
main.tf del módulo para entender cómo usarlo, la interfaz no está bien documentada.2. Remote state: la única opción en producción
El state de Terraform es el mapa de tu infraestructura. Guarda la relación entre los recursos que declaras en código y los recursos reales que existen en el cloud. Si ese mapa se pierde, se corrompe o dos personas lo modifican al mismo tiempo, los problemas que siguen son serios.
El state local es aceptable para aprender. En producción, siempre remote state.
La configuración mínima de remote state en AWS
La combinación estándar en AWS es S3 como backend con DynamoDB para el locking. El bucket de S3 guarda el state. DynamoDB garantiza que solo una persona puede hacer apply al mismo tiempo mediante un lock que se adquiere al inicio y se libera al terminar.
- Bucket S3 con versionado: activa el versionado en el bucket. Si el state se corrompe, puedes recuperar la versión anterior. Hemos usado esto en producción más veces de las que nos gustaría admitir.
- Cifrado en reposo: el state puede contener valores sensibles. KMS con una clave gestionada por el cliente es lo correcto. El cifrado por defecto de S3 es el mínimo aceptable.
- Acceso restringido: no todo el equipo necesita acceso de escritura al state. Los roles de solo lectura son suficientes para hacer plan. El apply debe ser un proceso controlado.
- Tabla DynamoDB con locking: la tabla necesita un atributo
LockIDcomo partition key. El resto lo gestiona Terraform automáticamente.
Separación de state por entorno y por componente
Un state para toda la infraestructura es un antipatrón. Si el state es enorme, las operaciones de plan y apply son lentas y cualquier error afecta a todo. La separación correcta:
- Por entorno: dev, staging y prod tienen states separados. Un
terraform destroyaccidental en dev no puede afectar a prod. - Por capa: separamos la infraestructura de red (VPCs, subredes) de los servicios (EKS, RDS) y de las aplicaciones. Cada capa tiene su propio state y su propio ciclo de vida.
- Por equipo: en organizaciones grandes, cada equipo gestiona su propio state. Usan
terraform_remote_statepara leer outputs de otros stacks sin tener acceso de escritura.
s3://company-terraform-state/{entorno}/{componente}/terraform.tfstate. La estructura del bucket refleja la arquitectura de la infraestructura. Fácil de auditar, fácil de gestionar permisos por path.3. Cómo evitar el terraform destroy accidental
terraform destroy elimina todos los recursos gestionados por un state. Es una operación legítima y necesaria. También es la operación que más incidentes de producción hemos visto causar cuando se ejecuta en el entorno equivocado o cuando alguien no entiende bien su alcance.
Prevent destroy en recursos críticos
Terraform tiene un lifecycle argument llamado prevent_destroy. Cuando está activo, cualquier plan que intente destruir ese recurso falla con un error explícito. Úsalo en bases de datos, buckets S3 con datos importantes y cualquier recurso cuya pérdida sea catastrófica.
Workspaces o directorios separados por entorno
La protección más efectiva contra el destroy en el entorno equivocado es hacer que sea estructuralmente imposible confundirse. Dos estrategias funcionan:
- Directorios separados:
envs/dev/,envs/staging/,envs/prod/. Cada directorio tiene su propio backend configuration apuntando a su propio state. Para hacer apply en prod tienes que estar en el directorio de prod. Simple y efectivo. - Pipelines con aprobación manual para prod: el apply en producción nunca se ejecuta desde una máquina local. Siempre desde una pipeline de CI/CD con aprobación manual de al menos dos personas. El plan se ejecuta automáticamente, el apply requiere aprobación explícita.
El plan como contrato antes del apply
Nadie debería ejecutar terraform apply sin haber leído el plan. Y leer el plan significa entenderlo, no solo hacer scroll hasta el final para ver si hay errores.
Lo que buscamos en un plan antes de aprobarlo: cuántos recursos se crean, cuántos se modifican y cuántos se destruyen. Si el plan destruye recursos que no esperábamos destruir, paramos y entendemos por qué antes de continuar. Un destroy inesperado en el plan es casi siempre señal de que algo está mal en el código o en el state.
Trabajar en equipo con Terraform sin pisarse
El locking del state previene conflictos técnicos. El proceso de trabajo previene conflictos humanos. Estos son los hábitos que hacen que un equipo funcione bien con Terraform:
- Los cambios de infraestructura pasan por PR review: igual que el código de aplicación. Nadie hace apply de cambios sin que al menos otra persona haya revisado el código y el plan.
- El plan se genera y se adjunta a la PR: Atlantis o GitHub Actions pueden generar el plan automáticamente como comentario en la PR. El reviewer ve exactamente qué va a cambiar sin necesidad de ejecutar nada en local.
- Un entorno a la vez: si alguien está haciendo apply en staging, nadie hace apply en staging hasta que termine. El locking de DynamoDB gestiona esto técnicamente, pero es buena práctica comunicarlo también.
- Los applies de producción se hacen con el equipo disponible: nunca en viernes por la tarde, nunca sin alguien que pueda ayudar si algo va mal.
Conclusión
Terraform en producción no es difícil. Es disciplinado. Los módulos bien versionados, el remote state correctamente configurado y los procesos de revisión antes de cada apply son lo que separa una infraestructura que da problemas de una que da confianza.
La mayoría de los incidentes de infraestructura que hemos visto no venían de Terraform haciendo algo inesperado. Venían de alguien ejecutando Terraform sin entender completamente qué iba a hacer. El plan existe precisamente para eso: léelo.
¿Tu equipo usa Terraform pero sin estructura sólida?
Te ayudamos a definir la arquitectura de módulos, el remote state y los procesos para que Terraform escale con tu equipo sin generar incidentes.
Hablar con un experto →