Módulo 3: The Iac Pipeline Fmt Validate Plan
1. Introducción al módulo: lo que corre antes de fusionar
Descripción
En los dos módulos anteriores construiste la herramienta y aprendiste su idioma: instalaste act, corriste workflows de punta a punta, disecaste on/jobs/steps/runs-on/uses/with/env, y aprendiste a simular eventos y secretos sin depender de una cuenta de GitHub real. Pero hasta ahora, ningún workflow de esta guía tocó la infraestructura de negocio de Andes Cargo — hello-andes-cargo.yml (Módulo 2) solo confirmó que el contenedor del job puede llegar hasta LocalStack, nunca corrió terraform plan ni terraform apply sobre el bucket, la tabla, los roles o la función real. Este módulo cierra esa brecha: vas a construir la mitad de CI del pipeline de Andes Cargo — el patrón estándar de la industria donde terraform fmt, terraform validate y terraform plan corren automáticamente en cada Pull Request, y el plan resultante se convierte en el artefacto que alguien revisa antes de fusionar. Al cerrar este módulo vas a tener ci.yml, el primer workflow de esta guía que ejecuta Terraform de verdad, dentro de un runner efímero, contra la misma infraestructura que terraform-and-iac-guide te enseñó a declarar a mano.
Conexión con el módulo
Las lecciones 2 y 3 son el porqué y el cómo del patrón: qué dice el tutorial oficial de HashiCorp sobre separar "revisar" de "aplicar" en dos workflows distintos (lección 2), y cómo un runner efímero instala Terraform en cada corrida, sin dejar rastro entre una corrida y la siguiente (lección 3). Las lecciones 4, 5 y 6 son manos a la obra puras, construyendo ci.yml pieza por pieza: fmt/validate como los primeros dos steps que pueden fallar el job en rojo (lección 4), la conexión de red hacia LocalStack a través de host.docker.internal (lección 5), y el primer terraform plan real corriendo dentro de ese runner (lección 6). La lección 7 cierra el círculo de revisión: publicar ese plan como evidencia, con el patrón real de GitHub ($GITHUB_STEP_SUMMARY, ejecutado) y el patrón de mercado (comentar en el PR, mostrado). La lección 8 —el proyecto— corre ci.yml completo, de punta a punta, con act pull_request -e pr-event.json, sobre un cambio real al HCL de Andes Cargo.
Qué te llevas de los Módulos 1 y 2 (y qué falta)
Al cerrar el Módulo 2 sabías escribir un workflow completo, simular cualquier evento con act -e, y pasarle secretos sin comprometerlos jamás en un commit. Lo que todavía no sabías —y es exactamente lo que resuelve este módulo—:
- Qué corre Terraform dentro de CI, no en tu laptop:
terraform fmt,terraform validate,terraform plan, cada uno con un propósito distinto y una razón distinta para fallar el job. - Por qué el patrón separa
plandeapplyen dos workflows distintos, en vez de uno solo con una condición — una decisión de seguridad, no de comodidad, citada directamente del tutorial oficial de HashiCorp. - Cómo instalar Terraform dentro de un runner que no persiste nada entre una corrida y la siguiente — a diferencia de tu laptop, donde
terraform-and-iac-guideinstaló el binario una vez y ahí se quedó. - Cómo ese runner efímero llega hasta LocalStack, que corre en tu host, no dentro del contenedor del job — la misma pieza de red que probaste con
awslocalen el Módulo 2, ahora al servicio de unterraform planreal. - Qué significa "revisar un
planantes de fusionar", en la práctica: no es una frase — es un artefacto de texto, generado por una máquina, que una persona lee antes de decir que sí.
El mapa de este módulo: las 8 lecciones
| # | Lección | Qué practicas |
|---|---|---|
| 1 | Introducción (esta) | El mapa del módulo, qué falta de los Módulos 1-2, por qué el plan es el punto de revisión |
| 2 | El patrón HashiCorp/GitHub: plan en PR, apply en merge | El patrón estándar de la industria, citado del tutorial oficial; por qué separar en dos workflows es más seguro |
| 3 | Instalando Terraform dentro de un runner efímero | hashicorp/setup-terraform@v3, versión pineada; por qué el runner instala Terraform en cada corrida |
| 4 | Manos a la obra: fmt y validate como pasos que pueden fallar el job | Ejecutado: ambos steps corriendo dentro de act, un error introducido a propósito, visto fallar en rojo, corregido |
| 5 | Conectando el runner a LocalStack a través del host | Ejecutado: host.docker.internal:4566, .actrc extendido, verificado con awslocal s3 ls |
| 6 | Manos a la obra: terraform plan corriendo dentro de CI | Ejecutado: el primer terraform plan real de esta guía, dentro de un job de act |
| 7 | Publicar el plan como evidencia de revisión | Ejecutado: $GITHUB_STEP_SUMMARY; Representativo: comentar el plan en el PR con actions/github-script |
| 8 | Proyecto: el ci.yml de Andes Cargo | Ejecutado: el workflow completo, de punta a punta, con act pull_request -e pr-event.json |
Analogía del módulo: el control de calidad antes del embarque, no la firma en la aduana
En terraform-and-iac-guide, cada vez que ibas a cambiar la infraestructura de Andes Cargo, corrías terraform plan en tu terminal, lo leías con tus propios ojos, y recién después corrías terraform apply — todo bajo tu control directo, un paso después del otro, sin que nadie más lo viera antes de que pasara. Ese flujo funciona cuando el único que puede tocar la infraestructura eres tú. Deja de funcionar en el momento en que un segundo, un tercer o un décimo compañero también puede proponer cambios: si cada uno corre su propio plan en su propia laptop, con sus propias credenciales, nadie más ve ese plan antes de que se aplique — el equivalente a que cada trabajador de un puerto decida, por su cuenta, qué contenedor sube al barco, sin que un inspector lo revise antes de sellar la bodega.
Este módulo construye exactamente ese inspector: un proceso automático que, en cuanto alguien propone un cambio (un Pull Request), genera el mismo plan que tú generarías a mano — pero lo hace en un lugar donde cualquiera con acceso al repositorio puede leerlo antes de que se aplique nada. El plan no es una formalidad que se firma sin mirar en la aduana; es la lista de carga que un inspector revisa contenedor por contenedor, antes de que el barco zarpe — no un sello que se estampa después de que ya zarpó. Eso es, literalmente, lo que "el plan es el punto de revisión, no el apply" significa: para cuando alguien corre apply, ya no debería haber ninguna sorpresa — todo lo que ese apply va a hacer, ya se leyó, ya se discutió, ya se aprobó, en el plan que lo precedió.
Por qué el plan es el punto de revisión, no el apply
Vale la pena ser explícito sobre esto antes de escribir una sola línea de YAML, porque es la idea que organiza el módulo entero. Hay dos maneras de construir un pipeline de infraestructura, y solo una es segura:
Camino equivocado: un único paso que corre terraform apply directamente en cuanto alguien propone un cambio, sin que nadie lo revise primero. Rápido, pero equivalente al apply manual que el Módulo 1 ya identificó como el problema original — solo que ahora la falta de revisión está automatizada, no resuelta.
Camino de esta guía (y de la industria): terraform plan corre automáticamente en cada Pull Request, antes de que exista la posibilidad de fusionar. El plan es un artefacto de solo lectura — no cambia nada, no toca ningún recurso real, solo calcula y muestra qué cambiaría si alguien corriera apply. Eso lo convierte en el lugar perfecto para poner un ser humano en el medio: alguien lee ese plan, confirma que hace lo que el Pull Request dice que hace, y recién entonces aprueba la fusión. El apply real —que si toca infraestructura de verdad— llega después, disparado por la fusión misma, no por la propuesta. Vas a construir esa segunda mitad en el Módulo 5; este módulo construye la primera, la que hace posible que exista algo que revisar.
Lo que NO cambia en este módulo
Sigues sin gastar un centavo, y el proyecto de Andes Cargo sigue siendo, línea por línea, el mismo HCL que terraform-and-iac-guide dejó terminado en su capstone: bucket andes-cargo-shipment-docs, tabla Shipments, roles LambdaManifestProcessorRole y AppServerRole, función process-shipment-manifest. Este módulo no declara un solo recurso de negocio nuevo — todo el trabajo es construir el pipeline que corre ese HCL, no ampliar lo que ese HCL declara. Misma cuenta 000000000000, misma región us-east-1.
Lo que sí cambia: por primera vez en esta guía, un workflow va a ejecutar Terraform de verdad. Ningún módulo anterior lo hizo — hello-andes-cargo.yml (Módulo 2) solo confirmó una ruta de red con awslocal, un comando de AWS CLI, no de Terraform.
Antes y después de este módulo
ANTES (fin del Módulo 2) DESPUÉS (fin del Módulo 3)
"andes-cargo-infra/ tiene HCL real, "Cada Pull Request dispara,
pero ningún workflow lo toca automáticamente, fmt + validate
todavía." + plan sobre ese mismo HCL."
"Sé que el contenedor del job "Sé que ese mismo camino de red
puede llegar a LocalStack, porque sostiene un terraform plan real,
lo probé con un comando de AWS CLI." no solo un comando de AWS CLI."
"No tengo ningún artefacto que "Tengo un plan legible, publicado
alguien pueda revisar antes de en el resumen del job, listo para
que un cambio se aplique." que alguien lo revise antes de
fusionar."
Errores comunes
Pensar que "CI para Terraform" significa correr apply automáticamente (conceptual, la confusión central de este módulo). Qué pasa: alguien, familiarizado con CI de código de aplicación (donde "pasar CI" a veces significa "ya se puede desplegar solo"), asume que la mitad de CI de un pipeline de infraestructura también debería aplicar los cambios. Por qué pasa: en muchos pipelines de aplicación, CI y CD están más fusionados de lo que este módulo enseña. Cómo detectarlo: si tu primer instinto al ver ci.yml es preguntar "¿y dónde está el apply?". Cómo corregirlo: recuerda la tabla del M1.3 — CI valida, CD-entrega deja listo con un gate humano, CD-despliegue aplica solo. Este módulo construye exclusivamente la parte de CI: fmt/validate/plan, nunca apply. El apply automático, disparado por la fusión a main, es contenido del Módulo 5 — una decisión deliberada, no un error de alcance.
Asumir que un plan "limpio" (sin errores) es lo mismo que un plan "aprobado" (de proceso). Qué pasa: alguien ve que ci.yml termina en verde y asume que eso equivale a que el cambio ya está aprobado para fusionar. Por qué pasa: un job verde se siente como "todo salió bien", sin distinguir entre "el plan se generó sin errores técnicos" y "una persona lo leyó y decidió que el cambio es correcto". Cómo corregirlo: un plan verde solo confirma que Terraform pudo calcular qué cambiaría — no dice nada sobre si ese cambio es el correcto. Por eso el Módulo 6 (branch protection) exige que un humano revise y apruebe antes de que la fusión sea posible, incluso con ci.yml en verde.
Ejercicios
Ejercicio 1 — Ordena las tres etapas de este módulo. Sin mirar el mapa de arriba, ordena estas tres cosas en el orden en que ci.yml las va a ejecutar dentro de un mismo job: (a) terraform plan, (b) terraform fmt -check, (c) terraform validate. Justifica el orden.
Ver solución
El orden correcto es (b) fmt -check → (c) validate → (a) plan. La razón es de costo creciente y de dependencia: fmt -check es instantáneo y no necesita ningún provider descargado — revisa solo el estilo del texto HCL. validate sí necesita los providers inicializados (terraform init), pero solo revisa sintaxis y coherencia interna, sin tocar ningún servicio de AWS/LocalStack. plan es el más costoso y el único que puede necesitar hablar con un proveedor real — tiene sentido dejarlo al final, para no gastar ese costo si el archivo ni siquiera tiene el formato correcto. Cada paso previo actúa como filtro barato antes del paso caro.
Ejercicio 2 — Explica la analogía del inspector portuario con tus propias palabras. Sin usar la palabra "inspector", explica en dos frases por qué revisar el plan antes de fusionar es distinto de revisar el código de la infraestructura después de que ya se aplicó.
Ver solución
Una respuesta completa suena, más o menos, así: "Revisar el plan antes de fusionar es como revisar la lista de carga antes de que el barco zarpe — todavía hay tiempo de corregir algo sin costo. Revisar la infraestructura después de aplicada es como abrir la bodega en el destino y descubrir que algo no debía estar ahí — para ese momento, ya zarpó, y corregirlo cuesta mucho más que haberlo visto a tiempo."
Ejercicio 3 — Anticipa por qué separar ci.yml de apply.yml. Sin haber leído todavía la lección 2, propón una razón de seguridad (no de organización de archivos) por la que sería peligroso que un único workflow, disparado por pull_request, tuviera tanto el plan como el apply en el mismo archivo, con una condición que decida cuál corre.
Ver solución
Un Pull Request puede venir de cualquiera con permiso de proponer cambios —incluido alguien de fuera del equipo central, en un repositorio con colaboradores externos—. Si el apply vive en el mismo archivo que el plan, disparado por el mismo evento (pull_request), la única barrera entre "alguien propuso un cambio" y "ese cambio se aplicó de verdad" es una condición dentro del YAML —código que, en teoría, también podría manipularse desde el propio Pull Request que se está evaluando—. Separar apply.yml en un archivo distinto, disparado únicamente por push a main (un evento que solo ocurre después de que alguien con permiso fusionó el cambio), elimina esa ambigüedad de raíz: no hay ninguna condición que decida si el apply corre, hay un evento completamente distinto que ni siquiera existe hasta que la fusión ya pasó. La lección 2 desarrolla esto a fondo, citado del tutorial oficial de HashiCorp.
Resumen y siguiente paso
En esta lección viste el mapa completo de las 8 lecciones de este módulo, y la idea central que las organiza a todas: el plan —no el apply— es el punto donde un ser humano revisa un cambio de infraestructura antes de que sea irreversible. La analogía del control de calidad antes del embarque —revisar la lista de carga antes de que el barco zarpe, no después de que llegó a destino— es la idea que vas a ver aplicada, en YAML real, en cada lección que sigue.
Antes de avanzar deberías poder: nombrar las 8 lecciones de este módulo y qué construye cada una; explicar por qué "CI para Terraform" nunca significa "apply automático"; y anticipar, aunque sea en términos generales, por qué separar plan de apply en dos workflows distintos es una decisión de seguridad.
La lección 2 abre con el patrón exacto que organiza todo esto — citado, palabra por palabra donde corresponde, del tutorial oficial de HashiCorp para automatizar Terraform con GitHub Actions.
Recursos
- HashiCorp Developer — Automate Terraform with GitHub Actions — el tutorial oficial que define el patrón que este módulo implementa, profundizado en la lección 2.
- GitHub Docs — Understanding GitHub Actions — repaso de la anatomía de un workflow, ya cubierta a fondo en el Módulo 2.
terraform-and-iac-guide(NIEVA) — el proyectoandes-cargo-infra/que este módulo automatiza sin reescribir, y el ciclo manualplan/applyque este módulo reemplaza por un pipeline.