
La infraestructura como código (IaC) es complicada. Muchas veces tienes que montar infraestructura (hoy, casi siempre con Terraform) y terminas escribiendo código a la medida de tu caso puntual.
Lo normal es que andes corto de tiempo y acabes hardcodeando todos los valores por defecto en uno o dos archivos enormes que concentran todo el trabajo. Mucha gente piensa: "por ahora que funcione y ya veré cómo mejorarlo cuando tenga un rato libre".
Terraformeando la infraestructura, esta vez como se debe.
Pasa el tiempo y te cae otra tarea encima. A esas alturas ya ni te acuerdas de qué hiciste exactamente para "que funcionara". O bien, otra persona tiene que meterse en tu código para entenderlo y adaptarlo a la tarea anterior y a la nueva al mismo tiempo.
Y, por encima de todo, está ese miedo paralizante a romper algo que funciona solo por querer "mejorarlo".
Recorrí ese camino muchísimas veces y nunca había dado con la forma de hacer que Terraform fuera un poco más amigable y fácil de depurar. Hasta que leí este post de Yevgeniy Birkman: " 5 lessons learned from writing over 300,000 lines of infrastructure code".
Me abrió los ojos sobre cómo lograr que Terraform sea más robusto, más limpio, más amigable y, sobre todo, cómo ganar la confianza necesaria para hacer los cambios o las mejoras que hagan falta.
Quiero compartir algunos consejos y aprendizajes propios sobre cómo refactorizar Terraform. Lo que sigue es un resumen de las slides que encontrarás al final del artículo.
Terraform monolítico
Cuando trabajas dentro de un archivo TF gigante, basta un error pequeño para que todo se caiga. Además, depurar Terraform se vuelve mucho más difícil y dar con la sección exacta sobre la que tienes que trabajar consume un montón de tiempo. Tarde o temprano, este patrón deriva en código duplicado y ciclos de desarrollo más lentos.
Vista a 10.000 pies
Encontrar rápido lo que buscas durante el desarrollo o al depurar un problema crítico es clave para trabajar con Terraform.
El plan de Terraform consolida todos los archivos en una sola ejecución, y eso podemos aprovecharlo creando archivos más chicos con mucha mejor visibilidad. Por lo general, esos archivos más pequeños se irán articulando como un módulo más reutilizable y componible.
Anatomía de un módulo
Las "300.000 líneas de código de infraestructura" fueron un excelente punto de partida y di un paso más creando un scaffold que uso en cada módulo. Este scaffold aporta mejor visibilidad y lineamientos claros para desarrollar un módulo.
No hay valores hardcodeados: cada valor hardcodeado se transforma en una variable por defecto y cada atributo del módulo es una variable (algunas obligatorias, otras con valor por defecto). Todo módulo debería seguir esta estructura:

La idea es ubicar cada elemento donde corresponde. Si el archivo main.tf supera las 30 líneas, divídelo en archivos lógicos: ec2.tf, autoscaling.tf, etc.
Una buena práctica es usar la carpeta 'examples' para el desarrollo del módulo y, además, como ejemplo de uso para futuras referencias. Cuando arranco un módulo nuevo, uso este snippet para crear el scaffold:
➜ export module_name="sample"➜ mkdir -p $module_name/examples $module_name/test➜ cd $module_name && touch \ main.tf \ versions.tf \ default-variables.tf \ required-variables.tf \ outputs.tf \ data.tfEstructura de módulos en 3 capas
Crea y amplía tu propia librería de bloques primitivos (terraform-resources).

Después, arma servicios a partir de esos bloques primitivos (terraform-services).

Despliega entornos end-to-end a partir de los servicios (terraform-live-envs).

El objetivo es aislar cada entorno (live) —dev, staging, production—, tomar cada componente y dividirlo en módulos de servicio genéricos. A su vez, divide cada módulo de servicio genérico en módulos de recursos, porque cada módulo debe hacer una sola cosa y estar lo más desacoplado posible.
Veamos el siguiente ejemplo:
Tienes una instancia mySQL desplegada en Google Cloud SQL (terraform-live-envs) construida sobre un servicio genérico llamado "sql", que a su vez contiene dos módulos de recursos (instance y user).

Refactorizar el código de Terraform
Crea un bucket nuevo donde se almacenará el nuevo estado de Terraform. Luego, reescribe tu código siguiendo los módulos en 3 capas (como se muestra arriba y se detalla en las slides). Importa cada uno de los recursos a tu código de Terraform en live-envs.
Terraform te mostrará el plan de ejecución de la operación de importación:
[1] los valores que existen en la versión desplegada pero no en tu código aparecerán con un signo menos para eliminarse.
[2] los valores que no existen en la versión desplegada pero sí en tu código aparecerán con un signo más para añadirse.
La meta es llegar al punto en el que el plan no muestre ningún cambio.
Slides
Las siguientes slides incluyen una breve introducción a Terraform y mis sugerencias para refactorizar módulos de Terraform existentes: 