Saltar al contenido principal

Día 23 - Variables y Configuración en Terraform

🔧 Variables y Configuración en Terraform

¡Bienvenido al Día 23 del desafío 90 Días de DevOps con Roxs!

Ayer aprendiste los fundamentos de Terraform. Hoy profundizaremos en hacer tu código flexible, reutilizable y mantenible usando variables avanzadas, locals inteligentes y funciones poderosas.

🎯 Lo que lograrás hoy:

  • 🔧 Dominar variables complejas con validaciones robustas
  • 🧮 Crear locals inteligentes que simplifiquen tu código
  • Usar funciones built-in para transformar datos
  • 🎛️ Gestionar configuraciones para múltiples entornos
  • 🏗️ Construir infraestructura dinámica que se adapte automáticamente

🚀 ¿Por qué es importante?

En el mundo real, la infraestructura debe ser:

  • 📈 Escalable: Crecer según demanda
  • 🔄 Reutilizable: Un código para múltiples entornos
  • 🛡️ Segura: Validaciones que previenen errores
  • 👥 Colaborativa: Fácil de entender para el equipo

📝 Variables en Terraform - Fundamentos Avanzados

Las variables son el corazón de la flexibilidad en Terraform. Te permiten crear código que se adapta a diferentes escenarios sin duplicación.

🏗️ Anatomía de una Variable

variable "nombre_variable" {
description = "Descripción clara y útil" # 📝 Documentación
type = string # 🏷️ Tipo de dato
default = "valor_por_defecto" # 🔧 Valor opcional
sensitive = false # 🔒 ¿Es sensitiva?
nullable = false # ❌ ¿Permite null?

validation { # ✅ Reglas de validación
condition = length(var.nombre_variable) > 3
error_message = "Debe tener más de 3 caracteres."
}
}

🎯 Variables con Validaciones Inteligentes

variables.tf

# Variable con múltiples validaciones
variable "app_name" {
description = "Nombre de la aplicación (debe seguir convenciones)"
type = string

validation {
condition = can(regex("^[a-z][a-z0-9-]*[a-z0-9]$", var.app_name))
error_message = "app_name debe empezar con letra, solo contener minúsculas, números y guiones."
}

validation {
condition = length(var.app_name) >= 3 && length(var.app_name) <= 32
error_message = "app_name debe tener entre 3 y 32 caracteres."
}
}

# Variable de entorno con validación estricta
variable "environment" {
description = "Entorno de despliegue (dev/staging/prod)"
type = string

validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment debe ser exactamente: dev, staging, o prod."
}
}

# Variable numérica con rangos
variable "instance_count" {
description = "Número de instancias (1-10)"
type = number

validation {
condition = var.instance_count >= 1 && var.instance_count <= 10
error_message = "instance_count debe estar entre 1 y 10."
}
}

# Variable con validación de formato de email
variable "admin_email" {
description = "Email del administrador"
type = string

validation {
condition = can(regex("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", var.admin_email))
error_message = "admin_email debe ser un email válido."
}
}

# Variable con validación de CIDR
variable "vpc_cidr" {
description = "CIDR block para VPC"
type = string
default = "10.0.0.0/16"

validation {
condition = can(cidrhost(var.vpc_cidr, 0))
error_message = "vpc_cidr debe ser un bloque CIDR válido."
}

validation {
condition = split("/", var.vpc_cidr)[1] >= 16 && split("/", var.vpc_cidr)[1] <= 24
error_message = "vpc_cidr debe tener subnet mask entre /16 y /24."
}
}

# Variable booleana con valor inteligente
variable "enable_monitoring" {
description = "Habilitar monitoreo (recomendado para prod)"
type = bool
default = true
}

# Variable sensitive para secrets
variable "database_password" {
description = "Password de la base de datos"
type = string
sensitive = true

validation {
condition = length(var.database_password) >= 8
error_message = "Password debe tener al menos 8 caracteres."
}
}

🏷️ Tipos de Datos Avanzados

🔤 Tipos Primitivos

# String con validación avanzada
variable "region" {
description = "Región de AWS"
type = string
default = "us-west-2"

validation {
condition = can(regex("^(us|eu|ap|sa|ca|me|af)-(north|south|east|west|central)-[1-9]$", var.region))
error_message = "Debe ser una región válida de AWS."
}
}

# Number con límites específicos
variable "port" {
description = "Puerto de la aplicación"
type = number
default = 8080

validation {
condition = var.port >= 1024 && var.port <= 65535
error_message = "Puerto debe estar entre 1024 y 65535."
}
}

# Boolean con lógica condicional
variable "enable_ssl" {
description = "Habilitar SSL (obligatorio en prod)"
type = bool
default = true
}

📚 Tipos Complejos - Lista (List)

# Lista simple
variable "availability_zones" {
description = "Zonas de disponibilidad"
type = list(string)
default = ["us-west-2a", "us-west-2b", "us-west-2c"]

validation {
condition = length(var.availability_zones) >= 2
error_message = "Debe especificar al menos 2 zonas de disponibilidad."
}
}

# Lista de números
variable "allowed_ports" {
description = "Puertos permitidos en el firewall"
type = list(number)
default = [22, 80, 443, 8080]
}

# Lista con validación de contenido
variable "supported_instance_types" {
description = "Tipos de instancia soportados"
type = list(string)
default = ["t3.micro", "t3.small", "t3.medium"]

validation {
condition = alltrue([
for instance_type in var.supported_instance_types :
can(regex("^(t3|t2|m5|c5)\\.(micro|small|medium|large|xlarge)$", instance_type))
])
error_message = "Todos los tipos de instancia deben ser válidos de AWS."
}
}

🗂️ Tipos Complejos - Mapa (Map)

# Map simple
variable "tags" {
description = "Tags comunes para recursos"
type = map(string)
default = {
Environment = "dev"
Project = "devops-challenge"
Owner = "roxs"
Team = "devops"
}
}

# Map con validación
variable "environment_configs" {
description = "Configuraciones específicas por entorno"
type = map(object({
instance_type = string
min_size = number
max_size = number
}))

default = {
dev = {
instance_type = "t3.micro"
min_size = 1
max_size = 2
}
staging = {
instance_type = "t3.small"
min_size = 2
max_size = 4
}
prod = {
instance_type = "t3.medium"
min_size = 3
max_size = 10
}
}

validation {
condition = alltrue([
for env, config in var.environment_configs :
config.min_size <= config.max_size
])
error_message = "min_size debe ser menor o igual que max_size para todos los entornos."
}
}

# Map anidado complejo
variable "network_config" {
description = "Configuración de red por región"
type = map(object({
vpc_cidr = string
subnets = map(object({
cidr = string
type = string
}))
}))

default = {
"us-west-2" = {
vpc_cidr = "10.0.0.0/16"
subnets = {
public_1 = {
cidr = "10.0.1.0/24"
type = "public"
}
private_1 = {
cidr = "10.0.2.0/24"
type = "private"
}
}
}
}
}

🏗️ Tipos Complejos - Objeto (Object)

# Object simple
variable "database_config" {
description = "Configuración de base de datos"
type = object({
name = string
port = number
username = string
ssl = bool
})

default = {
name = "app_db"
port = 5432
username = "admin"
ssl = true
}
}

# Object complejo con validaciones
variable "application_config" {
description = "Configuración completa de la aplicación"
type = object({
name = string
version = string

# Configuración de runtime
runtime = object({
language = string
version = string
memory = number
cpu = number
})

# Configuración de base de datos
database = object({
engine = string
version = string
storage = number
backups = bool
})

# Features opcionales
features = object({
monitoring = bool
logging = bool
caching = bool
load_balancer = bool
})

# Configuración de red
networking = object({
vpc_cidr = string
subnet_count = number
enable_nat = bool
})
})

# Validaciones del objeto
validation {
condition = contains(["python", "nodejs", "java", "go"], var.application_config.runtime.language)
error_message = "Runtime language debe ser uno de: python, nodejs, java, go."
}

validation {
condition = var.application_config.runtime.memory >= 512 && var.application_config.runtime.memory <= 8192
error_message = "Memory debe estar entre 512MB y 8GB."
}

validation {
condition = contains(["postgres", "mysql", "mongodb"], var.application_config.database.engine)
error_message = "Database engine debe ser: postgres, mysql, o mongodb."
}
}

# Object con valores opcionales
variable "monitoring_config" {
description = "Configuración de monitoreo (opcional)"
type = object({
enabled = bool
retention_days = optional(number, 30)
alert_email = optional(string, "admin@company.com")
slack_webhook = optional(string)
custom_metrics = optional(list(string), [])
})

default = {
enabled = true
}
}

📦 Tipos Complejos - Set

# Set de strings (sin duplicados)
variable "security_groups" {
description = "IDs de grupos de seguridad únicos"
type = set(string)
default = ["sg-123", "sg-456", "sg-789"]

validation {
condition = alltrue([
for sg in var.security_groups :
can(regex("^sg-[a-z0-9]{8,17}$", sg))
])
error_message = "Todos los security groups deben tener formato válido."
}
}

# Set de objetos
variable "allowed_cidrs" {
description = "CIDRs permitidos para acceso"
type = set(object({
cidr = string
description = string
}))

default = [
{
cidr = "10.0.0.0/8"
description = "Red interna"
},
{
cidr = "172.16.0.0/12"
description = "Red privada"
}
]
}

🔄 Tipos Dinámicos y Tuplas

# Tipo any para flexibilidad
variable "custom_config" {
description = "Configuración personalizada flexible"
type = any
default = {}
}

# Tupla con tipos específicos
variable "server_specs" {
description = "Especificaciones del servidor [tipo, vcpu, memoria, storage]"
type = tuple([string, number, number, number])
default = ["t3.medium", 2, 4096, 20]

validation {
condition = var.server_specs[1] >= 1 && var.server_specs[1] <= 96 # vCPU
error_message = "vCPU debe estar entre 1 y 96."
}

validation {
condition = var.server_specs[2] >= 512 && var.server_specs[2] <= 768000 # Memory MB
error_message = "Memoria debe estar entre 512MB y 768GB."
}
}

📊 Usando Variables de Forma Inteligente

🎯 En Recursos - Técnicas Avanzadas

# Uso básico de variables
resource "local_file" "basic_config" {
filename = "${var.app_name}-config.txt"
content = templatefile("${path.module}/templates/config.tmpl", {
app_name = var.app_name
environment = var.environment
port = var.port
enabled = var.enable_monitoring
})
}

# Uso condicional de variables
resource "local_file" "conditional_config" {
count = var.environment == "prod" ? 1 : 0

filename = "${var.app_name}-production.conf"
content = templatefile("${path.module}/templates/prod-config.tmpl", {
app_name = var.app_name
ssl_enabled = var.environment == "prod" ? true : var.enable_ssl
replica_count = var.environment == "prod" ? 3 : 1
})
}

# Uso dinámico con for_each
resource "local_file" "multi_env_configs" {
for_each = var.environment_configs

filename = "${var.app_name}-${each.key}.json"
content = jsonencode({
environment = each.key
instance_type = each.value.instance_type
scaling = {
min = each.value.min_size
max = each.value.max_size
}
features = {
monitoring = each.key == "prod" ? true : var.enable_monitoring
ssl = each.key == "prod" ? true : false
}
})
}

🔄 Interpolación Avanzada y Templates

# Template con lógica condicional compleja
resource "local_file" "advanced_config" {
filename = "app-${var.environment}.conf"
content = <<-EOF
# Configuración generada para ${upper(var.app_name)}
# Entorno: ${title(var.environment)}
# Generado: ${timestamp()}

[APPLICATION]
name = ${var.app_name}
environment = ${var.environment}
version = ${lookup(var.application_config, "version", "1.0.0")}

[RUNTIME]
language = ${var.application_config.runtime.language}
memory = ${var.application_config.runtime.memory}MB
cpu = ${var.application_config.runtime.cpu}

[DATABASE]
engine = ${var.application_config.database.engine}
host = ${var.environment == "prod" ? "prod-db.internal" : "dev-db.local"}
port = ${var.database_config.port}
ssl = ${var.database_config.ssl ? "enabled" : "disabled"}
backups = ${var.application_config.database.backups ? "enabled" : "disabled"}

[FEATURES]
%{ if var.application_config.features.monitoring ~}
monitoring_enabled = true
monitoring_endpoint = /metrics
%{ endif ~}

%{ if var.application_config.features.logging ~}
logging_enabled = true
log_level = ${var.environment == "prod" ? "info" : "debug"}
%{ endif ~}

%{ if var.application_config.features.caching ~}
cache_enabled = true
cache_ttl = ${var.environment == "prod" ? "3600" : "300"}
%{ endif ~}

[NETWORKING]
%{ for zone in var.availability_zones ~}
availability_zone = ${zone}
%{ endfor ~}

vpc_cidr = ${var.application_config.networking.vpc_cidr}
subnet_count = ${var.application_config.networking.subnet_count}

[SECURITY]
%{ for sg in var.security_groups ~}
security_group = ${sg}
%{ endfor ~}

%{ for cidr in var.allowed_cidrs ~}
# ${cidr.description}
allowed_cidr = ${cidr.cidr}
%{ endfor ~}

[PORTS]
%{ for port in var.allowed_ports ~}
allowed_port = ${port}
%{ endfor ~}
EOF
}

# Generación dinámica de archivos de configuración por componente
resource "local_file" "component_configs" {
for_each = toset(["frontend", "backend", "database", "cache"])

filename = "components/${each.key}-${var.environment}.yaml"
content = templatefile("${path.module}/templates/${each.key}.yaml.tpl", {
component = each.key
environment = var.environment
app_name = var.app_name
config = var.application_config

# Configuración específica por componente
replicas = {
frontend = var.environment == "prod" ? 3 : 1
backend = var.environment == "prod" ? 2 : 1
database = 1
cache = var.environment == "prod" ? 2 : 1
}[each.key]

resources = {
frontend = { cpu = "100m", memory = "128Mi" }
backend = { cpu = "200m", memory = "256Mi" }
database = { cpu = "500m", memory = "1Gi" }
cache = { cpu = "100m", memory = "64Mi" }
}[each.key]
})
}

🧮 Uso de Variables en Expresiones

# Cálculos dinámicos basados en variables
resource "local_file" "calculated_config" {
filename = "calculated-resources.json"
content = jsonencode({
# Cálculo de recursos totales
total_cpu_cores = sum([
for config in values(var.environment_configs) :
config.min_size * lookup({
"t3.micro" = 1,
"t3.small" = 1,
"t3.medium" = 2,
"t3.large" = 2
}, config.instance_type, 1)
])

# Cálculo de memoria total
total_memory_gb = sum([
for config in values(var.environment_configs) :
config.min_size * lookup({
"t3.micro" = 1,
"t3.small" = 2,
"t3.medium" = 4,
"t3.large" = 8
}, config.instance_type, 1)
])

# Cálculo de costos estimados
monthly_cost_estimate = sum([
for env, config in var.environment_configs :
config.min_size * lookup({
"t3.micro" = 8.5,
"t3.small" = 17.0,
"t3.medium" = 34.0,
"t3.large" = 67.0
}, config.instance_type, 25.0)
])

# Configuración optimizada por entorno
optimized_configs = {
for env, config in var.environment_configs :
env => merge(config, {
# Auto-scaling inteligente
desired_capacity = max(config.min_size,
env == "prod" ? 3 : 1
)

# Features automáticas por entorno
features_enabled = {
monitoring = env == "prod" ? true : var.enable_monitoring
backup = env == "prod" ? true : false
encryption = env == "prod" ? true : false
cdn = env == "prod" ? true : false
}
})
}
})
}

🏠 Variables Locales (Locals) - El Poder del Cálculo

Los locals son variables calculadas que transforman y combinan datos. Son el cerebro de tu configuración.

🧮 ¿Por qué usar Locals?

  • 🚀 Performance: Se calculan una vez, se usan muchas veces
  • 🎯 Claridad: Simplifican expresiones complejas
  • 🔄 Reutilización: Un cálculo, múltiples usos
  • 🛡️ Mantenibilidad: Centralizan la lógica de negocio

🎯 Locals Básicos pero Potentes

locals {
# 🏷️ Naming conventions automatizadas
resource_prefix = "${var.app_name}-${var.environment}"
dns_name = "${var.app_name}.${var.environment}.company.com"

# 📅 Timestamps inteligentes
creation_timestamp = timestamp()
readable_date = formatdate("YYYY-MM-DD", timestamp())
unique_suffix = formatdate("YYYYMMDD-hhmm", timestamp())

# 🏷️ Tags estandarizados
common_tags = merge(var.tags, {
Terraform = "true"
Environment = var.environment
Application = var.app_name
CreatedDate = local.readable_date
ResourceGroup = local.resource_prefix
})

# 🔄 Transformaciones de datos
uppercase_tags = {
for key, value in local.common_tags :
upper(key) => upper(value)
}

# 📊 Configuraciones por entorno
env_settings = {
dev = {
instance_type = "t3.micro"
min_replicas = 1
max_replicas = 2
enable_logging = true
enable_monitoring = false
backup_retention = 7
}
staging = {
instance_type = "t3.small"
min_replicas = 2
max_replicas = 4
enable_logging = true
enable_monitoring = true
backup_retention = 14
}
prod = {
instance_type = "t3.medium"
min_replicas = 3
max_replicas = 10
enable_logging = true
enable_monitoring = true
backup_retention = 30
}
}

# 🎯 Configuración actual automática
current_env = local.env_settings[var.environment]
}

🧮 Locals Avanzados con Lógica Compleja

locals {
# 🏗️ Configuración de infraestructura inteligente
infrastructure_config = {
# Auto-dimensionamiento basado en entorno
compute = {
instance_type = local.current_env.instance_type
desired_capacity = local.current_env.min_replicas

# Optimización automática de recursos
cpu_credits = startswith(local.current_env.instance_type, "t3") ? "unlimited" : null

# Configuración de storage por tipo de instancia
root_volume_size = lookup({
"t3.micro" = 8
"t3.small" = 10
"t3.medium" = 15
"t3.large" = 20
}, local.current_env.instance_type, 10)
}

# Red inteligente basada en número de AZs
networking = {
vpc_cidr = "10.${var.environment == "prod" ? 0 : var.environment == "staging" ? 1 : 2}.0.0/16"

# Subnets automáticas
public_subnets = [
for i, az in var.availability_zones :
"10.${var.environment == "prod" ? 0 : var.environment == "staging" ? 1 : 2}.${i + 1}.0/24"
]

private_subnets = [
for i, az in var.availability_zones :
"10.${var.environment == "prod" ? 0 : var.environment == "staging" ? 1 : 2}.${i + 10}.0/24"
]

# NAT Gateways inteligentes
enable_nat_gateway = var.environment == "prod" ? true : false
single_nat_gateway = var.environment != "prod" ? true : false
}

# Base de datos optimizada
database = merge(var.database_config, {
# Tamaño automático basado en entorno
allocated_storage = {
dev = 20
staging = 50
prod = 100
}[var.environment]

# Configuración de backup inteligente
backup_retention_period = local.current_env.backup_retention
backup_window = var.environment == "prod" ? "03:00-04:00" : "02:00-03:00"
maintenance_window = var.environment == "prod" ? "sun:04:00-sun:05:00" : "sun:03:00-sun:04:00"

# Multi-AZ solo en producción
multi_az = var.environment == "prod" ? true : false

# Tipo de instancia optimizado
instance_class = {
dev = "db.t3.micro"
staging = "db.t3.small"
prod = "db.r5.large"
}[var.environment]
})
}

# 🔐 Configuración de seguridad dinámica
security_config = {
# Reglas de firewall inteligentes
ingress_rules = concat(
# HTTP/HTTPS básico
[
{
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTP access"
},
{
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTPS access"
}
],

# SSH solo para no-producción o con restricciones
var.environment != "prod" ? [
{
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
description = "SSH access from internal network"
}
] : [],

# Puertos de aplicación personalizados
[
for port in var.allowed_ports : {
from_port = port
to_port = port
protocol = "tcp"
cidr_blocks = [local.infrastructure_config.networking.vpc_cidr]
description = "Application port ${port}"
}
],

# Acceso a base de datos solo desde VPC
[
{
from_port = var.database_config.port
to_port = var.database_config.port
protocol = "tcp"
cidr_blocks = [local.infrastructure_config.networking.vpc_cidr]
description = "Database access from VPC"
}
]
)

# Encriptación automática por entorno
encryption_config = {
ebs_encrypted = var.environment == "prod" ? true : false
s3_sse_algorithm = var.environment == "prod" ? "aws:kms" : "AES256"
rds_storage_encrypted = var.environment == "prod" ? true : false
}
}

# 📊 Cálculos de costos y recursos
cost_analysis = {
# Estimación mensual por servicio
monthly_costs = {
compute = local.current_env.min_replicas * lookup({
"t3.micro" = 8.5
"t3.small" = 17.0
"t3.medium" = 34.0
"t3.large" = 67.0
}, local.current_env.instance_type, 25.0)

database = lookup({
"db.t3.micro" = 15.0
"db.t3.small" = 30.0
"db.r5.large" = 182.0
}, local.infrastructure_config.database.instance_class, 50.0)

storage = local.infrastructure_config.database.allocated_storage * 0.115

network = var.environment == "prod" ? 45.0 : 15.0
}

total_monthly_estimate = sum(values(local.cost_analysis.monthly_costs))

# Recursos totales calculados
total_resources = {
vcpus = local.current_env.min_replicas * lookup({
"t3.micro" = 1
"t3.small" = 1
"t3.medium" = 2
"t3.large" = 2
}, local.current_env.instance_type, 1)

memory_gb = local.current_env.min_replicas * lookup({
"t3.micro" = 1
"t3.small" = 2
"t3.medium" = 4
"t3.large" = 8
}, local.current_env.instance_type, 2)

storage_gb = local.current_env.min_replicas * local.infrastructure_config.compute.root_volume_size
}
}

# 🎛️ Features dinámicas habilitadas
enabled_features = {
monitoring = local.current_env.enable_monitoring || var.enable_monitoring
logging = local.current_env.enable_logging
backup = var.environment != "dev"
cdn = var.environment == "prod"
waf = var.environment == "prod"

# Auto-scaling inteligente
auto_scaling = {
enabled = local.current_env.max_replicas > local.current_env.min_replicas
min_size = local.current_env.min_replicas
max_size = local.current_env.max_replicas
target_cpu = var.environment == "prod" ? 70 : 80
}
}
}

📋 Ejemplo de Uso de Locals en Recursos

# Archivo de configuración de infraestructura completa
resource "local_file" "infrastructure_summary" {
filename = "${local.resource_prefix}-infrastructure.json"
content = jsonencode({
project_info = {
name = var.app_name
environment = var.environment
created_at = local.creation_timestamp
resource_prefix = local.resource_prefix
}

infrastructure = local.infrastructure_config
security = local.security_config
cost_analysis = local.cost_analysis
features = local.enabled_features
components = local.application_components
})
}

# Archivo de configuración por componente
resource "local_file" "component_configs" {
for_each = local.application_components

filename = "components/${each.key}-${var.environment}.yaml"
content = templatefile("${path.module}/templates/component.yaml.tpl", {
component_name = each.key
component_config = each.value
environment = var.environment
tags = local.common_tags
security_group = local.security_groups[each.key]
})
}

# Terraform workspace summary
resource "local_file" "workspace_summary" {
filename = "${local.resource_prefix}-summary.txt"
content = <<-EOF
🚀 TERRAFORM WORKSPACE SUMMARY
================================

📋 PROJECT INFORMATION
Name: ${var.app_name}
Environment: ${upper(var.environment)}
Created: ${local.readable_date}
Resource Prefix: ${local.resource_prefix}

🏗️ INFRASTRUCTURE
VPC CIDR: ${local.infrastructure_config.networking.vpc_cidr}
Instance Type: ${local.infrastructure_config.compute.instance_type}
Min Replicas: ${local.current_env.min_replicas}
Max Replicas: ${local.current_env.max_replicas}

💾 DATABASE
Engine: ${var.database_config.engine}
Instance Class: ${local.infrastructure_config.database.instance_class}
Storage: ${local.infrastructure_config.database.allocated_storage}GB
Multi-AZ: ${local.infrastructure_config.database.multi_az}

🎛️ FEATURES ENABLED
%{ for feature, enabled in local.enabled_features ~}
%{ if enabled ~}
✅ ${title(feature)}
%{ endif ~}
%{ endfor ~}

📊 RESOURCE ALLOCATION
Total vCPUs: ${local.cost_analysis.total_resources.vcpus}
Total Memory: ${local.cost_analysis.total_resources.memory_gb}GB
Total Storage: ${local.cost_analysis.total_resources.storage_gb}GB

💰 COST ESTIMATION (Monthly)
Compute: $${local.cost_analysis.monthly_costs.compute}
Database: $${local.cost_analysis.monthly_costs.database}
Storage: $${local.cost_analysis.monthly_costs.storage}
Network: $${local.cost_analysis.monthly_costs.network}
──────────────────────────────
TOTAL: $${local.cost_analysis.total_monthly_estimate}

🏷️ COMMON TAGS
%{ for key, value in local.common_tags ~}
${key}: ${value}
%{ endfor ~}

──────────────────────────────
Generated by Terraform 🤖
EOF
}

🎯 Mejores Prácticas para Variables

📋 Naming Conventions

# ✅ BUENAS PRÁCTICAS
variable "app_name" { # ✅ snake_case
description = "Nombre de la aplicación" # ✅ Descripción clara
type = string # ✅ Tipo explícito

validation { # ✅ Validación incluida
condition = can(regex("^[a-z][a-z0-9-]*[a-z0-9]$", var.app_name))
error_message = "app_name debe seguir convenciones de naming."
}
}

variable "environment" {
description = "Entorno de despliegue (dev/staging/prod)" # ✅ Opciones claras
type = string

validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment debe ser: dev, staging, o prod."
}
}

# ❌ MALAS PRÁCTICAS
variable "AppName" { } # ❌ PascalCase
variable "app-name" { } # ❌ kebab-case
variable "APPNAME" { } # ❌ UPPERCASE
variable "a" { } # ❌ Nombre no descriptivo

🏗️ Organización de Variables

# variables.tf - Organizado por categorías

# ======================
# CONFIGURACIÓN BÁSICA
# ======================
variable "app_name" {
description = "Nombre de la aplicación"
type = string
# ... configuración
}

variable "environment" {
description = "Entorno de despliegue"
type = string
# ... configuración
}

# ======================
# CONFIGURACIÓN DE RED
# ======================
variable "vpc_cidr" {
description = "CIDR block para VPC"
type = string
# ... configuración
}

variable "availability_zones" {
description = "Zonas de disponibilidad"
type = list(string)
# ... configuración
}

# ======================
# CONFIGURACIÓN DE APLICACIÓN
# ======================
variable "application_config" {
description = "Configuración completa de la aplicación"
type = object({
runtime = object({
language = string
version = string
memory = number
cpu = number
})
# ... más configuración
})
}

# ======================
# CONFIGURACIÓN SENSIBLE
# ======================
variable "database_password" {
description = "Password de la base de datos"
type = string
sensitive = true
# ... configuración
}

🔒 Manejo de Variables Sensibles

# Variables marcadas como sensitive
variable "api_key" {
description = "API key para servicios externos"
type = string
sensitive = true # ✅ No aparece en logs
}

variable "database_credentials" {
description = "Credenciales de base de datos"
type = object({
username = string
password = string
})
sensitive = true # ✅ Todo el objeto es sensitive
}

# Uso de variables sensibles
resource "local_file" "app_config" {
content = templatefile("${path.module}/templates/config.tpl", {
api_key = var.api_key
# La variable sensible se puede usar normalmente
})

lifecycle {
ignore_changes = [content] # ✅ Evita cambios accidentales
}
}

🎛️ Valores Por Defecto Inteligentes

variable "instance_config" {
description = "Configuración de instancias"
type = object({
type = optional(string, "t3.micro") # ✅ Valor por defecto
count = optional(number) # ✅ Sin valor por defecto (requerido cuando se usa)
})
default = {} # ✅ Objeto vacío permite usar solo valores por defecto
}

variable "features" {
description = "Features de la aplicación"
type = object({
monitoring = optional(bool, true) # ✅ Habilitado por defecto
backup = optional(bool, false) # ✅ Deshabilitado por defecto
ssl = optional(bool, true) # ✅ Habilitado por defecto
})
default = {}
}

# Uso con coalesce para fallbacks múltiples
locals {
final_instance_type = coalesce(
var.instance_config.type,
var.environment == "prod" ? "t3.medium" : "t3.micro",
"t3.micro" # último fallback
)
}

🔧 Troubleshooting de Variables

Errores Comunes

Error: Variable not defined

# ❌ Error
│ Error: Reference to undeclared input variable
│ A variable named "app_name" was referenced but not declared.

# ✅ Solución: Declarar en variables.tf
variable "app_name" {
description = "Nombre de la aplicación"
type = string
}

Error: Invalid value for variable

# ❌ Error
│ Error: Invalid value for variable
│ The value "invalid-env" is not valid for variable "environment"

# ✅ Solución: Revisar validaciones
variable "environment" {
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment debe ser: dev, staging, o prod."
}
}

Error: Type constraint error

# ❌ Error
│ Error: Invalid value for input variable
│ Expected a string, but got number.

# ✅ Solución: Usar conversión de tipos
locals {
port_string = tostring(var.port) # Convertir number a string
}

🛠️ Comandos de Debugging

Validar Variables

# Validar todas las variables
terraform validate

# Ver valores de variables
terraform console
> var.app_name
> local.common_tags
> var.application_config.runtime.memory

# Ver variables de entorno
env | grep TF_VAR_

# Ver plan con variables específicas
terraform plan -var="app_name=debug-app"

Debugging de Expresiones

# En terraform console
> upper(var.app_name)
> length(var.availability_zones)
> keys(var.tags)
> jsonencode(local.common_tags)

# Testing de funciones
> can(regex("^[a-z-]+$", "test-app"))
> try(var.tags.NonExistent, "default")
> formatdate("YYYY-MM-DD", timestamp())

Inspección de Estado

# Ver variables en outputs
terraform output

# Ver estado completo
terraform show

# Inspeccionar recursos específicos
terraform state show local_file.config

🔍 Técnicas de Debugging Avanzadas

Debugging con Outputs

# Outputs para debugging
output "debug_info" {
value = {
variables = {
app_name = var.app_name
environment = var.environment
zones = var.availability_zones
}
locals = {
resource_prefix = local.resource_prefix
common_tags = local.common_tags
current_env = local.current_env
}
computed = {
validation_results = local.validation_results
all_validations = local.all_validations_pass
}
}
}

# Output para ver transformaciones
output "transformations" {
value = {
original_tags = var.tags
processed_tags = local.common_tags
uppercase_tags = local.uppercase_tags
}
}

Archivos de Debug

# Archivo de debug con toda la información
resource "local_file" "debug_output" {
filename = "debug-${var.environment}.json"
content = jsonencode({
timestamp = timestamp()
variables = {
app_name = var.app_name
environment = var.environment
application_config = var.application_config
}
locals = {
resource_prefix = local.resource_prefix
current_env = local.current_env
infrastructure = local.infrastructure_config
validation_results = local.validation_results
}
terraform_info = {
workspace = terraform.workspace
version = "1.6+"
}
})
}

🎯 Ejercicios Prácticos Avanzados

🏆 Ejercicio 1: Sistema de Configuración Multi-Ambiente

Crear un sistema que genere configuraciones diferentes para dev, staging y prod:

# Challenge: Implementar estas variables y locals
variable "environments" {
type = map(object({
instance_type = string
min_replicas = number
max_replicas = number
enable_monitoring = bool
backup_retention = number
ssl_required = bool
}))
}

locals {
# Generar configuración automática para cada ambiente
environment_configs = {
for env, config in var.environments :
env => merge(config, {
# Auto-sizing basado en environment
storage_size = env == "prod" ? 100 : env == "staging" ? 50 : 20

# Features automáticas
cdn_enabled = env == "prod"
waf_enabled = env == "prod"

# Naming convention
resource_prefix = "${var.app_name}-${env}"

# Costos estimados
monthly_cost = config.min_replicas * lookup({
"t3.micro" = 8.5
"t3.small" = 17.0
"t3.medium" = 34.0
}, config.instance_type, 25.0)
})
}
}

🏆 Ejercicio 2: Validador de Configuración Avanzado

# Challenge: Crear validaciones complejas
locals {
configuration_validation = {
# Validar que prod tenga configuración robusta
prod_requirements_met = (
var.environment == "prod" ? (
var.application_config.features.monitoring == true &&
var.application_config.features.backup == true &&
var.application_config.runtime.memory >= 1024
) : true
)

# Validar coherencia entre variables
memory_cpu_ratio = (
var.application_config.runtime.memory / var.application_config.runtime.cpu
)
memory_ratio_valid = (
local.memory_cpu_ratio >= 256 && local.memory_cpu_ratio <= 2048
)

# Validar nombres únicos
resource_names_unique = length(distinct([
var.app_name,
var.application_config.name
])) == 2
}

all_validations_passed = alltrue(values(local.configuration_validation))
}

# Generar reporte de validación
resource "local_file" "validation_report" {
filename = "validation-report-${var.environment}.txt"
content = <<-EOF
VALIDATION REPORT
=================

Environment: ${var.environment}
Timestamp: ${timestamp()}

Validation Results:
%{ for check, result in local.configuration_validation ~}
${result ? "✅" : "❌"} ${check}: ${result}
%{ endfor ~}

Overall Status: ${local.all_validations_passed ? "✅ PASSED" : "❌ FAILED"}

%{ if !local.all_validations_passed ~}
Please fix the failing validations before proceeding.
%{ endif ~}
EOF
}

🏆 Ejercicio 3: Generador de Configuración Dinámica

# Challenge: Generar configuraciones para múltiples servicios
variable "microservices" {
type = map(object({
port = number
language = string
memory_mb = number
replicas = number
dependencies = list(string)
}))
}

locals {
# Generar configuración para cada microservicio
service_configs = {
for service_name, config in var.microservices :
service_name => {
# Configuración base
name = service_name

# Configuración de red
internal_url = "http://${service_name}:${config.port}"

# Configuración de recursos basada en lenguaje
resources = {
cpu = config.language == "java" ? "500m" :
config.language == "python" ? "200m" :
"100m"
memory = "${config.memory_mb}Mi"
}

# Health checks específicos por lenguaje
health_check = {
path = config.language == "java" ? "/actuator/health" :
config.language == "nodejs" ? "/health" :
"/healthz"
port = config.port
}

# Variables de entorno automáticas
environment_vars = merge(
{
SERVICE_NAME = service_name
SERVICE_PORT = tostring(config.port)
ENVIRONMENT = var.environment
},
# URLs de dependencias
{
for dep in config.dependencies :
"${upper(dep)}_URL" => "http://${dep}:${var.microservices[dep].port}"
}
)
}
}
}

# Generar archivos de configuración para cada servicio
resource "local_file" "service_configs" {
for_each = local.service_configs

filename = "services/${each.key}-config.yaml"
content = templatefile("${path.module}/templates/service.yaml.tpl", {
service = each.value
global_config = {
app_name = var.app_name
environment = var.environment
tags = local.common_tags
}
})
}

✅ ¿Qué Aprendiste Hoy? - Resumen Completo

¡Felicitaciones! Has dominado uno de los aspectos más importantes de Terraform. Hoy aprendiste:

🎯 Variables Avanzadas

Tipos de datos complejos (objects, maps, lists, sets)
Validaciones robustas con regex y condiciones
Variables sensibles para manejo seguro de secrets
Valores opcionales y por defecto inteligentes
Organización y naming conventions

🧮 Locals Inteligentes

Cálculos complejos y transformaciones de datos
Configuración dinámica basada en entornos
For expressions y manipulación de colecciones
Lógica condicional avanzada
Optimización de recursos y costos

Funciones Built-in

+100 funciones organizadas por categoría
String manipulation y formateo
Operaciones de red y CIDR
Matemáticas y cálculos
Validaciones y manejo de errores

🎛️ Gestión de Variables

Múltiples fuentes de asignación (precedencia)
Archivos por entorno y organización
Variables de entorno para CI/CD
Estrategias avanzadas con Vault y secrets

🛠️ Troubleshooting

Debugging de variables y expresiones
Validación y testing
Mejores prácticas de organización


🚀 Comandos Esenciales del Día

# 🔍 VALIDACIÓN Y DEBUGGING
terraform validate # Validar sintaxis
terraform console # Consola interactiva
terraform plan -var="app_name=test" # Plan con variables

# 📊 INSPECCIÓN DE VARIABLES
terraform console
> var.app_name # Ver variable
> local.common_tags # Ver local
> keys(var.tags) # Funciones en tiempo real

# 🎛️ GESTIÓN DE ENTORNOS
terraform apply -var-file="dev.tfvars"
terraform workspace select prod
terraform apply -auto-approve

# 🔧 DEBUGGING AVANZADO
export TF_LOG=DEBUG
terraform plan > debug.log
terraform output debug_info

🎯 Checklist de Dominio

Marca cada elemento cuando lo hayas practicado:

Variables

  • ✅ Crear variables con validaciones
  • ✅ Usar tipos complejos (object, map)
  • ✅ Implementar variables sensibles
  • ✅ Configurar valores por defecto

Locals

  • ✅ Crear locals con cálculos
  • ✅ Usar for expressions
  • ✅ Implementar lógica condicional
  • ✅ Configuración por entorno

Funciones

  • ✅ Usar funciones de string
  • ✅ Manipular colecciones
  • ✅ Calcular CIDRs de red
  • ✅ Formatear fechas

Gestión

  • ✅ Crear archivos .tfvars
  • ✅ Usar variables de entorno
  • ✅ Organizar por ambiente
  • ✅ Debugging con console

🔮 ¿Qué Sigue Mañana?

Mañana en el Día 24 aplicaremos todo lo aprendido con:

🐳 Provider Docker

  • Gestión de imágenes Docker con Terraform
  • Creación y configuración de contenedores
  • Redes y volúmenes
  • Orquestación con docker-compose

🎯 Lo que construiremos:

  • Stack completo de aplicación web
  • Base de datos PostgreSQL
  • Redis para caché
  • Nginx como proxy reverso
  • Monitoreo con Prometheus

💡 Conceptos nuevos:

  • Provider configuration
  • Resource dependencies
  • Data sources
  • Resource lifecycle

🏆 Desafío Extra (Opcional)

Crea un generador de infraestructura completo que:

  1. 📝 Tome variables de múltiples ambientes
  2. 🧮 Calcule recursos automáticamente
  3. 💰 Estime costos por ambiente
  4. 📊 Genere reportes en JSON/YAML
  5. Valide configuraciones complejas
# Tu objetivo:
terraform apply -var-file="environments/prod.tfvars"
# Debe generar:
# - Configuraciones por servicio
# - Reportes de costos
# - Validaciones de seguridad
# - Archivos de deployment

📚 Recursos para Profundizar

🔗 Documentación Oficial

🛠️ Herramientas Útiles

📖 Lecturas Recomendadas


💬 Comparte tu progreso en la comunidad con el hashtag #DevOpsConRoxs

¡Excelente trabajo dominando las variables y configuración en Terraform! 🎉
Ahora tienes las herramientas para crear infraestructura verdaderamente flexible y mantenible.