Infraestructura Modularidad

📦 Módulos en Terraform: Arquitectura Reutilizable y Escalable

Un Módulo de Terraform es un conjunto de archivos de configuración .tf empaquetados en una misma carpeta para definir un componente de infraestructura reutilizable (por ejemplo: una red VPC completa, un clúster de Kubernetes EKS o una base de datos PostgreSQL con réplicas).

1. ¿Qué es un Módulo y tipos existentes?

En Terraform, cualquier directorio que contenga archivos .tf se considera un módulo. Existen dos clasificaciones principales:

  • Root Module (Módulo Raíz): El directorio principal desde el cual ejecutas los comandos terraform apply o terraform plan. Contiene la configuración global y llama a otros módulos.
  • Child Module (Módulo Hijo): Un módulo secundario empaquetado que es llamado dentro del Root Module mediante un bloque module "nombre" {}.
Analogía Programática: Piensa en un Módulo como una Función en lenguajes tradicionales. Acepta parámetros de entrada (variables.tf), ejecuta lógica de negocio internamente (main.tf) y devuelve un resultado (outputs.tf).

2. Estructura de Archivos Recomendada

Para seguir las convenciones de la comunidad y HashiCorp, la estructura interna de cualquier módulo debe ser limpia y predecible:

text (Estructura de Directorios)
modules/aws-vpc/
├── README.md           # Documentación de uso y parámetros
├── main.tf             # Recursos principales (VPC, Subnets, Gateways)
├── variables.tf        # Variables de entrada expuestas al usuario
├── outputs.tf          # Valores retornados (IDs de subnets, VPC ID, etc.)
├── versions.tf         # Versión mínima de Terraform y proveedores
└── examples/           # Ejemplos de implementación listos para usar
    └── basic-vpc/
        └── main.tf

3. Creando tu Primer Módulo Reutilizable (Child Module)

Diseñemos un módulo básico para aprovisionar un servidor web EC2 con su Security Group correspondiente:

hcl (modules/web-server/variables.tf)
variable "instance_type" {
  type        = string
  default     = "t3.micro"
  description = "Tipo de instancia EC2"
}

variable "server_name" {
  type        = string
  description = "Nombre identificador del servidor"
}

variable "vpc_id" {
  type        = string
  description = "ID de la VPC donde residirá el servidor"
}
hcl (modules/web-server/main.tf)
resource "aws_security_group" "web_sg" {
  name        = "${var.server_name}-sg"
  description = "Allow HTTP traffic"
  vpc_id      = var.vpc_id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_instance" "web" {
  ami                    = "ami-0c55b159cbfafe1f0" # Amazon Linux 2023
  instance_type          = var.instance_type
  vpc_security_group_ids = [aws_security_group.web_sg.id]

  tags = {
    Name = var.server_name
  }
}
hcl (modules/web-server/outputs.tf)
output "instance_id" {
  value       = aws_instance.web.id
  description = "ID de la instancia EC2 creada"
}

output "public_ip" {
  value       = aws_instance.web.public_ip
  description = "Dirección IP pública del servidor"
}

4. Invocación de Módulos desde el Root Module

Una vez creado el módulo, se invoca desde el archivo main.tf principal pasándole los parámetros requeridos mediante el bloque module:

hcl (main.tf del proyecto principal)
module "frontend_server" {
  source        = "./modules/web-server"
  server_name   = "mc-frontend-prod"
  instance_type = "t3.small"
  vpc_id        = "vpc-0a1b2c3d4e5f"
}

# Referenciando la salida (output) del módulo en otro recurso
output "app_url" {
  value = "http://${module.frontend_server.public_ip}"
}

5. Fuentes de Módulos (Module Sources)

La directiva source indica a Terraform desde dónde debe descargar el código del módulo:

  • Ruta Local Relative: source = "./modules/vpc"
  • Repositorio Git Privado o Público: source = "git::https://example.com/storage.git?ref=v1.2.0"
  • Terraform Registry Público: source = "terraform-aws-modules/vpc/aws"
  • Private Registry (HCP / Terraform Cloud): source = "app.terraform.io/mi-org/vpc/aws"

6. Buenas Prácticas de Versionado

Cuando los módulos se comparten entre múltiples equipos o proyectos, es crítico usar control de versiones estricto para no romper infraestructuras en producción si cambia la definición del módulo:

hcl (Uso de módulo versionado desde el Registry)
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.8.1" # Fijar siempre la versión exacta en producción

  name = "mc-production-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["us-east-1a", "us-east-1b"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]
}

7. Reglas de Oro en la Construcción de Módulos

  1. Principio de Responsabilidad Única: Un módulo debe hacer una sola cosa bien (ej. solo gestionar la base de datos o solo la red, no todo junto).
  2. Evita Hardcodear Valores: No coloques regiones, IDs de subnets ni nombres fijos dentro del código del módulo. Exponlos como `variables.tf`.
  3. Documenta los Outputs: Expon los identificadores principales (IDs, ARNs, endpoints) mediante `outputs.tf` para permitir la composición con otros módulos.