← IT Knowledge Base Home

Terraform — Fundamentos, HCL, Providers, Recursos, Estado, Módulos, Testing, Seguridad y Operación

Pregunta de origen (entrevista / cuestionario): ¿Cuál es su experiencia con Terraform (infraestructura como código, aprovisionamiento multi-cloud, gestión de estado y módulos) en diseño, automatización, seguridad y operación?

Esta página cubre 10 bloques del temario: fundamentos e IaC (Q1–Q8), instalación y CLI (Q9–Q16), HCL y expresiones (Q17–Q25), recursos, providers y dependencias (Q26–Q34), estado y backend (Q35–Q43), módulos y organización (Q44–Q52), variables, outputs y entornos (Q53–Q60), provisioners y configuración (Q61–Q68), testing y CI/CD (Q69–Q77) y seguridad, gobierno y operación (Q78–Q85), más una plantilla de respuesta para entrevista y referencias.

Contenido

1. Fundamentos, IaC y ecosistema

Q1. ¿Qué es Terraform y para qué se usa?

Terraform es una herramienta de infraestructura como código (IaC) desarrollada por HashiCorp que permite definir, aprovisionar y gestionar recursos de nube y servicios mediante archivos de configuración declarativos en HCL. Con un mismo flujo (init, plan, apply) se administran múltiples proveedores (AWS, Azure, GCP, Kubernetes, Databricks, Cloudflare, GitHub, etc.), se versiona la infraestructura en Git y se mantiene un estado que reconcilia lo deseado con lo existente. Se usa para crear redes, cómputo, bases de datos, permisos IAM, clusters, DNS y servicios SaaS; también para entornos efímeros, recuperación ante desastres y promoción entre dev/staging/prod. Su valor es la reproducibilidad, la revisión por pares y la reducción de cambios manuales.

Q2. ¿Qué significa que Terraform sea declarativo y cuáles son sus ventajas frente a la infraestructura manual?

En un enfoque declarativo se describe el estado final deseado, no los pasos para llegar; Terraform calcula el diff contra el estado real y ejecuta las acciones necesarias. Ventajas: reproducibilidad (el mismo código crea el mismo entorno), revisión de cambios con plan antes de aplicar, versionado y auditoría en Git, reducción de errores manuales, capacidad de recrear entornos y destruirlos de forma controlada. También facilita la colaboración y el cumplimiento, porque la infraestructura queda documentada y revisable. Límites: no elimina la necesidad de entender el proveedor, el estado puede desincronizarse si hay cambios manuales, y no es ideal para configurar software dentro de servidores (para eso se usan cloud-init, Ansible o imágenes). La clave es tratar la infraestructura como código de producción: revisada, probada y con dueño.

Q3. ¿Cuál es la historia y evolución de Terraform?

HashiCorp lanzó Terraform en 2014 como una herramienta multi-proveedor basada en HCL. El salto importante fue Terraform 0.12 (2019), que introdujo HCL2 con expresiones y tipos más potentes; Terraform 1.0 llegó en 2021 con estabilidad de lenguaje y flujo. En 2023 HashiCorp cambió la licencia de MPL 2.0 a BSL 1.1, lo que impulsó el fork OpenTofu bajo la Linux Foundation; en 2025 IBM adquirió HashiCorp. Terraform Cloud pasó a llamarse HCP Terraform, con ejecución remota, políticas y gestión de workspaces. En versiones 1.x recientes se han añadido terraform test, bloques import, moved y removed, valores efímeros, write-only arguments y mejoras de flujo. Conocer esta evolución ayuda a elegir versión, licencia y estrategia de migración.

Q4. ¿Terraform u OpenTofu? ¿Qué implica el cambio de licencia?

Terraform usa la licencia BSL 1.1 desde 2023, que permite uso interno y comercial pero restringe ofrecer un producto competidor basado en Terraform. OpenTofu es un fork open source (MPL 2.0) bajo la Linux Foundation, compatible con HCL, providers y estado, con su propio roadmap (por ejemplo, cifrado de estado y algunas mejoras). La elección depende de política de licencias, soporte, ecosistema y funciones requeridas: Terraform tiene el ecosistema más amplio y HCP Terraform; OpenTofu ofrece una alternativa comunitaria y evita la BSL. Muchos providers funcionan en ambos. En una entrevista conviene explicar la compatibilidad técnica, los riesgos de lock-in, la estrategia de migración y que la decisión suele ser organizacional, no solo técnica. Probar el flujo con una copia del estado antes de cambiar de binario.

Q5. ¿Cómo se compara Terraform con CloudFormation, Bicep, Pulumi y Ansible?

La elección depende de la nube, el equipo y el alcance; es común combinar Terraform para infraestructura con Ansible/cloud-init para configuración.

Q6. ¿Qué casos de uso y antipatrones son típicos en Terraform?

Casos de uso: aprovisionar redes y cómputo, crear clusters de Kubernetes y servicios gestionados, configurar IAM y políticas, entornos efímeros de pruebas, recuperación ante desastres, gestión de DNS/CDN y recursos SaaS. Antipatrones: usar Terraform para instalar software dentro de máquinas; editar manualmente recursos gestionados; un único estado gigante con todo; force push del estado; secretos en texto plano; providers sin fijar versión; módulos que configuran providers internamente; provisioners para tareas repetibles; count en listas que cambian de orden; target como forma habitual de trabajar; y aplicar sin revisar el plan. También es un antipatrón copiar/pegar bloques en lugar de modularizar, o crear una abstracción propia tan compleja que nadie la entiende. La regla es simple: infraestructura declarativa, estados pequeños, revisión y automatización.

Q7. ¿Qué es el ecosistema de providers y el registry?

Un provider es un plugin que traduce HCL a llamadas de API de un servicio: AWS, AzureRM, Google, Kubernetes, Helm, Databricks, Cloudflare, GitHub, Snowflake, etc. El Terraform Registry publica providers oficiales, verificados y de la comunidad, con documentación de recursos y data sources; también aloja módulos reutilizables. Cada provider tiene su ciclo de versiones y se declara en el bloque required_providers con restricciones de versión. Además de los oficiales, existen providers partner y community; en entornos regulados se pueden usar mirrors internos. La calidad y el mantenimiento varían: revisar releases, issues, licencia y soporte antes de adoptar uno. Los providers se descargan en terraform init y quedan registrados en el lock file; fijar versiones y actualizar de forma controlada es parte de la gestión de dependencias.

Q8. ¿Cuál es el flujo de trabajo básico de Terraform?

El ciclo es write → init → plan → apply. Se escribe la configuración (recursos, variables, providers), se ejecuta terraform init para descargar providers/módulos e inicializar el backend, terraform plan para ver el diff entre el estado deseado, el estado guardado y la realidad, y terraform apply para ejecutar los cambios (idealmente sobre un plan guardado y revisado). terraform destroy elimina lo gestionado. En equipos se añaden fmt, validate, análisis estático, tests, revisión por pares, bloqueo de estado y promoción entre entornos. El estado remoto es imprescindible para colaborar; sin él, dos personas pueden pisarse. El flujo debe ser reproducible en CI/CD y no depender de la máquina local de una persona.

2. Instalación, configuración y CLI

Q9. ¿Cómo se instala Terraform y cómo se fija la versión?

Se descarga el binario oficial desde releases.hashicorp.com o se usa un gestor de paquetes (apt, brew, choco, winget) o una herramienta de versiones como tfenv/tofuenv. El binario es autocontenido. Para equipos y CI conviene fijar la versión en un archivo (.terraform-version, required_version en HCL) y verificar checksums; así se evita que una actualización cambie el comportamiento entre locales y pipelines. La restricción required_version debe ser compatible con el backend y los providers. En contenedores se usa una imagen oficial o una imagen interna con la versión pinneada; en Windows se puede usar WSL. Mantener una versión única por repositorio y actualizarla de forma planificada, leyendo el changelog y probando en un entorno no productivo, es parte de la higiene de IaC.

Q10. ¿Cuáles son los comandos de CLI más importantes?

Conocer las opciones de plan (-out, -target, -replace, -refresh-only) y los códigos de salida es clave para CI/CD.

Q11. ¿Qué hace exactamente terraform init y qué es el lock file?

init prepara el directorio de trabajo: lee la configuración, instala los providers y módulos necesarios, configura el backend remoto y escribe metadatos en .terraform/. Si existe .terraform.lock.hcl, respeta las versiones y checksums registrados; con -upgrade busca versiones nuevas dentro de las restricciones. El lock file fija versiones exactas y hashes de providers para que todos (y el CI) usen lo mismo; debe versionarse en Git. En equipos multi-plataforma puede contener hashes de varias arquitecturas; conviene generarlo/actualizarlo de forma controlada. El directorio .terraform/ (binarios y caché) no se versiona. Si se cambia el backend, init -migrate-state ayuda a mover el estado. Revisar el lock file en los PR es una defensa contra cambios silenciosos de providers.

Q12. ¿Cómo se usan plan, apply y destroy de forma segura?

El flujo seguro: terraform plan -out=tfplan en un entorno con credenciales de solo lectura (idealmente), revisar el diff y aprobar; luego terraform apply tfplan con credenciales de escritura y sin regenerar el plan. -target sirve para emergencias, no como flujo habitual (puede dejar el estado parcial). -replace=address fuerza la recreación de un recurso concreto; -refresh-only solo reconcilia el estado con la realidad. Antes de destroy, revisar qué se eliminará (protecciones prevent_destroy, backups, dependencias) y usar entornos separados. En CI conviene guardar el plan como artefacto, aplicar exactamente ese plan y registrar quién aprobó. Nunca aplicar a producción desde una laptop sin revisión; y evitar auto-approve en prod salvo en flujos muy controlados.

Q13. ¿Para qué sirven fmt, validate, graph, show y output?

fmt aplica el formato canónico de HCL (con -check en CI); validate comprueba sintaxis, tipos y referencias sin contactar APIs (requiere init); graph genera el grafo de dependencias (DOT) para entender relaciones y ciclos; show muestra el estado o un plan guardado en formato legible/JSON; output consulta los outputs (con -json para automatización); providers lista providers y sus versiones; version muestra la versión de Terraform y providers. En CI, fmt -check, validate y show -json son útiles para lint, validación y análisis de políticas. graph no sustituye entender el plan, pero ayuda a diagnosticar dependencias y ciclos. Estas comprobaciones son baratas y atrapan errores antes de tocar infraestructura.

Q14. ¿Qué es HCP Terraform (antes Terraform Cloud) y cuándo conviene?

HCP Terraform es la plataforma gestionada de HashiCorp: ejecución remota de planes/applies, estado remoto, gestión de workspaces y variables, control de acceso, políticas (Sentinel/OPA), registry privado de módulos/providers, detección de drift y auditoría. Terraform Enterprise es la versión autogestionada para organizaciones con requisitos de red o cumplimiento. Conviene cuando se quiere delegar la ejecución, centralizar credenciales, aplicar políticas y evitar que cada equipo monte su propio backend. Alternativas: backend S3/GCS/Azure con CI propio, o plataformas como Spacelift, Env0, Scalr o Atlantis. La decisión depende de coste, control, requisitos de red y si se prefiere una solución gestionada o autogestionada.

Q15. ¿Qué variables de entorno y configuración global usa Terraform?

TF_VAR_nombre define variables de entrada; TF_LOG (TRACE/DEBUG/INFO/WARN/ERROR) y TF_LOG_PATH habilitan logs; TF_WORKSPACE selecciona workspace; TF_CLI_ARGS y TF_CLI_ARGS_plan/apply añaden argumentos; TF_PLUGIN_CACHE_DIR configura caché de providers; TF_IN_AUTOMATION ajusta la salida para CI; TF_INPUT=0 evita prompts. Las credenciales se pasan según el provider (AWS_ACCESS_KEY_ID, AWS_PROFILE, ARM_CLIENT_ID, GOOGLE_APPLICATION_CREDENTIALS) o, mejor, mediante roles/OIDC de corta duración. Cuidado con imprimir logs en CI porque pueden contener datos sensibles. Para depurar, usar TF_LOG en local y con precaución; en automatización, preferir variables seguras del pipeline y no dejarlas en el historial.

Q16. ¿Qué es el plugin cache y cómo acelera CI?

Terraform descarga providers en cada init si no están en caché; el plugin cache (TF_PLUGIN_CACHE_DIR) reutiliza binarios entre módulos y ejecuciones, reduciendo tiempo y llamadas al registry. En CI se puede cachear ese directorio entre jobs, siempre que se respete el lock file y los checksums. Alternativas: provider mirror interno o network mirror para entornos sin salida a Internet, y contenedores preconstruidos con providers. Precauciones: no compartir caché entre versiones incompatibles, limpiar si hay corrupción, y mantener el lock file en Git. El cache no sustituye fijar versiones; solo acelera. En un pipeline típico, cachear .terraform.d/plugin-cache y .terraform/ puede ahorrar minutos, pero no cachear el estado ni credenciales.

3. HCL, sintaxis y expresiones

Q17. ¿Cómo se estructura un proyecto Terraform y qué es HCL?

HCL (HashiCorp Configuration Language) es un lenguaje declarativo con bloques, argumentos y expresiones. Un proyecto típico separa archivos por responsabilidad: versions.tf (versiones y providers), providers.tf, variables.tf, main.tf, outputs.tf, locals.tf, terraform.tfvars y módulos en carpetas. Los bloques tienen tipo y etiquetas (resource "aws_s3_bucket" "this" { ... }); dentro van argumentos y bloques anidados. Se permiten comentarios con #, // y /* */. Terraform carga todos los .tf del directorio como un solo módulo raíz, así que el orden de archivos no importa. La convención y la separación lógica mejoran la legibilidad y las revisiones; evitar archivos gigantes y nombres ambiguos.

Q18. ¿Qué tipos de datos soporta HCL?

Tipos primitivos: string, number, bool. Colecciones: list (ordenada, admite duplicados), set (sin duplicados), map (claves string) y object (estructura con atributos con nombre y tipos). Estructurales: tuple (secuencia con tipos por posición) y object. También existe null para representar ausencia de valor y any para tipos dinámicos en variables. Los tipos se declaran en variables y outputs; HCL convierte valores cuando es seguro. Para colecciones heterogéneas, usar object o map(object({...})); para iterar, for_each sobre map/set y count sobre listas. Elegir bien el tipo evita errores de índice y cambios inesperados cuando se reordena una lista; los sets y maps son más estables para identidades.

Q19. ¿Cuáles son las expresiones más usadas (interpolación, condicionales, for, splat, funciones)?

Usar funciones con moderación y nombres claros; la lógica compleja se vuelve difícil de revisar. try y can ayudan con valores opcionales, pero pueden ocultar errores; preferir validaciones explícitas cuando el dato es obligatorio.

Q20. ¿Cómo funcionan las referencias y las dependencias implícitas?

Al referenciar un atributo de otro recurso (aws_subnet.this.id), Terraform crea una dependencia implícita y ordena la creación/actualización/destrucción en consecuencia. Así se evita hardcodear IDs y se construye el grafo. Las dependencias explícitas con depends_on solo se necesitan cuando no hay referencia de atributo pero sí una relación de orden (por ejemplo, permisos que deben existir antes). Para módulos, las referencias a outputs crean dependencias entre módulos. Entender el grafo ayuda a diagnosticar ciclos (Cycle: ...), destrucciones en orden incorrecto y recursos que se recrean antes de tiempo. Evitar depends_on indiscriminado: oculta relaciones y puede forzar esperas innecesarias. Si hay un ciclo, normalmente hay que romperlo con un data source, un recurso intermedio o rediseñando la dependencia.

Q21. ¿Cuándo usar variables, locals y outputs?

Regla práctica: si debe cambiar entre entornos o consumidores, variable; si es un cálculo interno, local; si otro módulo o pipeline lo necesita, output. Evitar variables para todo (dificulta el uso) y outputs innecesarios (amplían la interfaz). Documentar cada variable/output en variables.tf/outputs.tf con descripción y tipo. Los locals no deben convertirse en un lenguaje de programación oculto; si la lógica es muy compleja, quizá corresponda un módulo o un dato precalculado.

Q22. ¿Qué son los bloques dynamic y cuándo usarlos?

Un bloque dynamic genera bloques anidados repetidos a partir de una colección, por ejemplo reglas de seguridad, tags o argumentos de un recurso. Sintaxis: dynamic "ingress" { for_each = var.reglas content = { from_port = ingress.value.puerto ... } }. Es útil cuando el número de bloques es variable; para estructuras fijas, escribir los bloques explícitos suele ser más legible. Limitaciones: complica el plan y los mensajes de error, y no todos los bloques anidados se comportan igual; en algunos casos conviene usar el argumento de lista/mapa directamente. Evitar dynamic anidados y nombres genéricos; documentar la estructura esperada. Si el provider ofrece un argumento equivalente en mapa/lista, preferirlo. La prioridad es que el código sea comprensible para quien revisa el PR y para quien lee el plan.

Q23. ¿Cómo se leen archivos y plantillas (file, templatefile, jsonencode, yamldecode)?

file("${path.module}/user_data.sh") lee un archivo de texto; templatefile("${path.module}/init.tpl", { ... }) renderiza una plantilla con variables; jsonencode(var.objeto) serializa a JSON (muy usado en políticas IAM); yamldecode(file(...)) lee YAML; jsondecode hace lo propio con JSON. path.module, path.root y path.cwd ayudan a construir rutas portables. Patrón típico: generar user_data o un ConfigMap de Kubernetes desde una plantilla. Consideraciones: las plantillas deben versionarse, no contener secretos y ser deterministas; los cambios en un archivo referenciado con file no siempre se detectan si no se usa filehash o un trigger. Para JSON/YAML complejos, validar el resultado y evitar concatenaciones frágiles; jsonencode es preferible a construir strings a mano.

Q24. ¿Cuáles son los meta-argumentos count, for_each, depends_on, provider y lifecycle?

for_each sobre mapas evita recreaciones cuando se añade/elimina un elemento en medio de una lista; con count, reordenar cambia los índices y puede recrear recursos. Usar lifecycle con criterio: ignore_changes puede ocultar drift y prevent_destroy debe tener un proceso de excepción.

Q25. ¿Qué patrones de bucles y transformación de colecciones conviene dominar?

Para construir mapas estables: { for s in var.subnets : s.name => s } y luego for_each = local.subnets_map. Para aplanar estructuras: flatten([for g in var.grupos : g.miembros]). Para combinar listas: setproduct, concat, merge. Para deduplicar: distinct o convertir a toset. Para valores opcionales: coalesce, try, lookup con default. Para transformar tipos: tomap, tolist, toset. Para agrupar: group_by no existe de forma nativa; se construye con for. La clave es que las claves de for_each sean estables y significativas (nombre lógico, no índice). Evitar expresiones de varias líneas sin nombres intermedios en locals; documentar la forma de los datos. Probar con terraform console ayuda a validar transformaciones antes de aplicarlas.

4. Recursos, providers y dependencias

Q26. ¿Cómo funciona el ciclo de vida de un recurso y qué significa que un cambio fuerce reemplazo?

Terraform compara la configuración deseada con el estado y el recurso real, y decide crear, actualizar en sitio o reemplazar. Algunos atributos son ForceNew: el provider no puede cambiarlos sin destruir y recrear (por ejemplo, el nombre de una instancia o la zona de una base de datos). El plan lo muestra como -/+ (replace); si el recurso tiene dependencias, el reemplazo puede propagarse. Para controlar el orden se usa create_before_destroy; para forzar un reemplazo puntual, terraform apply -replace=.... Entender qué cambios son destructivos es clave para revisar planes y evitar downtime. En recursos con nombre único o IPs, el reemplazo puede fallar si el nuevo se crea antes de destruir el viejo; ahí ayuda create_before_destroy o un nombre intermedio. Documentar los atributos inmutables en los módulos y probar en staging.

Q27. ¿Cómo se configuran los providers, alias y restricciones de versión?

En terraform { required_providers { aws = { source = "hashicorp/aws", version = "~> 5.0" } } } se declara el provider y su restricción; en un bloque provider "aws" { region = ... } se configura. Los alias permiten varias configuraciones en el mismo módulo (por ejemplo, aws.east y aws.west), y se asignan con el meta-argumento provider en recursos o módulos. Las restricciones deben ser lo bastante amplias para recibir parches pero acotadas para evitar saltos mayores; el lock file fija la versión exacta. Los providers se heredan a los módulos hijos; lo recomendable es que un módulo no configure providers internamente, sino que los reciba. En multi-cloud se declaran varios providers y se aísla cada uno; revisar compatibilidad de versiones y deprecaciones al actualizar.

Q28. ¿Cómo se autentican los providers de forma segura?

Evitar credenciales estáticas en el código o en .tfvars. Opciones: variables de entorno del proveedor, perfiles locales para desarrollo, roles IAM asumibles, y en CI/CD federación OIDC (GitHub Actions, GitLab, Azure DevOps) para obtener credenciales de corta duración sin secretos de larga vida. En AWS se usa assume_role con un rol por entorno; en Azure, federated credentials/managed identity; en GCP, Workload Identity Federation. Los service principals/roles deben tener mínimo privilegio y separación por entorno. Para HCP Terraform se usan variables sensibles y dynamic provider credentials. Rotar credenciales, auditar su uso y nunca imprimir tokens en logs. La autenticación del provider es una superficie de ataque: si el pipeline puede asumir un rol de administrador, cualquier PR malicioso es un riesgo.

Q29. ¿Cómo se resuelven las dependencias y el paralelismo?

Terraform construye un grafo dirigido acíclico (DAG) con las referencias entre recursos, módulos y data sources; los nodos sin dependencias se ejecutan en paralelo (por defecto hasta 10 operaciones, ajustable con -parallelism). Las dependencias implícitas surgen de referencias a atributos; las explícitas, de depends_on. Si hay un ciclo, el plan falla con Cycle; se rompe eliminando la referencia circular, usando un data source, un recurso intermedio o separando en módulos/estados. El paralelismo mejora la velocidad pero puede saturar APIs o provocar límites de rate; en esos casos se reduce o se aplican reintentos. Entender el grafo también ayuda a prever el orden de destrucción y a diagnosticar por qué un recurso se recrea antes de que otro exista. terraform graph visualiza el DAG.

Q30. ¿Qué diferencia hay entre data sources y resources? ¿Qué es el drift?

Un resource crea y gestiona un objeto (Terraform es dueño de su ciclo de vida). Un data source solo lee información existente (AMI más reciente, VPC, secreto, zona DNS) sin crearla ni destruirla; se evalúa durante el plan/apply y puede cambiar entre ejecuciones. El drift es la diferencia entre el estado guardado y la realidad, normalmente por cambios manuales o por otros sistemas; se detecta con plan (refresh) y se corrige aplicando de nuevo o reconciliando. Un data source que devuelve algo distinto puede provocar cambios inesperados; fijar versiones/IDs cuando la estabilidad importa. Para recursos existentes que se quieren gestionar, usar import (o bloques import); para solo consultarlos, data source. Revisar el plan tras un drift y no aplicar a ciegas; el drift recurrente indica un proceso manual que hay que corregir.

Q31. ¿Qué opciones de lifecycle existen y cuándo usarlas?

El lifecycle debe responder a una necesidad concreta y documentada; no es un sustituto de un buen diseño de dependencias.

Q32. ¿Cómo se refactoriza sin recrear recursos (moved, import, removed)?

Renombrar un recurso o moverlo a un módulo cambia su dirección en el estado y, sin ayuda, Terraform lo destruiría y recrearía. Los bloques moved indican el cambio de dirección (from = aws_instance.old, to = module.app.aws_instance.this) y Terraform actualiza el estado sin tocar la infraestructura. Los bloques import declaran recursos existentes con su ID para incorporarlos al estado de forma reproducible; terraform import manual sigue existiendo, pero el bloque es preferible en código. Los bloques removed permiten dejar de gestionar un recurso sin destruirlo (con lifecycle { destroy = false }) o destruirlo de forma controlada. Buenas prácticas: hacer refactors en PR pequeños, revisar el plan (debe ser no changes o solo movimientos), y probar en un entorno con copia del estado. Estas herramientas son clave para evolucionar módulos sin downtime.

Q33. ¿Qué son los provisioners y por qué se considera un antipatrón?

Los provisioners ejecutan acciones después de crear/destruir un recurso: file copia archivos, remote-exec ejecuta comandos por SSH/WinRM, local-exec ejecuta en la máquina donde corre Terraform. Se consideran último recurso porque: no son declarativos ni idempotentes por sí mismos, dependen de conectividad y credenciales, complican el estado y los reintentos, y si fallan dejan recursos a medias. Para configuración de software se prefieren imágenes preconstruidas (Packer), cloud-init/user_data, Ansible o un sistema de configuración/GitOps. Los provisioners pueden ser aceptables para tareas puntuales de bootstrap (obtener una clave, registrar un nodo) o cuando el proveedor no ofrece otra vía, siempre con when = destroy/on_failure explícitos. La infraestructura debe ser inmutable y reproducible; los provisioners tienden a hacerla frágil.

Q34. ¿Para qué sirven null_resource, terraform_data, time y random?

Son útiles para casos concretos, pero abusar de ellos indica que la lógica debería estar en otro nivel. Evitar null_resource para orquestar pasos que deberían ser recursos; y no usar random para secretos que deban rotarse sin recrear.

5. Estado, backend y colaboración

Q35. ¿Qué es el estado de Terraform y por qué es tan importante?

El estado (terraform.tfstate) es el registro que mapea los recursos declarados en HCL con los objetos reales del proveedor, guardando atributos e identificadores. Permite calcular el diff, saber qué existe, planificar destrucciones y detectar drift. Sin estado, Terraform no puede gestionar recursos existentes de forma segura. Es un archivo sensible: contiene IDs, atributos y a veces secretos; debe almacenarse en un backend remoto con cifrado, bloqueo y control de acceso, nunca en Git. Se puede inspeccionar con state list/show y manipular con cuidado. El estado no es la verdad absoluta: la realidad manda, y refresh lo reconcilia. Perderlo o corromperlo puede implicar reimportar recursos o recrearlos; por eso se versiona/backupea y se protege como un secreto de producción.

Q36. ¿Qué backends existen y cómo se configura el bloqueo?

Backends: local (solo para pruebas), S3 (con bloqueo nativo o DynamoDB), azurerm, gcs, remote (HCP Terraform), consul, kubernetes, entre otros. Se configuran en terraform { backend "s3" { bucket = ... key = ... region = ... } } o con -backend-config para parametrizar. El state locking evita que dos ejecuciones modifiquen el estado a la vez: en S3 se usaba DynamoDB y hoy existe bloqueo nativo con use_lockfile; en Azure/GCS hay mecanismos propios. Además, el bucket debe tener versionado, cifrado, acceso restringido y logs. Nunca usar local en producción ni compartir credenciales de estado entre equipos. Si el bloqueo queda atascado, investigar el proceso antes de forzar -lock=false; forzar puede corromper el estado.

Q37. ¿Cómo se colabora con estado remoto y qué papel tienen los workspaces?

Con backend remoto, cada ejecución lee/escribe el mismo estado y el bloqueo serializa los cambios; los equipos comparten una fuente de verdad. Para separar entornos se puede usar una key/prefix distinta por entorno o workspaces (estado separado dentro del mismo backend). Los workspaces son cómodos para entornos efímeros o pruebas, pero no son un aislamiento fuerte: comparten configuración y credenciales, y un error puede afectar a varios. Para producción suele ser mejor un directorio/estado por entorno con backend y credenciales separadas. HCP Terraform gestiona workspaces con variables, permisos y ejecución remota. Convención: nombres claros (app-network-prod), permisos por workspace y política de quién puede aplicar. Documentar qué estado contiene qué y cómo se recupera.

Q38. ¿Qué es refresh, drift y cómo se reconcilia?

Antes del plan, Terraform consulta las APIs para actualizar el estado (refresh). Si alguien cambió un recurso por fuera, el plan muestra el drift y propone volver a la configuración deseada. terraform plan -refresh-only solo actualiza el estado sin proponer cambios de infraestructura; terraform apply -refresh-only acepta esa reconciliación. Si el cambio manual es deseado, se actualiza el código para reflejarlo; si no, se aplica para revertirlo. El drift recurrente indica procesos manuales, consolas o automatizaciones paralelas que hay que eliminar. En CI se programan plans periódicos para detectarlo. Cuidado con ignore_changes: puede ocultar drift legítimo. La meta es que la realidad converja con el código y que cualquier cambio pase por el repositorio.

Q39. ¿Qué datos sensibles contiene el estado y cómo se protegen?

El estado puede contener contraseñas, claves, tokens, certificados y atributos sensibles de recursos, incluso si se marcan como sensitive (la marca evita mostrarlos en la CLI, no los elimina del estado). Protección: backend remoto cifrado (SSE-KMS/CMK), acceso mínimo, bloqueo, versionado y auditoría; nunca en Git ni en artefactos de CI; cuidado con terraform show -json y logs. Para secretos, preferir generarlos y almacenarlos en un gestor (Vault, Secrets Manager, SSM) y referenciarlos con data sources efímeros, en lugar de dejarlos en el estado. Las versiones recientes incorporan ephemeral values y write-only arguments para reducir la exposición. Además, restringir quién puede leer el bucket de estado y rotar credenciales. El estado debe tratarse como un secreto de nivel producción.

Q40. ¿Cómo se manipula el estado de forma segura (mv, rm, pull, push, taint)?

Antes de cualquier manipulación: backup, bloqueo, revisar el plan y hacerlo en una ventana controlada. Nunca editar el JSON del estado a mano salvo emergencia y con respaldo. Documentar el cambio y verificar con plan que no hay destrucciones inesperadas. Estas operaciones son quirúrgicas, no parte del flujo diario.

Q41. ¿Qué hacer ante problemas de bloqueo o migración de backend?

Si el estado está bloqueado, primero identificar quién lo tiene (CI, otra persona, un proceso colgado); esperar o cancelar la ejecución. Forzar -lock=false solo si se está seguro de que nadie está escribiendo; puede provocar corrupción. En S3/DynamoDB, revisar el lock item; en HCP Terraform, el lock del workspace. Para migrar de backend: configurar el nuevo, ejecutar init -migrate-state, verificar que el estado se copió y que el plan es coherente, y luego retirar el antiguo con cuidado (mantener backup). Si se cambia la key/prefix, el nuevo backend puede parecer vacío; revisar la configuración. Documentar el procedimiento y probarlo en un entorno no productivo. La migración de estado es una operación de riesgo: backup y ventana de mantenimiento.

Q42. ¿Cómo se decide el tamaño y la separación de los estados?

El estado define el blast radius: un estado gigante tarda en planificar, concentra riesgos y bloquea a muchos equipos; demasiados estados pequeños complican las dependencias y la visibilidad. Un patrón común: separar por entorno (dev/staging/prod) y por dominio o capa (red, datos, aplicación, identidad), con dependencias explícitas mediante terraform_remote_state o data sources. Mantener juntos los recursos que cambian juntos y se despliegan juntos; separar lo que tiene ciclos de vida o equipos distintos. Evitar que un cambio de red requiera tocar la aplicación. Medir el tiempo de plan y la frecuencia de cambios. La decisión debe documentarse; no hay una única respuesta, pero sí principios: menor riesgo, claridad de propiedad y autonomía de equipos.

Q43. ¿Cómo se respalda y recupera el estado ante corrupción o desastre?

Habilitar versionado en el bucket de estado (S3/GCS) y, si aplica, replicación entre regiones; conservar versiones y registrar cambios. En HCP Terraform, usar las capacidades de historial/backup de la plataforma. Antes de operaciones riesgosas, hacer state pull y guardar una copia fuera del pipeline. Si el estado se corrompe: restaurar la última versión buena, verificar con plan, y si es necesario reimportar recursos. Mantener la configuración en Git permite reconstruir; el estado se puede regenerar importando, aunque es costoso. Documentar RPO/RTO del estado y probar la restauración. Evitar ediciones manuales y accesos amplios. El estado es un activo crítico: su pérdida puede dejar la infraestructura «huérfana» y obligar a un trabajo manual peligroso.

6. Módulos, workspaces y organización

Q44. ¿Qué es un módulo y qué tipos existen?

Un módulo es un conjunto reutilizable de archivos Terraform con una interfaz de variables y outputs. El módulo raíz es el directorio donde se ejecuta Terraform; los módulos hijos se invocan con module "nombre" { source = ... }. Fuentes: ruta local, registry público/privado, repositorio Git (con ref/tag) o bucket. Los módulos encapsulan lógica común (red, clúster, base de datos), reducen duplicación y estandarizan. No todos los directorios son módulos reutilizables: el código de un entorno concreto es el root. Un buen módulo tiene una responsabilidad clara, documentación, ejemplos, versionado y tests. Evitar módulos que lo hacen todo o que ocultan decisiones críticas; la abstracción debe simplificar, no sorprender. Los módulos también son una unidad de gobierno: se revisan, versionan y consumen como dependencias.

Q45. ¿Cómo se estructura y documenta un módulo?

Estructura habitual: main.tf (recursos), variables.tf (entradas con tipo, descripción, default y validaciones), outputs.tf (salidas), versions.tf (required_version y providers), README.md (uso, ejemplos, requisitos, inputs/outputs) y una carpeta examples/. Opcional: tests/ con terraform test o Terratest, y CHANGELOG.md. La documentación se puede generar con terraform-docs para mantener tablas de variables/outputs actualizadas. Convenciones: nombres en snake_case, descripciones claras, evitar variables ambiguas, y outputs solo los necesarios. El módulo no debe configurar providers ni backend; debe recibirlos. Versionar con tags semver y publicar en un registry privado si se reutiliza. Un módulo sin README ni ejemplos es difícil de adoptar y tiende a duplicarse; la documentación es parte del producto.

Q46. ¿Qué buenas prácticas de interfaz y encapsulamiento aplicar en módulos?

Definir una interfaz mínima y estable: pocas variables obligatorias, defaults sensatos, validaciones y outputs útiles. Evitar exponer detalles internos (nombres de recursos, estructuras complejas) que impidan refactorizar. No configurar providers dentro del módulo; recibir providers desde el root para permitir alias y evitar acoplamiento. Evitar depends_on sobre módulos si se puede expresar con datos; y no usar count dentro de un módulo para «apagar» recursos de forma implícita. Documentar supuestos y limitaciones. En lo posible, un módulo debe poder probarse de forma aislada con ejemplos. Si un módulo requiere 40 variables, quizá está mal dividido. La señal de un buen diseño es que el consumidor entiende qué pasar y qué obtiene sin leer toda la implementación.

Q47. ¿Cómo se versionan y distribuyen los módulos?

Los módulos se versionan con tags semver (por ejemplo, v1.2.0) y se referencian con source + version en el registry, o con ref en Git. Fijar versiones exactas o rangos acotados; actualizar deliberadamente y leer el changelog. Un registry privado (HCP Terraform, GitLab, Artifactory) centraliza módulos internos, controla acceso y publica documentación. Alternativas: repositorios Git con tags y submódulos, o buckets. Buenas prácticas: mantener compatibilidad semver, deprecar variables con aviso, pruebas por versión y ejemplos actualizados. Evitar referencias a main o ramas móviles en producción: un cambio no revisado puede romper todos los consumidores. El versionado de módulos es gestión de dependencias: merece el mismo rigor que las librerías de código.

Q48. ¿Workspaces, directorios separados o stacks? ¿Cómo separar entornos?

Para producción se suele preferir separación por estado/credenciales; para pruebas temporales, workspaces. La decisión debe considerar blast radius, quién aplica, requisitos de auditoría y coste operativo. No mezclar entornos en el mismo estado.

Q49. ¿Qué es Terragrunt y cuándo conviene?

Terragrunt es un wrapper de Terraform que reduce duplicación: genera backends y providers, organiza módulos por entorno, permite dependencias entre módulos (dependency) y ejecuta comandos en varios directorios (run-all). Es útil en repositorios con muchos entornos y configuraciones repetidas. Costos: una capa adicional de abstracción y herramienta que el equipo debe conocer; run-all puede ampliar el blast radius; y no sustituye un buen diseño de estados. Alternativas: módulos + roots por entorno, HCP Terraform Stacks, o generación con plantillas. Conviene cuando la duplicación es dolorosa y el equipo ya domina Terraform; no como primer paso para un proyecto pequeño. Documentar cómo se ejecuta y cómo se promociona entre entornos.

Q50. ¿Monorepo o polyrepo? ¿Cómo se asignan owners y se controla el cambio?

Monorepo: toda la IaC en un repositorio, con ventajas de visibilidad, cambios atómicos y reutilización; riesgos de acoplamiento, pipelines lentos y permisos amplios. Polyrepo: un repositorio por dominio/equipo, con autonomía y permisos más finos; riesgos de dependencias descoordinadas y duplicación. En ambos casos: código de módulos separado del código de entornos, revisión por pares, CODEOWNERS, ramas protegidas y pipelines que validen. Asignar owner por módulo/estado, documentar contratos y usar versionado. El cambio de infraestructura debe tener el mismo rigor que el de aplicación: PR, plan, aprobación y auditoría. La elección depende de tamaño, cultura y herramientas; lo importante es que la propiedad y el flujo de aprobación sean claros.

Q51. ¿Qué convenciones de nombres, tags y estructura conviene establecer?

Nombres: {empresa}-{entorno}-{dominio}-{recurso}, en minúsculas y guiones, respetando límites del proveedor; evitar nombres que codifiquen información volátil. Tags obligatorios: entorno, owner/equipo, aplicación, cost_center, data_classification, managed_by=terraform. Los tags permiten cost allocation, inventario y automatización; definirlos como locals y aplicarlos de forma consistente (merge de tags comunes + específicos). Estructura: environments/, modules/, shared/, docs/; archivos por responsabilidad. Documentar convenciones en un README/ADRs y validarlas con políticas (por ejemplo, que todo recurso etiquetable tenga tags). Unificar nombres de variables/outputs y evitar abreviaturas crípticas. La consistencia reduce errores y facilita la búsqueda; la convención debe ser útil, no burocrática.

Q52. ¿Qué patrones de composición y antipatrones de módulos existen?

Patrones: módulos por dominio (red, cómputo, datos, observabilidad), composición en el root, módulos «opinionated» para estándares corporativos, y módulos de datos que solo consultan. Antipatrones: mega-módulo que crea toda la plataforma; módulos que configuran providers/backends; exponer todos los atributos; usar count para habilitar/deshabilitar secciones; condicionales anidados que generan planes ilegibles; depends_on excesivo; versiones flotantes; duplicar recursos en lugar de componer; y módulos sin tests. También es un antipatrón envolver un único recurso sin aportar valor. La composición debe equilibrar reutilización y claridad: si el consumidor no entiende el plan, la abstracción falló. Preferir interfaces pequeñas, ejemplos y versionado.

7. Variables, outputs, validación y entornos

Q53. ¿Cómo se declaran variables y qué opciones tienen?

Sintaxis: variable "region" { type = string description = "..." default = "us-east-1" sensitive = false nullable = false validation { condition = ... error_message = "..." } }. Opciones clave: type (string, number, bool, list, map, object, set, tuple, any), default (si falta, la variable es obligatoria), description, sensitive (oculta en salida, no en el estado), nullable (si acepta null) y validation (reglas con condition y error_message). Para estructuras complejas, definir un object con atributos tipados y defaults por atributo (con optional). Evitar variables de tipo any salvo justificación; documentar cada una. Las variables son la interfaz pública del módulo: cambiarlas es un cambio de contrato. En lo posible, defaults seguros y validaciones que fallen temprano en el plan.

Q54. ¿Cuál es la precedencia de valores de variables?

De menor a mayor prioridad: defaults de la variable → terraform.tfvars y *.auto.tfvars (orden alfabético) → variables de entorno TF_VAR_ → archivo -var-file → -var en la línea de comandos. Los archivos *.auto.tfvars se cargan automáticamente; terraform.tfvars también. Si una variable no tiene default ni valor, Terraform la pide interactivamente (o falla en CI). En automatización, usar -var-file por entorno y variables del pipeline; evitar -var en scripts largos. Cuidado con múltiples archivos que definen la misma variable: el último gana, y puede ser confuso. Documentar la fuente de cada valor y no versionar secretos. La precedencia explica muchos comportamientos inesperados; revisar plan y el valor efectivo antes de aplicar.

Q55. ¿Cómo se organizan tfvars y secretos por entorno?

Una convención común: env/dev.tfvars, env/staging.tfvars, env/prod.tfvars, con valores no sensibles; los secretos se inyectan desde el gestor de secretos o variables del CI, no se versionan. Para secretos en tfvars, usar SOPS/age o un mecanismo de cifrado, pero aun así hay que controlar el acceso. Otra opción: generar variables desde SSM/Secrets Manager con data sources (cuidado con el estado). Los archivos de entorno deben contener solo diferencias y ser revisables; evitar copiar/pegar bloques enormes. En HCP Terraform, variables por workspace con marca sensitive. Validar que prod no herede valores de dev y que los defaults sean seguros. Separar credenciales de configuración. Si un secreto termina en el estado, tratarlo como comprometido y rotarlo; el objetivo es que el repositorio no sea la fuente de secretos.

Q56. ¿Cómo funcionan los outputs y qué significa sensitive?

Los outputs exponen valores (IDs, endpoints, ARNs, nombres) para el usuario, el CI u otros módulos. Sintaxis: output "endpoint" { value = aws_lb.this.dns_name description = "..." sensitive = true }. sensitive evita mostrarlos en la CLI y en la UI, pero no los elimina del estado; si un output sensible se usa en otro recurso, el plan puede ocultar el valor. depends_on en outputs fuerza orden si no hay referencia. En módulos, los outputs son la interfaz de salida: mantenerlos estables y documentados. Para valores que no deben persistir, versiones recientes ofrecen ephemeral. En CI se consumen con terraform output -json. No exponer secretos innecesariamente ni usar outputs como almacén de configuración; para integración entre estados, preferir terraform_remote_state o data sources con permisos acotados.

Q57. ¿Cuándo usar locals y cómo evitar complejidad excesiva?

Los locals sirven para calcular valores repetidos (tags comunes, nombres, mapas derivados) y dar nombres a expresiones complejas. Ayudan a la legibilidad si se usan con moderación: locals { nombre = "${var.app}-${var.env}" tags = merge(local.tags_base, var.tags) }. Antipatrón: construir una capa de «programación» con decenas de locals anidados, condicionales y transformaciones que nadie puede seguir. Si la lógica es muy compleja, dividir en módulos, precalcular datos o usar un lenguaje de programación (Pulumi) si el equipo lo prefiere. Documentar la forma de los datos y mantener expresiones simples. Los locals no se pueden sobrescribir desde fuera; si un valor debe variar por entorno, variable. Evitar locals que dependan de recursos no creados (provocan errores de plan) y recordar que se evalúan en cada plan. La claridad del plan es el mejor indicador.

Q58. ¿Cómo se valida la configuración antes de aplicar (validations, preconditions, check blocks)?

Complementar con validate, análisis estático (Checkov/tfsec/Trivy), políticas (OPA/Sentinel) y tests. Las validaciones deben dar mensajes accionables y no depender de APIs externas si pueden ser locales. Evitar duplicar la misma regla en muchos sitios; centralizar en módulos y políticas. Una validación que no se prueba puede ser tan peligrosa como su ausencia; incluir casos válidos e inválidos en los tests.

Q59. ¿Qué son los valores efímeros y write-only arguments?

Son mecanismos recientes para manejar datos sensibles sin persistirlos en el estado/plan. Los ephemeral values existen durante la operación pero no se guardan; los ephemeral resources abren conexiones o generan credenciales de corta duración (por ejemplo, un token de base de datos) para usarlas en el apply. Los write-only arguments permiten enviar un valor al provider sin almacenarlo en el estado (por ejemplo, una contraseña inicial). Se combinan con gestores de secretos y data sources efímeros. Limitaciones: no todos los providers/atributos los soportan y el comportamiento depende de la versión de Terraform; verificar compatibilidad. La ventaja es reducir la exposición de secretos en estado y planes. Aun así, el estado sigue siendo sensible: cifrado, acceso mínimo y rotación siguen siendo necesarios. No asumir que «sensitive» basta; entender dónde se persiste cada valor.

Q60. ¿Qué estrategia seguir para entornos y promoción?

Definir entornos con proposítos claros (dev, staging, prod), configuraciones y credenciales separadas, y un flujo de promoción: el mismo código/módulo versionado se prueba en dev, se valida en staging y se aplica en prod con aprobación. Las diferencias deben ser datos (tfvars/variables), no copias divergentes del código. Usar estados y backends separados; para prod, permisos más estrictos y ventanas de cambio. Evitar que staging sea «prod con otro nombre» sin datos representativos. Automatizar planes en PR y aplicar solo tras aprobación; registrar quién aprobó. Para entornos efímeros, workspaces o stacks temporales con TTL. Documentar el flujo y los criterios de promoción. La paridad entre entornos reduce sorpresas, pero no siempre es total; identificar y aceptar las diferencias conscientemente. La promoción debe ser reproducible y auditable.

8. Provisioners y gestión de configuración

Q61. ¿Cómo funcionan los connection blocks y remote-exec?

Para remote-exec o file, Terraform necesita un bloque connection con tipo (ssh/winrm), host, usuario, clave o certificado, timeout y bastión si aplica. Ejemplo: connection { type = "ssh" host = self.public_ip user = "ubuntu" private_key = file(...) }. Terraform espera a que el host esté accesible y ejecuta los comandos; si falla, el recurso queda en estado tainted y puede requerir intervención. Riesgos: credenciales en el estado/plan, dependencia de red, comandos no idempotentes, cambios fuera de Terraform y dificultad para reproducir. Además, las claves privadas pueden quedar en el estado. Alternativas: user_data/cloud-init para bootstrap, imágenes con Packer, Ansible o un agente de configuración. Si se usa remote-exec, limitarlo a tareas de emergencia o bootstrap mínimo, con scripts versionados y idempotentes, y sin secretos incrustados.

Q62. ¿cloud-init o user_data frente a provisioners?

cloud-init es el estándar de inicialización de instancias cloud: configura usuarios, paquetes, archivos, servicios y scripts al primer arranque. Se pasa como user_data (o equivalente) y es declarativo, reproducible y no requiere SSH desde Terraform. Es preferible a remote-exec para bootstrap. Limitaciones: se ejecuta en el arranque (no en cada apply), los cambios posteriores no se aplican solos, y los logs/errores requieren revisar la instancia. Para cambios continuos, usar gestión de configuración (Ansible, SSM) o reemplazar la instancia con una imagen nueva. Patrón inmutable: construir una AMI/imagen con Packer que ya traiga la aplicación, y usar user_data solo para datos dinámicos. Evitar scripts enormes en user_data; versionarlos y probarlos. La combinación imagen + cloud-init reduce el uso de provisioners y mejora la trazabilidad.

Q63. ¿Cómo se integran Packer y Ansible con Terraform?

Packer construye imágenes (AMI, imagen de GCP/Azure, Docker) de forma reproducible; Terraform selecciona la imagen (por ID o data source) y crea la infraestructura. Así el software queda horneado y el arranque es rápido. Ansible configura servidores existentes o tareas posteriores; puede ejecutarse desde un pipeline o desde un local-exec (no recomendado como patrón principal). La separación clara es: Packer construye artefactos, Terraform aprovisiona infraestructura, Ansible/SSM gestiona configuración continua. Evitar que Terraform sea el orquestador de Ansible en cada apply: acopla herramientas y complica el estado. Integrar en CI/CD: build de imagen → test → publicación → Terraform actualiza la referencia → despliegue. Versionar las plantillas y fijar versiones de Ansible/Packer. Esta división mejora la inmutabilidad y reduce el uso de remote-exec.

Q64. ¿Cuándo usar el provider external o un provider propio?

El provider external ejecuta un programa y consume su salida JSON como data source; sirve para integrar scripts existentes o APIs sin provider. Riesgos: dependencia de entorno, manejo de errores, seguridad (el script corre con privilegios del pipeline) y falta de idempotencia. Para recursos que no tienen provider, se puede escribir un provider personalizado (Go, Terraform Plugin Framework), lo que es costoso y requiere mantenimiento; alternativas: usar la API con un recurso http, un operador de Kubernetes, o separar esa parte del ciclo de Terraform. Antes de escribir un provider, comprobar si existe uno oficial/comunitario o si el recurso puede gestionarse con otra herramienta. Los providers propios deben versionarse, documentarse y probarse; no son un proyecto trivial. En general, external/provider propio son últimos recursos, no la primera opción.

Q65. ¿Cómo se seleccionan imágenes/AMIs con data sources?

Los data sources permiten buscar la AMI/imagen más reciente que cumple filtros (owner, nombre, arquitectura, tags) en lugar de hardcodear IDs. Patrón: data "aws_ami" "app" { most_recent = true owners = ["self"] filter { name = "name" values = ["app-*"] } }. Ventajas: no hay IDs móviles en el código y se integra con el pipeline de imágenes. Riesgos: most_recent puede cambiar y forzar reemplazos inesperados; fijar versiones/tags y usar lifecycle para controlar. Para producción, es común pasar la AMI como variable desde el pipeline (ID inmutable) y dejar el data source para desarrollo. Documentar quién publica la imagen y cómo se versiona. Combinar con pruebas de la imagen y un proceso de rollback. La inmutabilidad de la referencia es clave para que un apply no cambie la flota sin una decisión explícita.

Q66. ¿Terraform debe gestionar Kubernetes y Helm?

Terraform puede crear el clúster y, con los providers kubernetes y helm, desplegar namespaces, RBAC, operadores y charts. Es útil para el bootstrap y para recursos de plataforma que deben existir antes de GitOps. Limitaciones: el estado de Terraform no es un buen lugar para workloads muy dinámicos; los CRDs/operadores pueden entrar en conflicto; y aplicar Terraform con frecuencia sobre el clúster puede chocar con controladores. Patrón común: Terraform aprovisiona el clúster y componentes base; Argo CD/Flux (GitOps) gestiona aplicaciones y configuraciones continuas. Si se usa Helm con Terraform, fijar versiones de charts, revisar values y evitar secretos en el estado. Definir la frontera entre infraestructura y aplicaciones para que dos herramientas no gestionen el mismo objeto; el conflicto de ownership es una fuente típica de drift.

Q67. ¿Cómo se integran artefactos de build (Lambda, Docker) con Terraform?

Terraform debe desplegar artefactos inmutables, no construirlos en el apply. Patrón: el CI compila y publica (por ejemplo, un ZIP en S3 o una imagen en ECR con tag de commit), y Terraform recibe la referencia (bucket/key/version o image URI) como variable, la usa en el recurso y, si es necesario, fuerza actualización con source_code_hash o replace_triggered_by. Evitar local-exec que ejecute docker build o zip dentro del apply: acopla entornos, requiere herramientas locales y hace el plan impredecible. Para Lambda, empaquetar en CI y subir a S3; para contenedores, construir y escanear la imagen antes de desplegar. Versionar y firmar artefactos; mantener Terraform enfocado en infraestructura y referencias. Así el despliegue es reproducible y se puede hacer rollback cambiando la referencia.

Q68. ¿Dónde está la frontera entre Terraform y la gestión de configuración?

Terraform aprovisiona y gestiona el ciclo de vida de la infraestructura (redes, cómputo, permisos, servicios gestionados); la gestión de configuración (Ansible, Chef, Puppet, SSM) instala y mantiene software dentro de servidores. La frontera recomendada: usar imágenes preconstruidas y cloud-init para bootstrap, Ansible/SSM para cambios continuos, y Terraform solo para recursos del proveedor. Evitar que Terraform ejecute comandos remotos en cada apply; si una configuración cambia con frecuencia, no debería vivir en el estado de Terraform. En Kubernetes, la frontera es clúster/plataforma vs aplicaciones (GitOps). Documentar la responsabilidad de cada herramienta y el flujo de cambios; así se evitan conflictos, drift y dependencias ocultas. La pregunta clave: ¿este cambio debe recrear infraestructura o solo actualizar software? La respuesta define la herramienta.

9. Testing, validación y CI/CD

Q69. ¿Qué controles ejecutar antes de un plan/apply?

Un pipeline serio no se limita a terraform apply. Orden recomendado: (1) terraform fmt -check -recursive; (2) terraform init con backend remoto (o -backend=false si solo se valida sintaxis); (3) terraform validate; (4) lint específico (TFLint) y escaneo de seguridad (Checkov, Trivy/tfsec); (5) terraform test y pruebas de módulos; (6) terraform plan -out=tfplan y revisión del diff; (7) políticas sobre el JSON del plan (OPA/Conftest/Sentinel); (8) aprobación y terraform apply tfplan exacto. validate comprueba la configuración, pero no garantiza que el apply funcione: permisos, cuotas, APIs y estado solo se ven en el plan/apply. El plan debe generarse con las mismas credenciales y entorno que el apply, y el apply no debe volver a planificar. Guardar el plan como artefacto con retención y trazabilidad.

Q70. ¿Qué es terraform test y cómo funcionan los mocks?

Desde Terraform 1.6 existe un framework nativo de pruebas: archivos .tftest.hcl (en tests/ o junto a la configuración) con bloques run. Cada run puede usar command = plan o command = apply, definir variables y validar con assert { condition = ... error_message = ... }. Con mock_provider (1.7+) se simulan providers para pruebas unitarias sin crear recursos reales; mock_resource, mock_data, override_resource y override_data permiten fijar valores. expect_failures comprueba que una validación o precondition falla cuando debe. Se ejecuta con terraform test y admite filtros con -filter. Ventajas: rápido, sin costo y integrado en CI. Límite: no valida el comportamiento real del cloud; para eso se necesitan pruebas de integración en una cuenta sandbox.

Q71. ¿Cómo se prueba la infraestructura con Terratest o Kitchen-Terraform?

Terratest (Gruntwork) es una librería de Go para pruebas de infraestructura: crea un directorio temporal, ejecuta terraform init/apply contra una cuenta sandbox, valida con SDKs (AWS/GCP/Azure), HTTP o comandos, y destruye con defer terraform.Destroy(...). Usa nombres únicos (random.UniqueId) y tags para aislar y limpiar. Kitchen-Terraform combina Terraform con InSpec para verificar recursos y propiedades. Son pruebas de integración reales: detectan problemas de permisos, cuotas, imágenes, redes y comportamiento del provider, pero son lentas, cuestan dinero y pueden ser inestables por dependencias externas. Patrón: pruebas unitarias con terraform test y mocks en cada PR; integración de módulos críticos en nightly o pre-release, con cuenta sandbox, TTL y limpieza garantizada.

Q72. ¿Qué es policy as code y cómo se aplica con Sentinel, OPA o Checkov?

Policy as code convierte reglas de la organización en código versionado y testeado: regiones permitidas, cifrado obligatorio, tags, buckets privados, tipos de instancia, IAM sin admin. Opciones: OPA/Rego con Conftest sobre terraform show -json tfplan; Sentinel en HCP Terraform/Terraform Enterprise; scanners estáticos como Checkov o Trivy (que absorbió tfsec); y validaciones nativas (validation, precondition, postcondition). Se aplican en el PR (shift-left) y otra vez sobre el plan antes del apply (defensa en profundidad). Las políticas deben tener tests propios, niveles (advisory vs blocking), excepciones con justificación y fecha, y mensajes claros. No sustituyen la revisión humana: evitan clases enteras de errores y hacen el cumplimiento reproducible.

Q73. ¿Cómo se diseña un pipeline CI/CD para Terraform?

En cada PR: fmt, validate, lint, tests, escaneo de seguridad, plan con comentario del diff, políticas y estimación de costo; al fusionar en la rama protegida, aprobación y apply del plan guardado. Backend remoto con locking (S3 + DynamoDB, Azure Storage, GCS, HCP Terraform) y concurrencia limitada a un run por estado. Credenciales por entorno con OIDC y roles separados; nunca claves estáticas en el repositorio. El apply corre solo desde el pipeline, no desde laptops. Los entornos se promueven con el mismo código y versiones fijas (dev → staging → prod), con aprobaciones y ventanas de cambio. Añadir drift detection programado, retención de planes/logs y auditoría. Herramientas: GitHub Actions/GitLab CI, Atlantis, HCP Terraform, Spacelift, env0 o Scalr. La regla clave: el plan revisado es el plan que se aplica.

Q74. ¿Cómo se autentica el pipeline sin secretos de larga duración?

Con OIDC: el pipeline solicita un token de identidad efímero y lo intercambia por credenciales cloud. En GitHub Actions se usa permissions: id-token: write y aws-actions/configure-aws-credentials con role-to-assume; en AWS se configura un IAM OIDC provider y un rol cuya trust policy limite el sub a repositorio, rama o environment (por ejemplo, solo refs/heads/main del repo de infraestructura). Equivalentes: Workload Identity Federation en GCP y federated credentials en Azure. Ventajas: sin claves en secrets, rotación automática, alcance por entorno y trazabilidad en CloudTrail. Complementar con roles distintos por entorno, permisos mínimos, environment protection rules y revisión de la trust policy como código.

Q75. ¿Cómo se detecta y gestiona el drift?

Drift es la diferencia entre la infraestructura real y el estado. Se detecta con terraform plan -detailed-exitcode (0 sin cambios, 2 con cambios) o terraform plan -refresh-only, y se corrige con terraform apply (reconciliar a lo declarado) o terraform apply -refresh-only (adoptar el cambio en el estado, solo si es intencional). En equipos: ejecutar planes programados y alertar si hay drift; investigar el origen (clickops, autoscaling, operadores, otro pipeline) antes de decidir. Si un atributo lo gestiona otro sistema, documentarlo y usar lifecycle.ignore_changes de forma deliberada, no como parche. El drift no debe silenciarse: puede indicar incidentes, cambios manuales o conflictos de ownership. Para recursos críticos, prevent_destroy y backups.

Q76. ¿Qué aportan Atlantis, Spacelift, env0, Scalr o HCP Terraform?

Plataformas de automatización: HCP Terraform/Terraform Cloud (runs remotos, registry privado, políticas Sentinel, run tasks, estimación de costo, SSO y auditoría), Atlantis (open source, self-hosted, comenta plan/apply en el PR y bloquea por estado), Spacelift (políticas Rego, drift, workers, RBAC), env0 y Scalr (gestión multi-equipo/multi-cloud), Digger o Terrakube como alternativas open source, y pipelines propios con GitHub Actions/GitLab CI. Criterios de elección: SaaS vs self-hosted, soporte multi-cloud, control de acceso, políticas, auditoría, locking, costo, integración con VCS y esfuerzo operativo. Lo importante no es la herramienta sino el flujo: PR con plan revisado, apply aprobado, estado remoto bloqueado y trazabilidad.

Q77. ¿Cómo se versiona, promueve y hace rollback de la infraestructura?

Versionar: repositorio Git para la configuración, módulos con tags semver, providers y módulos fijados, lock file comprometido y changelog. Promover: el mismo commit y las mismas versiones pasan de dev a staging a prod; el plan se genera una vez y se aplica el artefacto aprobado. Rollback: revertir el commit y aplicar (forward fix) suele ser lo correcto para cambios declarativos; para despliegues inmutables, volver a la imagen/artefacto anterior; para datos, restaurar un backup (Terraform no revierte datos). El estado también se versiona: habilitar versionado en el bucket del backend y probar la restauración. Evitar terraform destroy como rollback y state manual salvo emergencia documentada. Los cambios destructivos requieren ventana, backup y aprobación.

10. Seguridad, gobierno, costos y operación

Q78. ¿Cómo se manejan secretos en Terraform?

Reglas: no escribir secretos en .tf, .tfvars ni en el repositorio; usar un gestor (Vault, AWS Secrets Manager, SSM Parameter Store, GCP Secret Manager, Azure Key Vault) y leerlos con data sources o inyectarlos como variables desde el pipeline. sensitive = true solo oculta el valor en la salida; el secreto sigue en el estado y en el plan. Por eso: backend cifrado (SSE-KMS/CMEK), bucket privado, acceso auditado, versionado y sin lectura amplia; nunca terraform output sin control. Terraform moderno ofrece valores efímeros (ephemeral) y argumentos write-only para que ciertos secretos no se persistan en el estado; verificar la versión y el soporte del provider. Cifrar estado y plan con las capacidades disponibles y restringir los artefactos de CI. Rotar credenciales, no reutilizar las de humanos y eliminar secretos del historial si se filtraron.

Q79. ¿Cómo se aplica el mínimo privilegio en Terraform?

Separar cuentas/proyectos por entorno y dominio; un rol de pipeline por stack, con permisos solo sobre los servicios y recursos que gestiona; trust policy OIDC limitada a repo, rama o environment; permission boundaries y SCPs para prohibir acciones peligrosas (organizations, borrado de logs, cambios de billing). Evitar Action: "*" y Resource: "*"; usar condiciones y tags. Humanos con SSO/MFA y acceso temporal; break-glass auditado; sin access keys de larga duración. Revisar permisos con IAM Access Analyzer, CloudTrail e informes de acceso; el rol que aplica Terraform no debe poder elevar privilegios ni tocar otros equipos. Recordar que leer el estado también es sensible: quien lo lee ve atributos y a veces secretos. La revisión de cambios IAM debe ser parte del PR.

Q80. ¿Cómo se protege la cadena de suministro de providers y módulos?

Fijar providers con restricciones (~> o versión exacta en producción) y comprometer .terraform.lock.hcl; ejecutar terraform providers lock para las plataformas del equipo/CI y verificar checksums; usar el registry oficial o un mirror/registry privado; revisar providers de terceros. Módulos: referenciar tags o commits inmutables, nunca ramas móviles; revisar el código y los recursos que crea; mantener un registry interno de módulos aprobados. Actualizaciones controladas: terraform init -upgrade, revisar changelog, plan en entorno inferior, pruebas y promoción. Firmar commits y artefactos cuando sea posible, generar SBOM y restringir quién publica módulos. Evitar latest, descargas sin verificación y scripts remotos sin pin. La reproducibilidad depende de versiones y hashes, no de la suerte.

Q81. ¿Qué controles de gobierno y compliance se implementan?

Definir estándares (nombres, tags obligatorios, regiones, cifrado, retención), publicar módulos aprobados y golden paths, y automatizar guardrails con policy as code. Separar responsabilidades: quien escribe el cambio no aprueba producción; accesos por rol; auditoría de runs y de estado. Tags mínimos: owner, aplicación, entorno, centro de costo y clasificación de datos. Aplicar default_tags del provider y validar que existan. Mapear controles a marcos (CIS, ISO 27001, SOC 2, PCI) y evidenciar con logs, planes aprobados y reportes de política. Excepciones con justificación, responsable y caducidad. La gobernanza debe facilitar el camino correcto (módulos y pipelines) en vez de depender solo de revisiones manuales.

Q82. ¿Cómo se controlan y estiman los costos?

Estimar el impacto en el PR con Infracost (diff de costo por cambio), etiquetar para allocation/showback, presupuestos y alertas, y políticas que limiten tipos, regiones o SKUs costosos. Derecho de tamaño (right-sizing), autoscaling, spot cuando aplique, lifecycle de almacenamiento y limpieza de imágenes y logs. Entornos efímeros con TTL y destrucción automática; apagar no productivos fuera de horario. Revisar data transfer, recursos huérfanos y volúmenes sin adjuntar. Ojo: la estimación de Infracost es aproximada y no cubre todo (soporte, descuentos, transferencia). El costo es una consecuencia del diseño: estado pequeño, recursos apagados y arquitectura adecuada importan más que microoptimizar el precio de una instancia.

Q83. ¿Cómo se limita el blast radius y se organiza el estado?

Dividir el estado por dominio y ciclo de vida (red, datos, IAM, aplicación), en cuentas/proyectos separados por entorno. Un estado monolítico hace planes lentos, aumenta el riesgo de destruir algo no relacionado y concentra permisos. Dependencias explícitas vía outputs/remote state; evitar referencias ocultas. Aprobaciones y políticas para producción; prevent_destroy y deletion protection en recursos críticos; backups y pruebas de restauración. -target solo para emergencias y seguido de un plan completo. Roles acotados por estado; límites de concurrencia; ventanas de cambio. Refactorizar con bloques moved/removed en vez de recrear. Un estado comprometido es acceso a toda la infraestructura que gestiona: tratarlo como un secreto crítico.

Q84. ¿Cómo se diagnostican los errores típicos de Terraform?

(1) Lock: terraform force-unlock <ID> solo tras confirmar que no hay un run activo; revisar backend y lock ID. (2) 403/denied: credenciales o rol equivocado, región o cuenta incorrecta, trust OIDC, SCP, KMS o permisos faltantes; revisar CloudTrail. (3) Ciclos: terraform graph y refactorizar dependencias; depends_on solo cuando aporta. (4) Conflictos de versión: restricciones, lock file, init -upgrade. (5) Drift: plan -refresh-only. (6) Direcciones cambiadas o recursos sin importar: moved, state mv, state rm + import (con backup). (7) Throttling o errores intermitentes: -parallelism=1 o menor, reintentos del provider. (8) Depuración: TF_LOG=DEBUG/TRACE y TF_LOG_PATH, terraform console, terraform show -json; nunca editar el estado a mano sin copia. (9) Plan guardado y -replace para recrear un recurso concreto.

Q85. ¿Cómo se importan recursos, se actualizan providers y qué antipatrones evitar?

Importar: bloque import { to = aws_instance.web id = "i-123" } (Terraform 1.5+) y luego escribir la configuración; terraform plan -generate-config-out=generated.tf ayuda a generarla (revisar y limpiar). El comando terraform import sigue existiendo, pero el bloque es declarativo y versionable. Refactorizar: bloques moved para cambiar direcciones sin recrear; removed para dejar de gestionar sin destruir. Actualizar providers: leer changelog, subir restricciones, init -upgrade, plan en dev, probar y promover; hacer backup del estado. Antipatrones: clickops, secretos en código o estado sin cifrar, estado local compartido, -target habitual, un estado gigante, módulos sin versión, providers sin pin, provisioners para configuración, ignore_changes indiscriminado, applies desde laptops y ausencia de tests o políticas. Buenas prácticas: cambios pequeños, revisión, CI/CD, backend remoto, módulos reutilizables, documentación y drift detection.

11. Cómo responder preguntas de Terraform en una entrevista (plantilla)

Apertura (sí + estructura): «Sí. He trabajado con Terraform durante [X] años en [AWS/Azure/GCP/multi-cloud], gestionando [redes, cómputo, Kubernetes, datos e IAM] como código. He diseñado módulos, backends remotos y pipelines de plan/apply con políticas de seguridad, y he operado [N] entornos con varios equipos».

Fundamentos e IaC: «Entiendo Terraform como una herramienta declarativa e idempotente: describo el estado deseado, Terraform calcula el diff contra el estado y aplica solo lo necesario. El plan es el contrato: se revisa, se aprueba y se aplica el mismo artefacto. La ventaja frente a scripts es la reproducibilidad, la revisión por pares y la capacidad de detectar drift».

Estado y colaboración: «Uso backend remoto con locking (por ejemplo, S3 + DynamoDB o HCP Terraform), versionado y cifrado; separo estados por dominio y entorno para reducir el blast radius. Nunca aplico desde laptops ni edito el estado a mano sin backup; los cambios de dirección se hacen con bloques moved e import».

Módulos y entornos: «Estructuro módulos pequeños con interfaces claras (variables, outputs y validaciones), versionados con semver y publicados en un registry interno. Promuevo el mismo código y las mismas versiones entre entornos; uso directorios o workspaces de forma deliberada y mantengo los valores por entorno fuera del módulo».

Testing y CI/CD: «En cada PR ejecuto fmt, validate, lint, escaneo de seguridad, terraform test con mocks y un plan revisado; aplico políticas sobre el JSON del plan y estimación de costo. El apply corre en el pipeline, solo desde la rama principal y con aprobación; para módulos críticos hago pruebas de integración con Terratest en una cuenta sandbox».

Seguridad y gobierno: «Autentico con OIDC y credenciales efímeras, aplico mínimo privilegio por stack, cifro el estado y mantengo los secretos fuera del código y del estado cuando es posible. Uso policy as code para regiones, cifrado, tags e IAM, con excepciones documentadas y auditadas».

Operación y costos: «Detecto drift con planes programados, mantengo backups y versionado del estado, y tengo un proceso de rollback basado en revertir el cambio o volver al artefacto anterior. Controlo costos con tags, Infracost, TTL en entornos efímeros y políticas de tipos/regiones».

Cierre (resultados + aplicación al rol): «En mis proyectos logré [métrica: reducción de errores de despliegue, tiempo de aprovisionamiento, recursos gestionados, ahorro de costo, disminución de MTTR]. En este rol aportaría [diseño de IaC/estandarización/automatización] y ayudaría a construir una plataforma reproducible, segura y fácil de operar».

Evidencia sugerida por área (elige lo que realmente hayas hecho)

ÁreaQué mencionarImpacto demostrable
Fundamentos e IaCDeclarativo, idempotencia, plan/apply, drift, ciclo de vidaReproducibilidad, menos errores manuales, auditoría
HCL y providersExpresiones, for_each, dynamic, providers múltiples, data sourcesCódigo reutilizable y legible, menor duplicación
Recursos y dependenciasImplicit/explicit dependencies, lifecycle, moved, replaceCambios seguros, menos recreaciones accidentales
Estado y backendBackend remoto, locking, cifrado, versionado, separaciónColaboración sin conflictos, recuperación ante fallos
Módulos y entornosComposición, semver, variables/outputs, promociónEstandarización, velocidad de entrega, menos deuda
Testing y CI/CDfmt/validate, terraform test, Terratest, OPA/Checkov, plan en PRMenos fallos en producción, trazabilidad, rapidez
Seguridad y gobiernoOIDC, mínimo privilegio, secretos, cifrado, policy as code, tagsCumplimiento, menor superficie de ataque
Costos y operaciónInfracost, budgets, TTL, right-sizing, drift detection, troubleshootingAhorro, menor MTTR, estabilidad operativa
Migración y adopciónImport blocks, moved/removed, state mv, provider upgradesAdopción sin downtime y sin recrear recursos
Liderazgo técnicoEstándares, revisión de código, formación, documentaciónEquipos autónomos, consistencia y buenas prácticas

Consejos

12. Referencias

ReferenciaQué cubre
HashiCorp Developer — Terraform Documentation (developer.hashicorp.com/terraform)Guías oficiales de configuración, CLI, estado, backends, módulos, pruebas e importación.
Terraform Registry (registry.terraform.io)Providers y módulos oficiales y de la comunidad, versiones y documentación.
HCL Configuration LanguageSintaxis, expresiones, funciones, tipos, bloques y estilo recomendado.
Terraform CLI Commandsinit, plan, apply, destroy, state, import, test, graph, console y opciones avanzadas.
State y BackendsRemote state, locking, cifrado, versionado, workspaces y separación de estados.
Module Development y Best PracticesDiseño de módulos, composición, versionado, interfaces y publicación.
Terraform TestingFramework nativo .tftest.hcl, asserts, mocks y pruebas de integración.
Import, moved y removed blocksAdopción de recursos existentes y refactorización sin recrear.
HCP Terraform / Terraform EnterpriseRuns remotos, registry privado, Sentinel, run tasks, SSO y auditoría.
Google Cloud — Terraform best practicesEstructura de repositorios, entornos, estado, CI/CD y seguridad.
AWS Prescriptive Guidance — Terraform best practicesPatrones de colaboración, backend, módulos y automatización.
Open Policy Agent y ConftestPolicy as code con Rego sobre planes de Terraform.
Checkov y Trivy (tfsec)Escaneo estático de configuraciones IaC y detección de malas prácticas.
Terratest, Atlantis e InfracostPruebas de integración, automatización de PR y estimación de costos.
Terraform: Up & Running (Yevgeniy Brikman)Fundamentos, patrones de producción, módulos, testing y equipos.
HashiCorp Certified: Terraform AssociateObjetivos del examen y repaso de fundamentos, CLI, estado y módulos.