Terraform Associate
Deep Dive
Los módulos son el mecanismo principal de reutilización y encapsulación en Terraform. Entender la diferencia entre módulo raíz y módulos hijos, cómo pasan datos y la estructura estándar de archivos es fundamental para el examen.
Contenido
Un módulo es simplemente un directorio que contiene uno o más archivos .tf. No hay nada especial en él: cualquier directorio con archivos .tf es un módulo.
Los módulos permiten encapsular y reutilizar configuraciones. En lugar de repetir la misma configuración de VPC en 10 proyectos, la defines una vez en un módulo y la llamas con diferentes parámetros.
Encapsulación
Oculta la complejidad interna. El usuario solo necesita conocer los inputs y outputs.
Reutilización
El mismo módulo puede usarse con diferentes variables para dev, staging y prod.
Consistencia
Todos los proyectos siguen el mismo patrón porque usan el mismo módulo.
Módulo raíz (root module)
terraform init/plan/applyMódulos hijos (child modules)
# Estructura típica con módulos:
infra/
├── prod/
│ ├── main.tf ← módulo raíz (llama a módulos hijos)
│ ├── variables.tf
│ └── outputs.tf
└── modules/
├── vpc/ ← módulo hijo (networking)
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── eks/ ← módulo hijo (cluster)
├── main.tf
├── variables.tf
└── outputs.tfSe usa el bloque module con una etiqueta (nombre local), el argumento source (obligatorio) y los inputs del módulo como argumentos adicionales.
# Llamar a un módulo local
module "vpc" {
source = "./modules/vpc" # source obligatorio
# Inputs del módulo (variables definidas en modules/vpc/variables.tf)
name = "mi-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b"]
environment = var.environment
}
# Llamar a un módulo del registry (con version obligatorio)
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = "mi-cluster"
cluster_version = "1.29"
vpc_id = module.vpc.vpc_id # output del módulo vpc
subnet_ids = module.vpc.private_subnet_ids
}
# Acceder a outputs del módulo:
# module.<nombre_local>.<nombre_output>
output "cluster_endpoint" {
value = module.eks.cluster_endpoint
}💡 terraform get vs terraform init
Ambos descargan los módulos al directorio .terraform/modules/. La diferencia es que terraform init también instala providers y configura el backend, mientras que terraform get solo descarga módulos. En la práctica siempre usas init.
Los módulos se comunican con su entorno a través de variables (inputs) y outputs. Este es el contrato del módulo.
modules/vpc/variables.tf (inputs)
variable "name" {
type = string
description = "Nombre de la VPC"
}
variable "cidr" {
type = string
default = "10.0.0.0/16"
description = "CIDR block de la VPC"
}
variable "azs" {
type = list(string)
description = "Lista de zonas de disponibilidad"
}modules/vpc/outputs.tf (outputs)
output "vpc_id" {
value = aws_vpc.main.id
description = "ID de la VPC creada"
}
output "private_subnet_ids" {
value = aws_subnet.private[*].id
description = "IDs de las subnets privadas"
}
output "public_subnet_ids" {
value = aws_subnet.public[*].id
description = "IDs de las subnets públicas"
}⚠️ Solo puedes acceder a lo que el módulo expone como output
Desde el módulo padre, no puedes acceder a recursos internos del módulo hijo directamente (por ejemplo, module.vpc.aws_vpc.main.id no funciona). Solo puedes usar los outputs explícitamente definidos en el módulo hijo (module.vpc.vpc_id). Esto garantiza el encapsulamiento.
HashiCorp define una estructura estándar recomendada para módulos. Los módulos del Terraform Registry deben seguirla.
main.tf
RecomendadoRecursos principales del módulo. Es el punto de entrada conceptual. Para módulos complejos, puede dividirse en múltiples archivos (network.tf, compute.tf, etc.).
variables.tf
RecomendadoTodas las declaraciones variable {}. Define el contrato de entrada del módulo.
outputs.tf
RecomendadoTodos los bloques output {}. Define qué expone el módulo hacia afuera.
README.md
RegistryDocumentación del módulo: qué hace, inputs, outputs, ejemplos de uso. Obligatorio para publicar en el registry.
versions.tf
RecomendadoBloque terraform {} con required_version y required_providers. Separa los requisitos de versión del código principal.
locals.tf
OpcionalBloque locals {} para valores calculados. Solo necesario si hay muchos locales.
CHANGELOG.md
RegistryHistorial de cambios por versión. Importante para módulos publicados.
examples/
RegistryDirectorio con ejemplos de uso del módulo. Cada subdirectorio es un ejemplo completo y funcional.
¿Entendiste este tema?
Pon a prueba lo que acabas de aprender
El módulo raíz llama a un módulo hijo 'networking' que crea una VPC y define un output llamado 'vpc_id'. ¿Cómo accede el módulo raíz a ese valor?