Terraform para laboratorios reproducibles
Artículo 5 de 7
71%
completado
Testing IaC: del HCL válido al plan que una política puede aprobar
Capas de prueba para Terraform/OpenTofu: formato, validación, tests, planes guardados, políticas y evidencia sin crear infraestructura por accidente.
Prerequisitos recomendados
Si alguno de estos conceptos todavía está medio nebuloso, empieza por aquí. Te ahorra tropiezos y un par de cejas fruncidas.
terraform validate responde si la configuración es sintáctica e internamente consistente. No responde si una base de datos quedará pública, si un cambio reemplaza un recurso crítico o si el módulo conserva la propiedad que prometía. La prueba útil aparece cuando cada capa responde una pregunta distinta.
Terraform carga archivos de prueba y ejecuta secuencias de plan o apply con assertions; la propia documentación advierte que un test puede crear infraestructura real y generar costo. [1]
La pirámide que evita probar todo con apply
Evidencia incremental
Cinco filtros antes del cambio real
La regla práctica es sencilla: no subas de capa para probar algo que la capa anterior puede rechazar. Una variable sin validación no necesita una VPC real. Un nombre inválido no necesita credenciales. Una política de “sin ingress global” puede inspeccionar el plan antes de que exista una regla de firewall.
plan es una propuesta, no una promesa eterna
OpenTofu crea un plan comparando configuración, state previo y objetos remotos; el comando no ejecuta los cambios y permite revisar la propuesta antes de aplicar. [2]
Tres objetos suelen confundirse:
- plan especulativo: sirve para revisar intención; puede quedar obsoleto;
- plan guardado: congela acciones concretas para aplicar exactamente ese artefacto;
- salida textual: ayuda a humanos, pero no es una interfaz estable para políticas.
Para automatización, conserva el archivo de plan solo dentro de un entorno protegido: puede incluir valores sensibles derivados de configuración y state. Extrae JSON para inspección mecánica, pero trata ambos artefactos como datos confidenciales.
tofu plan -out=tfplan -detailed-exitcode
tofu show -json tfplan > tfplan.jsonCon -detailed-exitcode, diferencia “sin cambios”, “hay cambios” y “error”. Mezclarlos produce pipelines que celebran un fallo o marcan drift como error genérico sin evidencia.
Qué probar en un módulo
Una prueba eficaz no copia toda la salida. Formula una propiedad:
run "plan_rejects_public_ingress" {
command = plan
variables {
allowed_cidrs = ["0.0.0.0/0"]
}
expect_failures = [var.allowed_cidrs]
}Luego cubre al menos:
- camino feliz mínimo;
- input fuera del dominio;
- output que otro módulo consume;
- recurso que nunca debe reemplazarse por un cambio compatible;
- teardown o mock cuando el test hace apply.
No asserts el ID aleatorio de un recurso; asserts la relación que debe sobrevivir a una implementación distinta.
Policy as code: contrato externo al módulo
Una policy revisa decisiones que el módulo no debería autoadjudicarse: regiones permitidas, cifrado, tags, gasto máximo, tipos de instancia o reemplazo de datos persistentes. Debe consumir una representación estructurada del plan y producir:
- regla evaluada;
- recurso afectado;
- evidencia observada;
- severidad;
- excepción, responsable y caducidad cuando exista.
Ubicar la regla en la capa correcta
La decisión no consiste en acumular checks: consiste en colocar cada restricción en la capa más temprana que pueda demostrarla sin crear infraestructura innecesaria. Se descarta un apply de prueba cuando una validación, assertion o policy sobre el plan ya puede rechazar el riesgo con mejor diagnóstico.
Fallo realista: aprobar el texto equivocado
Un pipeline publica un plan en el pull request. Después se mergea otra rama, cambia el state remoto y el job aplica un plan nuevo sin mostrarlo. Técnicamente hubo review; operacionalmente se revisó otro objeto. La corrección es diferenciar plan especulativo de PR y plan concreto de apply, y exigir que la aprobación final se asocie al hash del artefacto que realmente se ejecuta.
Una estrategia por capas, no una batería indiscriminada
Las pruebas más baratas deben responder primero. Formato y validación encuentran errores del lenguaje; tests de módulo comprueban invariantes de la interfaz; una policy evalúa el plan dentro de reglas organizacionales; la integración crea recursos y verifica comportamiento. Ejecutar siempre la capa más cara vuelve lento el feedback y no garantiza que el diagnóstico sea mejor.
Diseña también pruebas negativas. Un CIDR abierto, un tag ausente o un reemplazo prohibido debe fallar por la razón esperada. Si la policy acepta el caso inseguro o rechaza todo el plan por una expresión incidental, todavía no codifica el contrato. Conserva una muestra pequeña del JSON de plan como fixture, pero revísala cuando cambia el provider: los campos desconocidos y sensibles requieren tratamiento explícito.
En integración, cada fixture necesita nombre único, presupuesto, timeout y teardown idempotente. Registra los recursos creados antes del primer assertion para poder limpiarlos incluso si el test se interrumpe. Un pipeline verde que deja infraestructura huérfana no es reproducible; solo trasladó el fallo a la factura y al siguiente laboratorio.
Asocia cada prueba con un riesgo concreto. Si una assertion no protege contrato, seguridad, costo o comportamiento observable, quizá solo duplica la implementación. Esa relación permite retirar tests obsoletos y explica por qué un fallo bloquea el cambio.
Los resultados deben identificar fixture, versión de provider, modo plan/apply y recursos creados. Con ese contexto, una falla intermitente puede investigarse sin repetir a ciegas todo el pipeline.
Publica el reporte como artefacto con retención suficiente para comparar regresiones entre cambios consecutivos.
Contrato de lab
El lab debe correr sin credenciales reales mediante mocks o providers locales y producir:
fmtyvalidatelimpios;- un test positivo y uno negativo;
tfplan.jsoninspeccionado por una policy simple;- rechazo de
0.0.0.0/0y de un recurso fuera de allowlist; - evidencia de que ningún test dejó recursos;
- hash del plan y reporte JUnit o JSON.
Conexiones dentro de la serie
- Antes: Providers, restricciones y lockfile.
- Después: CI/CD y credenciales efímeras.
- Lab relacionado: Terraform lab network, cuya allowlist es una policy mínima sobre intención.
Bibliografía académica
- [1]
HashiCorp, "terraform test command reference."
Tests, assertions, plan/apply y cleanup.
- [2]
OpenTofu, "Command: plan."
Plan como propuesta revisable antes de aplicar.
Rutas vecinas
Si este tema te abrió hambre, sigue por aquí.
Series y labs que comparten área o dependen de esta ruta. Pocas opciones, para no convertir la curiosidad en menú infinito.
Labs para convertir lectura en práctica
Sigue leyendo
Ideas conectadas con este artículo
Una ruta corta para continuar sin abrir veinte pestañas y perder el hilo.
Módulos Terraform: composición, contratos y límites que sí explican
Diseño de módulos Terraform/OpenTofu como contratos: inputs, outputs, providers, invariantes, compatibilidad y composición sin cajas negras.
Terraform y OpenTofu para laboratorios reproducibles
Base práctica para Terraform y OpenTofu en labs cloud: providers, state, plan, módulos, costos y teardown seguro.
Terraform state: backends, locking y recuperación sin superstición
Cómo razonar sobre state, backends y locking en Terraform/OpenTofu: invariantes, múltiples escritores, fallos parciales y recuperación verificable.
Terraform en CI/CD: planes revisables y credenciales que caducan
Arquitectura segura de CI/CD para Terraform/OpenTofu: plan de PR, apply protegido, OIDC, credenciales efímeras, concurrencia y artefactos sensibles.