Módulo 6: Rollback And Safety Nets

1. Introducción: qué hacer cuando el `apply` sale mal

Descripción

Los cinco módulos anteriores construyeron un pipeline que funciona cuando todo sale bien: un cambio se propone, ci.yml lo revisa, alguien aprueba, apply.yml lo aplica, drift.yml vigila que nadie lo toque por fuera. Este módulo hace la pregunta que ningún módulo anterior tuvo que responder todavía: ¿qué pasa cuando algo sale mal? No "mal" en el sentido de un error de red o un plan que falla por sintaxis —eso ya lo viste fallar y corregir desde el Módulo 3—, sino "mal" en un sentido más incómodo: un cambio que se fusionó, que apply.yml aplicó sin errores técnicos, y que resulta ser, en retrospectiva, un error de negocio o un riesgo que nadie debió aprobar. Vas a aprender el patrón de rollback específico de infraestructura, una capa de control de repositorio que ningún workflow puede ejecutar pero que cambia por completo quién puede tocar main, una revisión honesta del incidente más citado de esta familia de guías, y vas a construir —de verdad, ejecutado— el primer guardrail automatizado de todo el pipeline.

Conexión con el módulo

Este es el tercer y último módulo de piezas antes del panorama general del Módulo 7 y el capstone del Módulo 8. Donde el Módulo 3 respondió "¿qué cambiaría, y quién lo revisa?" y el Módulo 5 respondió "¿quién lo aplica, y cuándo?", este módulo responde una pregunta distinta, que ningún pipeline puede evitar para siempre: "¿qué hacemos cuando ya se aplicó algo que no debía aplicarse?". Las lecciones 2 y 3 responden con el mecanismo correcto —revertir el commit de HCL, no "deshacer" nada a mano—, ejecutado de punta a punta sobre el mismo ci.yml/apply.yml que ya conoces. La lección 4 agrega una capa de control que vive fuera de cualquier archivo YAML: branch protection, la configuración de repositorio que convierte "cualquiera puede tocar main" en "nada llega sin pasar por ci.yml". La lección 5 vuelve, con más profundidad, al incidente de terraform-and-iac-guide que ya nombraste en el Módulo 1. Las lecciones 6, 7 y 8 cierran el módulo con la pieza que le faltaba a todo el pipeline: un guardrail automatizado, real, que revisa el plan antes de que llegue a apply y detiene, por sí solo, un intento de destruir la tabla Shipments.


Por qué "deshacer infraestructura" no es lo mismo que "revertir código"

Piensa en cómo revertirías un error en una aplicación normal, sin infraestructura de por medio: alguien despliega la versión v42 de un servicio, alguien más nota un bug, y la solución es desplegar de nuevo la v41 —una versión anterior, ya construida, ya probada, lista para volver a correr—. El rollback de aplicación funciona porque el artefacto anterior todavía existe: la imagen de contenedor de v41 sigue en el registro, lista para arrancar de nuevo con un solo comando.

La infraestructura no tiene ese lujo. Cuando apply.yml corre terraform apply sobre un cambio, no despliega una "versión" nueva de un artefacto inmutable — modifica objetos reales que ya existen: agrega un tag a un bucket, cambia una política IAM, borra una tabla. No hay una "versión anterior del bucket" guardada en algún registro, lista para volver a activarse con un docker run. Lo único que existe es el HCL que describía el estado anterior, y la única forma de "deshacer" el cambio es escribir de nuevo ese HCL —o, más precisamente, dejar que Git lo escriba por ti— y dejar que el mismo pipeline lo vuelva a aplicar.

   ROLLBACK DE APLICACIÓN                    ROLLBACK DE INFRAESTRUCTURA

   v42 desplegada, con un bug                cambio aplicado, con un problema
        │                                          │
        ▼                                          ▼
   la v41 YA EXISTE                           NO existe una "versión anterior"
   (imagen en el registro)                    del bucket/tabla/rol guardada
        │                                          │
        ▼                                          ▼
   desplegar v41 de nuevo                     git revert del commit de HCL
   (activar un artefacto                      (recrear la DESCRIPCIÓN del
    ya construido)                             estado anterior)
        │                                          │
        ▼                                          ▼
   el runtime cambia de                       el MISMO pipeline (ci.yml → apply.yml)
   versión activa                             vuelve a correr, con el plan al revés

Este módulo enseña la columna de la derecha. La columna de la izquierda —rollback de una aplicación, de un contenedor, de un servicio corriendo— es un tema completo por derecho propio, y vive en kubernetes-and-eks-in-production-guide y aws-serverless-and-containers-guide, no aquí.


El mapa de este módulo: las 8 lecciones

#LecciónQué practicas
1Introducción (esta)El mapa completo; por qué "deshacer infraestructura" no es "revertir código"
2Qué significa rollback para infraestructuraEl patrón exacto: git revert del commit de HCL, no una operación de Terraform nueva; frontera al rollback de aplicación
3Manos a la obra: el pipeline de git revertEjecutado: git revert de un commit real, ci.yml corriendo plan sobre el revert, apply.yml intentando aplicarlo
4Branch protection como controlSettings → Branches, PR obligatorio, checks obligatorios — representativo, configuración de repositorio que act no puede ejecutar
5¿Un pipeline hubiera detenido el incidente de Claude Code?Revisión honesta, con más profundidad que el Módulo 1: lo que un pipeline SÍ y NO hubiera cambiado
6Guardrails de apply, nombradosconftest/policy-as-code como el siguiente paso — nombrado, pointer a cloud-security-and-guardrails-guide
7Manos a la obra: un guardrail mínimo, real y ejecutadoEjecutado: un grep sobre terraform show -json que falla el job si el plan destruye Shipments
8Proyecto: la red de seguridad de Andes CargoEjecutado: el pipeline completo con el guardrail integrado, probado con un cambio inocuo y uno destructivo

Lo que este módulo NO construye

Tres fronteras, declaradas desde ya:

  • conftest/Open Policy Agent como sistema de políticas completo (reglas Rego, gobernanza de qué falla el build y qué solo advierte) se nombra en la lección 6, exactamente como hizo terraform-and-iac-guide en su Módulo 8 — la construcción completa vive en cloud-security-and-guardrails-guide. El guardrail que sí construyes en la lección 7 es deliberadamente más simple que eso: un grep artesanal, no una política declarativa.
  • Branch protection contra un repositorio de GitHub.com real se describe con el camino de clics exacto (lección 4), pero no se ejecuta: es configuración de repositorio, no un workflow, y no existe forma de que act la aplique.
  • Rollback de aplicación (volver a una versión anterior de una imagen de contenedor, un rollout de Kubernetes) se nombra por contraste en la lección 2, sin construirse aquí — frontera a kubernetes-and-eks-in-production-guide y aws-serverless-and-containers-guide.

Honestidad de ejecución de este módulo específico

El mismo patrón de los cinco módulos anteriores, con una precisión nueva que vale la pena nombrar desde ya. Las lecciones 3, 7 y 8 son genuinamente ejecutadas, con dos matices que las distinguen de todo lo anterior:

  • La lección 3 reutiliza exactamente el ci.yml/apply.yml ya construidos, sin cambiar una sola línea — lo único nuevo es el commit que revierte, y el plan que ese revert produce. El intento de apply.yml, como en cada módulo anterior sin un LOCALSTACK_AUTH_TOKEN válido, falla con el mismo connection refused honesto que ya conoces.
  • La lección 7 necesita algo que ningún módulo anterior necesitó: un plan que proponga destruir un recurso. Y aquí aparece una consecuencia directa de un hecho que ya estableciste en el Módulo 3 y el Módulo 5: sin un apply real completado contra LocalStack, el state de este proyecto está —y siempre estuvo— completamente vacío. Un state vacío nunca puede producir una acción delete genuina: no hay nada aplicado que destruir. La lección 7 resuelve esto con una técnica legítima y honesta —sembrar el state local con un archivo de prueba (fixture) que declara que la tabla Shipments ya existe, exactamente el mismo principio detrás de probar una política de seguridad contra un plan de ejemplo en vez de depender de infraestructura viva—. Vas a ver el mecanismo completo, verificado, en la lección 7.

Nada de esto se esconde ni se explica después del hecho: cada bloque de código de este módulo dice, en el momento exacto en que aparece, si corrió de verdad y con qué salió del laboratorio.


Errores comunes

Asumir que este módulo enseña a "deshacer" un apply con un comando de Terraform (conceptual, el error que este módulo existe para prevenir). Qué pasa: alguien busca algo como terraform undo o terraform rollback, comandos que no existen. Cómo detectarlo: si tu primera pregunta al abrir este módulo es "¿cuál es el comando de Terraform para deshacer esto?". Cómo corregirlo: Terraform no tiene un comando de rollback — el mecanismo completo es de Git (revertir el commit) combinado con el mismo pipeline que ya construiste (dejar que plan/apply corran de nuevo, en sentido contrario). La lección 2 desarrolla esto con precisión completa.

Creer que branch protection y el guardrail de la lección 7 hacen lo mismo (de alcance). Qué pasa: alguien termina este módulo pensando que ambos mecanismos son intercambiables o redundantes. Cómo corregirlo: branch protection controla quién puede fusionar y bajo qué condición de proceso (PR revisado, checks en verde) — no mira el contenido del cambio. El guardrail de la lección 7 mira específicamente el contenido del plan y bloquea un tipo de cambio concreto (destruir Shipments), sin importar quién lo propuso. Son capas complementarias, no una sustituyendo a la otra — el Módulo 8 las corre juntas, en el mismo pipeline.


Ejercicios

Ejercicio 1 — Explica, sin usar la palabra "revertir", qué haría un rollback de infraestructura. En dos frases, describe el mecanismo completo a un colega que nunca usó Terraform en un pipeline.

Ver solución

Una respuesta completa suena, más o menos, así: "Cuando un cambio de infraestructura resulta ser un error, no existe una 'versión anterior' del recurso guardada en ningún lado para simplemente reactivarla — lo que sí existe es el código que describía el estado anterior, en un commit de Git más viejo. La solución es crear un nuevo commit que deshaga exactamente ese cambio de código, y dejar que el mismo pipeline automatizado —el que ya revisa y aplica cualquier cambio— procese ese nuevo commit como cualquier otro: lo revisa, y si se aprueba, lo aplica."

Ejercicio 2 — Predice el problema central de la lección 7 antes de leerla. Basándote en todo lo que ya sabes de los Módulos 3 y 5 sobre el state de este proyecto en esta máquina específica, ¿por qué crees que probar un guardrail que detecta "destruir Shipments" no puede hacerse simplemente borrando la tabla del HCL?

Ver solución

Porque, como ya confirmaste en el Módulo 3 (lección 6) y el Módulo 5 (lecciones 6 y 7), el state de este proyecto en esta máquina nunca tuvo un apply real completado contra LocalStack — está, y siempre estuvo, vacío. Un plan solo puede proponer destruir un recurso que el state cree que existe; si el state no tiene ningún recurso, quitar ese recurso del HCL simplemente significa "una cosa menos por crear", no "una cosa por destruir". Probar el guardrail de verdad va a necesitar alguna forma de hacerle creer al state que la tabla ya existe, sin depender de un apply real.

Ejercicio 3 — Ubica la frontera de este módulo con cloud-security-and-guardrails-guide antes de llegar a la lección 6. Sin leerla todavía, ¿qué crees que hace conftest que el grep de la lección 7 no hace?

Ver solución

Una respuesta razonable: conftest evalúa el plan en JSON contra un conjunto de reglas declarativas, escritas en un lenguaje de políticas (Rego), que puede expresar condiciones arbitrariamente complejas —sobre cualquier recurso, cualquier atributo, combinaciones de condiciones— y que un equipo entero puede mantener como código de políticas versionado. Un grep solo puede buscar un patrón de texto específico, escrito a mano para un caso muy puntual (esta tabla, esta acción) — funciona, pero no escala a "cualquier política que el equipo necesite" sin reescribir el script cada vez. La lección 6 confirma esta intuición con precisión completa.


Resumen y siguiente paso

En esta lección viste el mapa completo del Módulo 6: por qué el rollback de infraestructura es, estructuralmente, distinto del rollback de una aplicación; las ocho lecciones que construyen el patrón completo, desde git revert hasta un guardrail automatizado real; y la honestidad de ejecución específica de este módulo, incluida la técnica que la lección 7 necesita para poder probar un guardrail de destrucción sin un apply real completado.

Antes de avanzar deberías poder: explicar por qué Terraform no tiene un comando de "deshacer"; nombrar las tres piezas nuevas de este módulo (rollback vía git revert, branch protection, el guardrail); y anticipar, sin sorpresas, qué vas a poder ejecutar de verdad y qué queda representativo.

La lección 2 empieza por el principio: el mecanismo exacto de un rollback de infraestructura, comparado punto por punto con el rollback de una aplicación que quizás ya conozcas de otro contexto.

Recursos

  1. GitHub Docs — GitHub Actions — documentación oficial, base de todo este módulo.
  2. HashiCorp Developer — Automate Terraform with GitHub Actions — el patrón completo, ya citado desde el Módulo 3, que este módulo endurece con controles adicionales.
  3. Módulo 3 de esta guía (the-iac-pipeline-fmt-validate-plan) — el ci.yml que este módulo reutiliza sin cambios estructurales.
  4. Módulo 5 de esta guía (apply-on-merge-the-cd-half) — el apply.yml que este módulo reutiliza, y el hallazgo de state vacío que la lección 7 resuelve.