Crear y Usar Módulos de Terraform para Reutilización

Video
30 min~5 min lectura

Reproductor de video

Concepto clave

Los módulos de Terraform son contenedores reutilizables para grupos de recursos que funcionan juntos. Piensa en ellos como plantillas de infraestructura que puedes usar una y otra vez en diferentes proyectos o entornos. En el mundo real, es similar a cómo un arquitecto usa planos estandarizados para construir casas con la misma estructura básica, pero adaptándolas a diferentes terrenos o necesidades específicas.

Cuando trabajas en proyectos DevOps, encontrarás que ciertos patrones de infraestructura se repiten constantemente: una VPC con subredes públicas y privadas, un clúster ECS con balanceador de carga, o una base de datos RDS con réplicas. En lugar de copiar y pegar código cada vez, los módulos te permiten encapsular esta lógica en un componente que puedes versionar, compartir y reutilizar. Esto no solo ahorra tiempo, sino que también reduce errores y mantiene la consistencia en toda tu organización.

Cómo funciona en la práctica

Para crear un módulo, organizas tu código Terraform en una estructura de directorios específica. El directorio del módulo contiene archivos .tf que definen recursos, variables y outputs. Luego, desde tu configuración principal, llamas al módulo usando un bloque module, pasando los valores necesarios a través de variables.

Veamos el proceso paso a paso:

  1. Crea un directorio para tu módulo (ej: modules/ec2-instance)
  2. Dentro, define los archivos Terraform con recursos, variables y outputs
  3. En tu configuración raíz, llama al módulo especificando la ruta
  4. Proporciona valores para las variables requeridas
  5. Aplica los cambios como lo harías normalmente

La magia está en que puedes usar el mismo módulo para desplegar instancias EC2 en desarrollo, staging y producción, simplemente cambiando los valores de las variables como el tipo de instancia o el tamaño del almacenamiento.

Código en acción

Primero, veamos cómo se vería un despliegue de EC2 sin módulos:

# main.tf sin módulos
resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.public.id
  
  tags = {
    Name = "web-server-prod"
    Environment = "production"
  }
}

resource "aws_security_group" "web_sg" {
  name        = "web-security-group"
  description = "Security group for web server"
  
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Ahora, refactorizamos esto en un módulo reutilizable:

# modules/ec2-web-server/main.tf
variable "instance_type" {
  description = "Tipo de instancia EC2"
  type        = string
  default     = "t2.micro"
}

variable "environment" {
  description = "Entorno (dev, staging, prod)"
  type        = string
}

variable "subnet_id" {
  description = "ID de la subred donde desplegar"
  type        = string
}

resource "aws_instance" "this" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = var.instance_type
  subnet_id     = var.subnet_id
  
  tags = {
    Name        = "web-server-${var.environment}"
    Environment = var.environment
  }
}

resource "aws_security_group" "this" {
  name        = "web-sg-${var.environment}"
  description = "Security group for web server in ${var.environment}"
  
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

output "instance_id" {
  description = "ID de la instancia creada"
  value       = aws_instance.this.id
}

output "security_group_id" {
  description = "ID del security group creado"
  value       = aws_security_group.this.id
}

Y así lo usamos desde nuestra configuración principal:

# main.tf usando el módulo
module "web_server_prod" {
  source        = "./modules/ec2-web-server"
  instance_type = "t2.large"
  environment   = "production"
  subnet_id     = aws_subnet.public.id
}

module "web_server_dev" {
  source        = "./modules/ec2-web-server"
  instance_type = "t2.micro"
  environment   = "development"
  subnet_id     = aws_subnet.public.id
}

Errores comunes

  • Ciclos de dependencia en módulos: Cuando el módulo A depende del output del módulo B, y viceversa. Solución: Rediseña la arquitectura para tener dependencias unidireccionales o usa data sources.
  • Variables no documentadas: Crear módulos sin describir qué hace cada variable. Esto hace que otros desarrolladores no sepan cómo usarlos. Solución: Siempre incluye descripciones en todas las variables y outputs.
  • Módulos demasiado específicos o demasiado genéricos: Un módulo que solo sirve para un caso muy particular no es reutilizable, pero uno que intenta hacer todo se vuelve complejo. Solución: Encuentra el balance - módulos que encapsulan un patrón común pero permiten configuración.
  • No versionar módulos: Usar rutas locales sin control de versiones. Solución: Publica tus módulos en GitHub o Terraform Registry con versiones semánticas.
  • Ignorar outputs: No exponer información útil desde el módulo. Solución: Piensa qué información necesitarán otros módulos o configuraciones y exponla como output.

Checklist de dominio

  1. Puedo crear un módulo Terraform que encapsule al menos 3 recursos AWS relacionados
  2. Sé definir variables con tipos, descripciones y valores por defecto apropiados
  3. Puedo usar outputs para exponer información útil desde mis módulos
  4. Entiendo cómo llamar módulos desde diferentes fuentes (local, GitHub, Terraform Registry)
  5. Puedo usar el mismo módulo para desplegar recursos en diferentes entornos (dev/staging/prod)
  6. Sé cómo estructurar directorios para módulos reutilizables
  7. Puedo documentar mis módulos para que otros desarrolladores los usen correctamente

Crear un módulo Terraform para una arquitectura web básica en AWS

En este ejercicio práctico, crearás un módulo Terraform reutilizable que despliegue una arquitectura web básica en AWS. Sigue estos pasos:

  1. Crea una nueva carpeta llamada terraform-module-exercise y dentro de ella, crea la estructura:
    • modules/web-architecture/ (para tu módulo)
    • environments/production/ (para la configuración de producción)
    • environments/development/ (para la configuración de desarrollo)
  2. Dentro de modules/web-architecture/, crea los siguientes archivos:
    • main.tf - Define: una VPC, dos subredes (pública y privada), un Internet Gateway, una tabla de rutas, y una instancia EC2 en la subred pública con un security group que permita HTTP/HTTPS.
    • variables.tf - Define variables para: environment (string), instance_type (string con default "t2.micro"), vpc_cidr (string con default "10.0.0.0/16").
    • outputs.tf - Expone: vpc_id, public_subnet_id, instance_public_ip.
  3. En environments/production/main.tf, llama a tu módulo con:
    • environment = "production"
    • instance_type = "t2.large"
    • vpc_cidr = "10.1.0.0/16"
  4. En environments/development/main.tf, llama al mismo módulo con:
    • environment = "development"
    • instance_type = "t2.micro" (usa el valor por defecto)
    • vpc_cidr = "10.2.0.0/16"
  5. Ejecuta terraform init, terraform plan y terraform apply (en modo dry-run o con un backend configurado) para verificar que tu módulo funciona correctamente en ambos entornos.
Pistas
  • Recuerda que la VPC necesita un Internet Gateway y rutas para que la subred pública sea accesible desde internet.
  • Usa count o for_each en el módulo si quieres hacerlo más flexible para diferentes números de subredes.
  • Considera separar la VPC y la instancia EC2 en submódulos si tu arquitectura crece en complejidad.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.