Automatización de Infraestructura: Construyendo el Futuro con IaC

La automatización de infraestructura es el pilar fundamental de las operaciones modernas de TI. En un mundo donde la velocidad de desarrollo se mide en despliegues por día, mantener procesos manuales de aprovisionamiento es una limitante crítica. Infrastructure as Code (IaC) no solo elimina esta barrera, sino que transforma completamente cómo concebimos y gestionamos los recursos tecnológicos.

El Paradigma de Infrastructure as Code

Transformación del Modelo Tradicional

graph TD
    A[Modelo Tradicional] --> B[Procesos Manuales]
    A --> C[Configuración Inconsistente]
    A --> D[Documentación Desactualizada]
    
    E[Modelo IaC] --> F[Automatización Completa]
    E --> G[Configuración Declarativa]
    E --> H[Código como Documentación]
    
    B --> I[8 horas por servidor]
    F --> J[5 minutos por servidor]

La evolución hacia IaC representa un cambio paradigmático fundamental:

AspectoTradicionalIaC
AprovisionamientoManual, 4-8 horasAutomatizado, 5-10 minutos
ConsistenciaVariable por operadorDeterminística
VersionadoDocumentos desactualizadosGit como fuente de verdad
TestingManual, post-despliegueAutomatizado, pre-despliegue
RollbackProceso complejoterraform destroy + redeploy

Stack Completo de Herramientas IaC

Terraform: Orchestración Multi-Cloud

# main.tf - Infraestructura empresarial completa
terraform {
  required_version = ">= 1.5"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.23"
    }
  }
  
  backend "s3" {
    bucket         = "mi-empresa-terraform-state"
    key            = "infrastructure/terraform.tfstate"
    region         = "us-west-2"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

# Variables de configuración
variable "environment" {
  description = "Entorno de despliegue"
  type        = string
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod."
  }
}

variable "application_config" {
  description = "Configuración de aplicaciones"
  type = object({
    name           = string
    version        = string
    replicas       = number
    cpu_request    = string
    memory_request = string
    cpu_limit      = string
    memory_limit   = string
  })
}

# Data sources para obtener información existente
data "aws_availability_zones" "available" {
  state = "available"
}

data "aws_caller_identity" "current" {}

# Configuración de red con módulo personalizado
module "networking" {
  source = "./modules/networking"
  
  vpc_cidr             = "10.0.0.0/16"
  availability_zones   = data.aws_availability_zones.available.names
  environment          = var.environment
  enable_nat_gateway   = var.environment == "prod" ? true : false
  enable_vpn_gateway   = var.environment == "prod" ? true : false
  
  tags = local.common_tags
}

# EKS Cluster con configuración empresarial
module "eks_cluster" {
  source = "./modules/eks"
  
  cluster_name     = "${var.environment}-cluster"
  cluster_version  = "1.28"
  vpc_id          = module.networking.vpc_id
  subnet_ids      = module.networking.private_subnet_ids
  
  # Configuración de nodos con múltiples grupos
  node_groups = {
    general = {
      instance_types = ["t3.medium", "t3a.medium"]
      capacity_type  = "SPOT"
      min_size      = 2
      max_size      = 10
      desired_size  = var.environment == "prod" ? 3 : 2
      
      labels = {
        role = "general"
      }
      
      taints = []
    }
    
    compute_optimized = {
      instance_types = ["c5.xlarge", "c5a.xlarge"]
      capacity_type  = "ON_DEMAND"
      min_size      = 0
      max_size      = 5
      desired_size  = var.environment == "prod" ? 1 : 0
      
      labels = {
        role = "compute-intensive"
      }
      
      taints = [
        {
          key    = "compute-intensive"
          value  = "true"
          effect = "NO_SCHEDULE"
        }
      ]
    }
  }
  
  # Addons habilitados
  addons = {
    aws-ebs-csi-driver = {
      version = "v1.24.0-eksbuild.1"
    }
    vpc-cni = {
      version = "v1.15.1-eksbuild.1"
    }
    coredns = {
      version = "v1.10.1-eksbuild.4"
    }
  }
  
  tags = local.common_tags
}

# Base de datos RDS con alta disponibilidad
module "database" {
  source = "./modules/rds"
  
  identifier     = "${var.environment}-postgres"
  engine         = "postgres"
  engine_version = "15.4"
  instance_class = var.environment == "prod" ? "db.r6g.xlarge" : "db.t3.micro"
  
  allocated_storage     = var.environment == "prod" ? 100 : 20
  max_allocated_storage = var.environment == "prod" ? 1000 : 100
  storage_encrypted     = true
  
  db_name  = "myapp"
  username = "dbadmin"
  password = random_password.db_password.result
  
  vpc_security_group_ids = [aws_security_group.database.id]
  db_subnet_group_name   = module.networking.database_subnet_group_name
  
  # Configuración de backup y mantenimiento
  backup_retention_period = var.environment == "prod" ? 30 : 7
  backup_window          = "03:00-04:00"
  maintenance_window     = "sun:04:00-sun:05:00"
  
  # Multi-AZ solo en producción
  multi_az = var.environment == "prod" ? true : false
  
  # Monitoring y logging
  monitoring_interval = 60
  monitoring_role_arn = aws_iam_role.rds_enhanced_monitoring.arn
  
  enabled_cloudwatch_logs_exports = ["postgresql"]
  
  deletion_protection = var.environment == "prod" ? true : false
  
  tags = local.common_tags
}

# Sistema de caché Redis
module "redis_cache" {
  source = "./modules/elasticache"
  
  cluster_id           = "${var.environment}-redis"
  node_type           = var.environment == "prod" ? "cache.r6g.large" : "cache.t3.micro"
  num_cache_nodes     = var.environment == "prod" ? 3 : 1
  parameter_group_name = "default.redis7"
  port                = 6379
  
  subnet_group_name = module.networking.cache_subnet_group_name
  security_group_ids = [aws_security_group.redis.id]
  
  # Configuración de backup
  snapshot_retention_limit = var.environment == "prod" ? 30 : 1
  snapshot_window         = "03:00-05:00"
  
  tags = local.common_tags
}

# Sistema de monitoreo con Prometheus/Grafana
module "monitoring" {
  source = "./modules/monitoring"
  
  cluster_name = module.eks_cluster.cluster_name
  environment  = var.environment
  
  # Configuración de almacenamiento para métricas
  prometheus_storage_size = var.environment == "prod" ? "100Gi" : "20Gi"
  grafana_storage_size    = var.environment == "prod" ? "10Gi" : "5Gi"
  
  # Configuración de alertas
  slack_webhook_url = var.slack_webhook_url
  alert_manager_config = templatefile("${path.module}/templates/alertmanager.yml", {
    environment = var.environment
  })
  
  tags = local.common_tags
}

# Sistema de logging con ELK Stack
module "logging" {
  source = "./modules/logging"
  
  cluster_name = module.eks_cluster.cluster_name
  environment  = var.environment
  
  # Configuración de ElasticSearch
  elasticsearch_node_count = var.environment == "prod" ? 3 : 1
  elasticsearch_node_type  = var.environment == "prod" ? "m5.large.elasticsearch" : "t3.small.elasticsearch"
  elasticsearch_storage    = var.environment == "prod" ? 100 : 20
  
  # Configuración de retención
  log_retention_days = var.environment == "prod" ? 90 : 7
  
  tags = local.common_tags
}

# Generación de contraseña segura
resource "random_password" "db_password" {
  length  = 16
  special = true
}

# Almacenamiento seguro de secretos
resource "aws_secretsmanager_secret" "database_credentials" {
  name_recovery_window_in_days = 7
  description                  = "Database credentials for ${var.environment}"
  
  tags = local.common_tags
}

resource "aws_secretsmanager_secret_version" "database_credentials" {
  secret_id = aws_secretsmanager_secret.database_credentials.id
  secret_string = jsonencode({
    username = "dbadmin"
    password = random_password.db_password.result
    endpoint = module.database.endpoint
    port     = 5432
    dbname   = "myapp"
  })
}

# Configuración de seguridad - Security Groups
resource "aws_security_group" "database" {
  name_prefix = "${var.environment}-database-"
  vpc_id      = module.networking.vpc_id
  description = "Security group for RDS database"

  ingress {
    description     = "PostgreSQL from EKS nodes"
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [module.eks_cluster.node_security_group_id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = merge(local.common_tags, {
    Name = "${var.environment}-database-sg"
  })
  
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group" "redis" {
  name_prefix = "${var.environment}-redis-"
  vpc_id      = module.networking.vpc_id
  description = "Security group for Redis cache"

  ingress {
    description     = "Redis from EKS nodes"
    from_port       = 6379
    to_port         = 6379
    protocol        = "tcp"
    security_groups = [module.eks_cluster.node_security_group_id]
  }

  tags = merge(local.common_tags, {
    Name = "${var.environment}-redis-sg"
  })
  
  lifecycle {
    create_before_destroy = true
  }
}

# IAM Role para enhanced monitoring de RDS
resource "aws_iam_role" "rds_enhanced_monitoring" {
  name_prefix = "${var.environment}-rds-monitoring-"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "monitoring.rds.amazonaws.com"
        }
      }
    ]
  })

  tags = local.common_tags
}

resource "aws_iam_role_policy_attachment" "rds_enhanced_monitoring" {
  role       = aws_iam_role.rds_enhanced_monitoring.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole"
}

# Configuración de tags comunes
locals {
  common_tags = {
    Environment   = var.environment
    Project      = "MyApp"
    Owner        = "DevOps Team"
    ManagedBy    = "Terraform"
    CreatedDate  = "2025-05-06"
    CostCenter   = "Engineering"
  }
}

# Outputs importantes
output "vpc_id" {
  description = "ID de la VPC creada"
  value       = module.networking.vpc_id
}

output "eks_cluster_endpoint" {
  description = "Endpoint del cluster EKS"
  value       = module.eks_cluster.cluster_endpoint
}

output "database_endpoint" {
  description = "Endpoint de la base de datos RDS"
  value       = module.database.endpoint
  sensitive   = true
}

output "redis_endpoint" {
  description = "Endpoint del cluster Redis"
  value       = module.redis_cache.cache_nodes
}

output "kubeconfig_command" {
  description = "Comando para configurar kubectl"
  value       = "aws eks update-kubeconfig --region us-west-2 --name ${module.eks_cluster.cluster_name}"
}

Módulos Terraform Reutilizables

# modules/networking/main.tf - Módulo de red empresarial
variable "vpc_cidr" {
  description = "CIDR block for VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "availability_zones" {
  description = "List of availability zones"
  type        = list(string)
}

variable "environment" {
  description = "Environment name"
  type        = string
}

variable "enable_nat_gateway" {
  description = "Enable NAT Gateway for private subnets"
  type        = bool
  default     = true
}

variable "enable_vpn_gateway" {
  description = "Enable VPN Gateway"
  type        = bool
  default     = false
}

variable "tags" {
  description = "Common tags"
  type        = map(string)
  default     = {}
}

# VPC Principal
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = merge(var.tags, {
    Name = "${var.environment}-vpc"
    Type = "main"
  })
}

# Internet Gateway
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = merge(var.tags, {
    Name = "${var.environment}-igw"
  })
}

# Subredes públicas
resource "aws_subnet" "public" {
  count = min(length(var.availability_zones), 3)
  
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 8, count.index)
  availability_zone = var.availability_zones[count.index]
  
  map_public_ip_on_launch = true

  tags = merge(var.tags, {
    Name = "${var.environment}-public-${substr(var.availability_zones[count.index], -1, 1)}"
    Type = "public"
    "kubernetes.io/role/elb" = "1"
  })
}

# Subredes privadas
resource "aws_subnet" "private" {
  count = min(length(var.availability_zones), 3)
  
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 8, count.index + 10)
  availability_zone = var.availability_zones[count.index]

  tags = merge(var.tags, {
    Name = "${var.environment}-private-${substr(var.availability_zones[count.index], -1, 1)}"
    Type = "private"
    "kubernetes.io/role/internal-elb" = "1"
  })
}

# Subredes de base de datos
resource "aws_subnet" "database" {
  count = min(length(var.availability_zones), 3)
  
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 8, count.index + 20)
  availability_zone = var.availability_zones[count.index]

  tags = merge(var.tags, {
    Name = "${var.environment}-database-${substr(var.availability_zones[count.index], -1, 1)}"
    Type = "database"
  })
}

# Elastic IPs for NAT Gateways
resource "aws_eip" "nat" {
  count = var.enable_nat_gateway ? min(length(var.availability_zones), 3) : 0
  
  domain = "vpc"
  depends_on = [aws_internet_gateway.main]

  tags = merge(var.tags, {
    Name = "${var.environment}-nat-eip-${count.index + 1}"
  })
}

# NAT Gateways
resource "aws_nat_gateway" "main" {
  count = var.enable_nat_gateway ? min(length(var.availability_zones), 3) : 0
  
  allocation_id = aws_eip.nat[count.index].id
  subnet_id     = aws_subnet.public[count.index].id

  tags = merge(var.tags, {
    Name = "${var.environment}-nat-${count.index + 1}"
  })

  depends_on = [aws_internet_gateway.main]
}

# Route table para subredes públicas
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }

  tags = merge(var.tags, {
    Name = "${var.environment}-public-rt"
  })
}

# Asociación de route table para subredes públicas
resource "aws_route_table_association" "public" {
  count = min(length(var.availability_zones), 3)
  
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

# Route tables para subredes privadas
resource "aws_route_table" "private" {
  count = var.enable_nat_gateway ? min(length(var.availability_zones), 3) : 1
  
  vpc_id = aws_vpc.main.id

  dynamic "route" {
    for_each = var.enable_nat_gateway ? [1] : []
    content {
      cidr_block     = "0.0.0.0/0"
      nat_gateway_id = aws_nat_gateway.main[var.enable_nat_gateway ? count.index : 0].id
    }
  }

  tags = merge(var.tags, {
    Name = "${var.environment}-private-rt-${count.index + 1}"
  })
}

# Asociación de route tables para subredes privadas
resource "aws_route_table_association" "private" {
  count = min(length(var.availability_zones), 3)
  
  subnet_id      = aws_subnet.private[count.index].id
  route_table_id = aws_route_table.private[var.enable_nat_gateway ? count.index : 0].id
}

# DB Subnet Group
resource "aws_db_subnet_group" "main" {
  name       = "${var.environment}-db-subnet-group"
  subnet_ids = aws_subnet.database[*].id

  tags = merge(var.tags, {
    Name = "${var.environment}-db-subnet-group"
  })
}

# Cache Subnet Group
resource "aws_elasticache_subnet_group" "main" {
  name       = "${var.environment}-cache-subnet-group"
  subnet_ids = aws_subnet.private[*].id
  
  tags = var.tags
}

# VPC Endpoints para servicios AWS
resource "aws_vpc_endpoint" "s3" {
  vpc_id       = aws_vpc.main.id
  service_name = "com.amazonaws.${data.aws_region.current.name}.s3"
  
  tags = merge(var.tags, {
    Name = "${var.environment}-s3-endpoint"
  })
}

resource "aws_vpc_endpoint" "ec2" {
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${data.aws_region.current.name}.ec2"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.private[*].id
  security_group_ids  = [aws_security_group.vpc_endpoints.id]
  
  private_dns_enabled = true
  
  tags = merge(var.tags, {
    Name = "${var.environment}-ec2-endpoint"
  })
}

# Security Group para VPC Endpoints
resource "aws_security_group" "vpc_endpoints" {
  name_prefix = "${var.environment}-vpc-endpoints-"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [var.vpc_cidr]
  }

  tags = merge(var.tags, {
    Name = "${var.environment}-vpc-endpoints-sg"
  })
}

# Data source para obtener la región actual
data "aws_region" "current" {}

# Outputs
output "vpc_id" {
  description = "ID of the VPC"
  value       = aws_vpc.main.id
}

output "vpc_cidr_block" {
  description = "CIDR block of the VPC"
  value       = aws_vpc.main.cidr_block
}

output "public_subnet_ids" {
  description = "List of IDs of public subnets"
  value       = aws_subnet.public[*].id
}

output "private_subnet_ids" {
  description = "List of IDs of private subnets"
  value       = aws_subnet.private[*].id
}

output "database_subnet_ids" {
  description = "List of IDs of database subnets"
  value       = aws_subnet.database[*].id
}

output "database_subnet_group_name" {
  description = "Name of the database subnet group"
  value       = aws_db_subnet_group.main.name
}

output "cache_subnet_group_name" {
  description = "Name of the cache subnet group"
  value       = aws_elasticache_subnet_group.main.name
}

output "internet_gateway_id" {
  description = "ID of the Internet Gateway"
  value       = aws_internet_gateway.main.id
}

output "nat_gateway_ids" {
  description = "List of IDs of the NAT Gateways"
  value       = aws_nat_gateway.main[*].id
}

Ansible para Configuración y Compliance

Playbook Empresarial de Hardening

# playbooks/enterprise-hardening.yml
---
- name: Enterprise Security Hardening
  hosts: all
  become: true
  vars:
    # Configuración de cumplimiento
    compliance_level: "high"
    audit_logs_retention_days: 90
    failed_login_attempts_limit: 3
    password_min_length: 14
    password_complexity: true
    
    # Servicios a deshabilitar
    disabled_services:
      - telnet
      - rsh
      - rcp
      - rlogin
      - ypbind
      - tftp
      - xinetd
      - chargen-dgram
      - chargen-stream
      - daytime-dgram
      - daytime-stream
      - discard-dgram
      - discard-stream
      - echo-dgram
      - echo-stream
      - time-dgram
      - time-stream
    
    # Configuración de firewall
    firewall_allowed_ports:
      - 22/tcp   # SSH
      - 80/tcp   # HTTP
      - 443/tcp  # HTTPS
      
    # Paquetes de seguridad requeridos
    required_security_packages:
      - aide
      - auditd
      - rsyslog
      - logrotate
      - fail2ban
      - rkhunter
      - chkrootkit

  tasks:
    # ============ HARDENING DEL SISTEMA OPERATIVO ============
    
    - name: Aplicar actualizaciones de seguridad
      package:
        name: "*"
        state: latest
      when: ansible_facts['os_family'] in ['RedHat', 'Debian']
      tags: [security, updates]

    - name: Instalar paquetes de seguridad esenciales
      package:
        name: "{{ required_security_packages }}"
        state: present
      tags: [security, packages]

    # ============ CONFIGURACIÓN DE USUARIOS Y ACCESOS ============
    
    - name: Configurar política de contraseñas
      lineinfile:
        path: /etc/security/pwquality.conf
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
        create: yes
      loop:
        - { regexp: '^minlen', line: 'minlen = {{ password_min_length }}' }
        - { regexp: '^dcredit', line: 'dcredit = -1' }
        - { regexp: '^ucredit', line: 'ucredit = -1' }
        - { regexp: '^lcredit', line: 'lcredit = -1' }
        - { regexp: '^ocredit', line: 'ocredit = -1' }
        - { regexp: '^difok', line: 'difok = 3' }
        - { regexp: '^maxrepeat', line: 'maxrepeat = 2' }
        - { regexp: '^maxclassrepeat', line: 'maxclassrepeat = 2' }
      tags: [security, passwords]

    - name: Configurar bloqueo de cuentas por intentos fallidos
      lineinfile:
        path: /etc/security/faillock.conf
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
        create: yes
      loop:
        - { regexp: '^deny', line: 'deny = {{ failed_login_attempts_limit }}' }
        - { regexp: '^unlock_time', line: 'unlock_time = 900' }
        - { regexp: '^fail_interval', line: 'fail_interval = 900' }
      tags: [security, access_control]

    - name: Eliminar usuarios del sistema innecesarios
      user:
        name: "{{ item }}"
        state: absent
        remove: yes
      loop:
        - games
        - news
        - gopher
        - ftp
      ignore_errors: yes
      tags: [security, users]

    - name: Configurar timeout para sesiones inactivas
      lineinfile:
        path: /etc/profile
        line: "{{ item }}"
        create: yes
      loop:
        - "TMOUT=600"
        - "readonly TMOUT"
        - "export TMOUT"
      tags: [security, sessions]

    # ============ CONFIGURACIÓN DE RED Y FIREWALL ============
    
    - name: Deshabilitar servicios de red inseguros
      systemd:
        name: "{{ item }}"
        state: stopped
        enabled: no
      loop: "{{ disabled_services }}"
      ignore_errors: yes
      tags: [security, services]

    - name: Configurar parámetros de red seguros (sysctl)
      sysctl:
        name: "{{ item.name }}"
        value: "{{ item.value }}"
        state: present
        reload: yes
      loop:
        - { name: 'net.ipv4.ip_forward', value: '0' }
        - { name: 'net.ipv4.conf.all.send_redirects', value: '0' }
        - { name: 'net.ipv4.conf.default.send_redirects', value: '0' }
        - { name: 'net.ipv4.conf.all.accept_source_route', value: '0' }
        - { name: 'net.ipv4.conf.default.accept_source_route', value: '0' }
        - { name: 'net.ipv4.conf.all.accept_redirects', value: '0' }
        - { name: 'net.ipv4.conf.default.accept_redirects', value: '0' }
        - { name: 'net.ipv4.conf.all.secure_redirects', value: '0' }
        - { name: 'net.ipv4.conf.default.secure_redirects', value: '0' }
        - { name: 'net.ipv4.conf.all.log_martians', value: '1' }
        - { name: 'net.ipv4.conf.default.log_martians', value: '1' }
        - { name: 'net.ipv4.icmp_echo_ignore_broadcasts', value: '1' }
        - { name: 'net.ipv4.icmp_ignore_bogus_error_responses', value: '1' }
        - { name: 'net.ipv4.conf.all.rp_filter', value: '1' }
        - { name: 'net.ipv4.conf.default.rp_filter', value: '1' }
        - { name: 'net.ipv4.tcp_syncookies', value: '1' }
      tags: [security, network]

    - name: Instalar y configurar fail2ban
      package:
        name: fail2ban
        state: present
      tags: [security, intrusion_prevention]

    - name: Configurar fail2ban para SSH
      copy:
        content: |
          [sshd]
          enabled = true
          port = ssh
          filter = sshd
          logpath = /var/log/auth.log
          maxretry = {{ failed_login_attempts_limit }}
          bantime = 3600
          findtime = 600
        dest: /etc/fail2ban/jail.local
        backup: yes
      notify: restart fail2ban
      tags: [security, intrusion_prevention]

    # ============ CONFIGURACIÓN DE AUDITORÍA Y LOGGING ============
    
    - name: Configurar auditd para auditoría del sistema
      lineinfile:
        path: /etc/audit/auditd.conf
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
      loop:
        - { regexp: '^max_log_file_action', line: 'max_log_file_action = rotate' }
        - { regexp: '^num_logs', line: 'num_logs = 10' }
        - { regexp: '^max_log_file', line: 'max_log_file = 100' }
        - { regexp: '^space_left_action', line: 'space_left_action = email' }
        - { regexp: '^admin_space_left_action', line: 'admin_space_left_action = halt' }
      tags: [security, auditing]

    - name: Configurar reglas de auditoría críticas
      copy:
        content: |
          # Auditar cambios en archivos de configuración críticos
          -w /etc/passwd -p wa -k passwd_changes
          -w /etc/group -p wa -k group_changes
          -w /etc/shadow -p wa -k shadow_changes
          -w /etc/sudoers -p wa -k sudo_changes
          
          # Auditar llamadas del sistema críticas
          -a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time_change
          -a always,exit -F arch=b64 -S clock_settime -k time_change
          -a always,exit -F arch=b64 -S unlink -S unlinkat -S rename -S renameat -k delete
          -a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -k perm_mod
          -a always,exit -F arch=b64 -S chown -S fchown -S fchownat -S lchown -k perm_mod
          
          # Auditar montajes y desmontajes
          -a always,exit -F arch=b64 -S mount -k mounts
          
          # Auditar intentos de acceso a archivos sin éxito
          -a always,exit -F arch=b64 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EACCES -k access
          -a always,exit -F arch=b64 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EPERM -k access
          
          # Auditar uso de comandos privilegiados
          -a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
          -a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
          
          # Inmutable
          -e 2
        dest: /etc/audit/rules.d/custom.rules
        backup: yes
      notify: restart auditd
      tags: [security, auditing]

    - name: Configurar rotación de logs
      copy:
        content: |
          /var/log/auth.log {
              rotate {{ audit_logs_retention_days }}
              daily
              missingok
              notifempty
              compress
              delaycompress
              create 640 root adm
              postrotate
                  /usr/lib/rsyslog/rsyslog-rotate
              endscript
          }
          
          /var/log/syslog {
              rotate 30
              daily
              missingok
              notifempty
              compress
              delaycompress
              create 640 root adm
              postrotate
                  /usr/lib/rsyslog/rsyslog-rotate
              endscript
          }
        dest: /etc/logrotate.d/custom-security
        backup: yes
      tags: [security, logging]

    # ============ DETECCIÓN DE INTRUSOS Y MALWARE ============
    
    - name: Inicializar base de datos AIDE
      command: aideinit
      args:
        creates: /var/lib/aide/aide.db.new.gz
      tags: [security, intrusion_detection]

    - name: Mover base de datos AIDE inicializada
      command: mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
      args:
        creates: /var/lib/aide/aide.db.gz
        removes: /var/lib/aide/aide.db.new.gz
      tags: [security, intrusion_detection]

    - name: Configurar cron para verificaciones AIDE diarias
      cron:
        name: "AIDE integrity check"
        minute: "0"
        hour: "2"
        job: "/usr/bin/aide --check | mail -s 'AIDE Integrity Report' root@localhost"
        user: root
      tags: [security, intrusion_detection]

    - name: Configurar rkhunter
      lineinfile:
        path: /etc/rkhunter.conf
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
      loop:
        - { regexp: '^UPDATE_MIRRORS', line: 'UPDATE_MIRRORS=1' }
        - { regexp: '^MIRRORS_MODE', line: 'MIRRORS_MODE=0' }
        - { regexp: '^WEB_CMD', line: 'WEB_CMD=""' }
      tags: [security, malware_detection]

    - name: Configurar cron para escaneos rkhunter semanales
      cron:
        name: "rkhunter weekly scan"
        minute: "0"
        hour: "3"
        weekday: "0"
        job: "/usr/bin/rkhunter --update && /usr/bin/rkhunter --cronjob --report-warnings-only | mail -s 'rkhunter Weekly Report' root@localhost"
        user: root
      tags: [security, malware_detection]

    # ============ CONFIGURACIÓN DE SSH SEGURO ============
    
    - name: Configurar SSH de forma segura
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
        backup: yes
      loop:
        - { regexp: '^Protocol', line: 'Protocol 2' }
        - { regexp: '^LogLevel', line: 'LogLevel VERBOSE' }
        - { regexp: '^X11Forwarding', line: 'X11Forwarding no' }
        - { regexp: '^MaxAuthTries', line: 'MaxAuthTries {{ failed_login_attempts_limit }}' }
        - { regexp: '^IgnoreRhosts', line: 'IgnoreRhosts yes' }
        - { regexp: '^HostbasedAuthentication', line: 'HostbasedAuthentication no' }
        - { regexp: '^PermitRootLogin', line: 'PermitRootLogin no' }
        - { regexp: '^PermitEmptyPasswords', line: 'PermitEmptyPasswords no' }
        - { regexp: '^PermitUserEnvironment', line: 'PermitUserEnvironment no' }
        - { regexp: '^Ciphers', line: 'Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr' }
        - { regexp: '^MACs', line: 'MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512' }
        - { regexp: '^KexAlgorithms', line: 'KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256' }
        - { regexp: '^ClientAliveInterval', line: 'ClientAliveInterval 600' }
        - { regexp: '^ClientAliveCountMax', line: 'ClientAliveCountMax 0' }
        - { regexp: '^LoginGraceTime', line: 'LoginGraceTime 60' }
        - { regexp: '^Banner', line: 'Banner /etc/issue.net' }
      notify: restart sshd
      tags: [security, ssh]

    - name: Crear banner de seguridad
      copy:
        content: |
          ******************************************************************************
          *                                AVISO LEGAL                                *
          ******************************************************************************
          
          SISTEMA PRIVADO - ACCESO AUTORIZADO ÚNICAMENTE
          
          Este sistema es propiedad privada y está destinado únicamente para uso 
          autorizado. El acceso no autorizado está prohibido y será perseguido 
          según la ley. Todas las actividades en este sistema son monitoreadas 
          y registradas.
          
          Al continuar, usted reconoce que:
          - Su actividad será monitoreada y registrada
          - El uso no autorizado puede resultar en acciones legales
          - No tiene expectativas de privacidad en este sistema
          
          ******************************************************************************
        dest: /etc/issue.net
        mode: '0644'
      tags: [security, ssh]

  handlers:
    - name: restart fail2ban
      systemd:
        name: fail2ban
        state: restarted
        enabled: yes

    - name: restart auditd
      command: service auditd restart
      
    - name: restart sshd
      systemd:
        name: sshd
        state: restarted

    # ============ VALIDACIÓN POST-HARDENING ============
    
    - name: Verificar configuración de seguridad
      block:
        - name: Verificar que fail2ban esté corriendo
          command: fail2ban-client status
          register: fail2ban_status
          changed_when: false

        - name: Verificar que auditd esté corriendo
          command: auditctl -s
          register: auditd_status
          changed_when: false

        - name: Verificar configuración SSH
          command: sshd -t
          register: ssh_config
          changed_when: false

        - name: Mostrar resultados de verificación
          debug:
            msg: |
              Fail2ban status: {{ 'OK' if fail2ban_status.rc == 0 else 'ERROR' }}
              Auditd status: {{ 'OK' if auditd_status.rc == 0 else 'ERROR' }}
              SSH config: {{ 'OK' if ssh_config.rc == 0 else 'ERROR' }}
      tags: [security, validation]

Gestión de Aplicaciones con Ansible

# playbooks/application-deployment.yml
---
- name: Deploy Application Stack
  hosts: kubernetes_masters
  vars:
    app_name: "myapp"
    app_version: "{{ app_version | default('latest') }}"
    namespace: "{{ app_namespace | default('default') }}"
    replicas: "{{ app_replicas | default(3) }}"
    
    # Configuración de base de datos
    database_host: "{{ database_endpoint }}"
    database_port: 5432
    database_name: "myapp"
    
    # Configuración de Redis
    redis_host: "{{ redis_endpoint }}"
    redis_port: 6379

  tasks:
    - name: Create namespace
      kubernetes.core.k8s:
        name: "{{ namespace }}"
        api_version: v1
        kind: Namespace
        state: present

    - name: Create database secret
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: v1
          kind: Secret
          metadata:
            name: "{{ app_name }}-db-secret"
            namespace: "{{ namespace }}"
          type: Opaque
          data:
            username: "{{ database_username | b64encode }}"
            password: "{{ database_password | b64encode }}"
            host: "{{ database_host | b64encode }}"
            port: "{{ database_port | string | b64encode }}"
            database: "{{ database_name | b64encode }}"

    - name: Deploy application
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: "{{ app_name }}"
            namespace: "{{ namespace }}"
            labels:
              app: "{{ app_name }}"
              version: "{{ app_version }}"
          spec:
            replicas: "{{ replicas }}"
            selector:
              matchLabels:
                app: "{{ app_name }}"
            template:
              metadata:
                labels:
                  app: "{{ app_name }}"
                  version: "{{ app_version }}"
              spec:
                containers:
                - name: "{{ app_name }}"
                  image: "myregistry/{{ app_name }}:{{ app_version }}"
                  ports:
                  - containerPort: 8080
                    name: http
                  env:
                  - name: DATABASE_URL
                    valueFrom:
                      secretKeyRef:
                        name: "{{ app_name }}-db-secret"
                        key: url
                  - name: REDIS_URL
                    value: "redis://{{ redis_host }}:{{ redis_port }}"
                  - name: ENVIRONMENT
                    value: "{{ environment }}"
                  resources:
                    requests:
                      memory: "{{ app_memory_request | default('256Mi') }}"
                      cpu: "{{ app_cpu_request | default('250m') }}"
                    limits:
                      memory: "{{ app_memory_limit | default('512Mi') }}"
                      cpu: "{{ app_cpu_limit | default('500m') }}"
                  livenessProbe:
                    httpGet:
                      path: /health
                      port: 8080
                    initialDelaySeconds: 30
                    periodSeconds: 10
                  readinessProbe:
                    httpGet:
                      path: /ready
                      port: 8080
                    initialDelaySeconds: 5
                    periodSeconds: 5

    - name: Create service
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: v1
          kind: Service
          metadata:
            name: "{{ app_name }}-service"
            namespace: "{{ namespace }}"
          spec:
            selector:
              app: "{{ app_name }}"
            ports:
            - port: 80
              targetPort: 8080
              protocol: TCP
            type: ClusterIP

    - name: Create ingress
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: networking.k8s.io/v1
          kind: Ingress
          metadata:
            name: "{{ app_name }}-ingress"
            namespace: "{{ namespace }}"
            annotations:
              kubernetes.io/ingress.class: "nginx"
              cert-manager.io/cluster-issuer: "letsencrypt-prod"
              nginx.ingress.kubernetes.io/rewrite-target: /
          spec:
            tls:
            - hosts:
              - "{{ app_domain }}"
              secretName: "{{ app_name }}-tls"
            rules:
            - host: "{{ app_domain }}"
              http:
                paths:
                - path: /
                  pathType: Prefix
                  backend:
                    service:
                      name: "{{ app_name }}-service"
                      port:
                        number: 80

CI/CD Pipeline con GitLab CI/CD

# .gitlab-ci.yml - Pipeline completo de IaC
stages:
  - validate
  - security-scan
  - plan
  - deploy
  - test
  - cleanup

variables:
  TF_ROOT: ${CI_PROJECT_DIR}/terraform
  TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/production
  ANSIBLE_HOST_KEY_CHECKING: "False"
  ANSIBLE_RETRY_FILES_ENABLED: "False"

cache:
  key: "${CI_COMMIT_REF_SLUG}"
  paths:
    - ${TF_ROOT}/.terraform

before_script:
  - cd ${TF_ROOT}
  - terraform --version
  - terraform init

# ========== VALIDACIÓN ==========

validate:terraform:
  stage: validate
  script:
    - terraform validate
    - terraform fmt -check
  only:
    - merge_requests
    - main

validate:ansible:
  stage: validate
  image: willhallonline/ansible:2.12-alpine
  script:
    - cd ansible
    - ansible-playbook --syntax-check playbooks/*.yml
    - ansible-lint playbooks/
  only:
    - merge_requests
    - main

# ========== ESCANEO DE SEGURIDAD ==========

security:terraform:
  stage: security-scan
  image: bridgecrew/checkov:latest
  script:
    - checkov -d ${TF_ROOT} --framework terraform --output cli --output junitxml --output-file-path console,results.xml
  artifacts:
    reports:
      junit: results.xml
  only:
    - merge_requests
    - main

security:secrets:
  stage: security-scan
  image: trufflesecurity/trufflehog:latest
  script:
    - trufflehog git https://gitlab.com/${CI_PROJECT_PATH}.git --branch ${CI_COMMIT_REF_NAME} --only-verified
  allow_failure: true
  only:
    - merge_requests
    - main

# ========== PLANIFICACIÓN ==========

plan:production:
  stage: plan
  script:
    - terraform plan -var-file="environments/production.tfvars" -out="production.tfplan"
    - terraform show -json production.tfplan > production-plan.json
  artifacts:
    name: production-plan
    paths:
      - ${TF_ROOT}/production.tfplan
      - ${TF_ROOT}/production-plan.json
    expire_in: 7 days
  only:
    - main

plan:staging:
  stage: plan
  script:
    - terraform plan -var-file="environments/staging.tfvars" -out="staging.tfplan"
  artifacts:
    name: staging-plan
    paths:
      - ${TF_ROOT}/staging.tfplan
    expire_in: 7 days
  only:
    - merge_requests

# ========== DESPLIEGUE ==========

deploy:staging:
  stage: deploy
  script:
    - terraform apply -auto-approve staging.tfplan
    - terraform output -json > staging-outputs.json
  artifacts:
    name: staging-outputs
    paths:
      - ${TF_ROOT}/staging-outputs.json
    expire_in: 1 day
  environment:
    name: staging
    url: https://staging.myapp.com
  only:
    - merge_requests
  dependencies:
    - plan:staging

deploy:production:
  stage: deploy
  script:
    - terraform apply -auto-approve production.tfplan
    - terraform output -json > production-outputs.json
  artifacts:
    name: production-outputs
    paths:
      - ${TF_ROOT}/production-outputs.json
    expire_in: 30 days
  environment:
    name: production
    url: https://myapp.com
  when: manual
  only:
    - main
  dependencies:
    - plan:production

# ========== CONFIGURACIÓN CON ANSIBLE ==========

configure:staging:
  stage: deploy
  image: willhallonline/ansible:2.12-alpine
  script:
    - cd ansible
    - ansible-galaxy install -r requirements.yml
    - ansible-playbook -i inventories/staging playbooks/site.yml
  environment:
    name: staging
  only:
    - merge_requests
  dependencies:
    - deploy:staging

configure:production:
  stage: deploy
  image: willhallonline/ansible:2.12-alpine
  script:
    - cd ansible
    - ansible-galaxy install -r requirements.yml
    - ansible-playbook -i inventories/production playbooks/site.yml
  environment:
    name: production
  when: manual
  only:
    - main
  dependencies:
    - deploy:production

# ========== TESTING ==========

test:infrastructure:
  stage: test
  image: python:3.9
  script:
    - pip install pytest requests boto3
    - cd tests
    - pytest infrastructure/ -v --junitxml=report.xml
  artifacts:
    reports:
      junit: tests/report.xml
  only:
    - merge_requests
    - main

test:security:
  stage: test
  image: willhallonline/ansible:2.12-alpine
  script:
    - cd ansible
    - ansible-playbook -i inventories/staging playbooks/security-validation.yml
  only:
    - merge_requests
    - main

# ========== CLEANUP ==========

cleanup:staging:
  stage: cleanup
  script:
    - terraform destroy -var-file="environments/staging.tfvars" -auto-approve
  when: manual
  environment:
    name: staging
    action: stop
  only:
    - merge_requests

Patrones Avanzados y Mejores Prácticas

GitOps con ArgoCD

# applications/production-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-production
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: production
  source:
    repoURL: https://gitlab.com/myorg/myapp-config.git
    targetRevision: HEAD
    path: environments/production
    helm:
      valueFiles:
        - values.yaml
        - values-production.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allowEmpty: false
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
      - PruneLast=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m
  revisionHistoryLimit: 10

---
# Multi-cluster deployment
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-multi-cluster
  namespace: argocd
spec:
  generators:
  - clusters:
      selector:
        matchLabels:
          environment: production
  template:
    metadata:
      name: '{{ name }}-myapp'
    spec:
      project: production
      source:
        repoURL: https://gitlab.com/myorg/myapp-config.git
        targetRevision: HEAD
        path: environments/production
        helm:
          valueFiles:
            - values.yaml
            - 'values-{{ name }}.yaml'
      destination:
        server: '{{ server }}'
        namespace: production
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

Testing de Infraestructura con Terratest

// tests/terraform_test.go
package test

import (
    "testing"
    "time"

    "github.com/gruntwork-io/terratest/modules/aws"
    "github.com/gruntwork-io/terratest/modules/terraform"
    "github.com/stretchr/testify/assert"
)

func TestTerraformInfrastructure(t *testing.T) {
    t.Parallel()

    // Configuración de Terraform
    terraformOptions := &terraform.Options{
        TerraformDir: "../terraform",
        VarFiles:     []string{"environments/test.tfvars"},
        Vars: map[string]interface{}{
            "environment": "test",
        },
    }

    // Cleanup al final del test
    defer terraform.Destroy(t, terraformOptions)

    // Ejecutar terraform init y apply
    terraform.InitAndApply(t, terraformOptions)

    // Obtener outputs
    vpcId := terraform.Output(t, terraformOptions, "vpc_id")
    clusterName := terraform.Output(t, terraformOptions, "eks_cluster_name")
    
    // Validar que la VPC fue creada
    assert.NotEmpty(t, vpcId)
    
    // Validar que el cluster EKS está funcionando
    aws.WaitForEksClusterToBeReady(t, "us-west-2", clusterName, 10*time.Minute)
    
    // Validar configuración de seguridad
    validateSecurityGroups(t, vpcId)
    validateNetworkAcls(t, vpcId)
}

func validateSecurityGroups(t *testing.T, vpcId string) {
    // Obtener security groups de la VPC
    securityGroups := aws.GetSecurityGroupsForVpc(t, vpcId, "us-west-2")
    
    // Validar que no hay reglas inseguras
    for _, sg := range securityGroups {
        for _, rule := range sg.IpPermissions {
            for _, ipRange := range rule.IpRanges {
                if *ipRange.CidrIp == "0.0.0.0/0" {
                    // Solo permitir puertos seguros abiertos al mundo
                    allowedPorts := []int64{80, 443}
                    found := false
                    for _, port := range allowedPorts {
                        if *rule.FromPort == port {
                            found = true
                            break
                        }
                    }
                    assert.True(t, found, "Insecure rule found: port %d open to 0.0.0.0/0", *rule.FromPort)
                }
            }
        }
    }
}

Monitorización y Observabilidad de IaC

Prometheus Monitoring de Terraform

# monitoring/terraform-exporter.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: terraform-exporter
  namespace: monitoring
spec:
  replicas: 1
  selector:
    matchLabels:
      app: terraform-exporter
  template:
    metadata:
      labels:
        app: terraform-exporter
    spec:
      containers:
      - name: terraform-exporter
        image: camptocamp/terraform-exporter:latest
        ports:
        - containerPort: 9100
        env:
        - name: TF_STATE_URL
          value: "s3://mi-empresa-terraform-state/infrastructure/terraform.tfstate"
        - name: AWS_REGION
          value: "us-west-2"
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"

---
apiVersion: v1
kind: Service
metadata:
  name: terraform-exporter
  namespace: monitoring
  labels:
    app: terraform-exporter
spec:
  ports:
  - port: 9100
    targetPort: 9100
  selector:
    app: terraform-exporter

---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: terraform-exporter
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: terraform-exporter
  endpoints:
  - port: http
    interval: 30s
    path: /metrics

Alertas de Drift de Infraestructura

# monitoring/terraform-alerts.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: terraform-alerts
  namespace: monitoring
spec:
  groups:
  - name: terraform.rules
    rules:
    - alert: TerraformDriftDetected
      expr: terraform_resource_drift > 0
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "Terraform drift detected"
        description: "Infrastructure drift detected in {{ $labels.resource_type }}.{{ $labels.resource_name }}"

    - alert: TerraformStateLocked
      expr: terraform_state_locked == 1
      for: 15m
      labels:
        severity: critical
      annotations:
        summary: "Terraform state locked for too long"
        description: "Terraform state has been locked for more than 15 minutes"

    - alert: TerraformPlanChanges
      expr: increase(terraform_plan_changes_total[1h]) > 0
      labels:
        severity: info
      annotations:
        summary: "Terraform plan detected changes"
        description: "{{ $value }} changes detected in last hour"

Casos de Uso Empresariales

Multi-Region Disaster Recovery

# disaster-recovery/multi-region.tf
# Configuración de DR multi-región con replicación automática

locals {
  regions = {
    primary = {
      name = "us-west-2"
      azs  = ["us-west-2a", "us-west-2b", "us-west-2c"]
    }
    dr = {
      name = "us-east-1"
      azs  = ["us-east-1a", "us-east-1b", "us-east-1c"]
    }
  }
}

# Desplegar infraestructura en región primaria
module "primary_region" {
  source = "../modules/region-infrastructure"
  
  region = local.regions.primary.name
  azs    = local.regions.primary.azs
  
  environment = var.environment
  is_primary  = true
  
  # Configuración específica de región primaria
  database_backup_retention = 35
  enable_cross_region_backup = true
  dr_region = local.regions.dr.name
  
  tags = local.common_tags
}

# Desplegar infraestructura en región DR
module "dr_region" {
  source = "../modules/region-infrastructure"
  
  region = local.regions.dr.name
  azs    = local.regions.dr.azs
  
  environment = "${var.environment}-dr"
  is_primary  = false
  
  # Configuración específica de región DR
  database_backup_retention = 7
  enable_read_replica = true
  primary_database_arn = module.primary_region.database_arn
  
  tags = merge(local.common_tags, {
    Purpose = "DisasterRecovery"
  })
}

# Route 53 Health Checks y Failover
resource "aws_route53_health_check" "primary" {
  fqdn                            = module.primary_region.load_balancer_dns
  port                            = 443
  type                            = "HTTPS"
  resource_path                   = "/health"
  failure_threshold               = 3
  request_interval                = 30

  tags = merge(local.common_tags, {
    Name = "${var.environment}-primary-health-check"
  })
}

resource "aws_route53_record" "primary" {
  zone_id = data.aws_route53_zone.main.zone_id
  name    = "app.${var.domain_name}"
  type    = "A"

  set_identifier = "primary"
  failover_routing_policy {
    type = "PRIMARY"
  }
  
  health_check_id = aws_route53_health_check.primary.id

  alias {
    name                   = module.primary_region.load_balancer_dns
    zone_id                = module.primary_region.load_balancer_zone_id
    evaluate_target_health = true
  }
}

resource "aws_route53_record" "secondary" {
  zone_id = data.aws_route53_zone.main.zone_id
  name    = "app.${var.domain_name}"
  type    = "A"

  set_identifier = "secondary"
  failover_routing_policy {
    type = "SECONDARY"
  }

  alias {
    name                   = module.dr_region.load_balancer_dns
    zone_id                = module.dr_region.load_balancer_zone_id
    evaluate_target_health = true
  }
}

Compliance Automation (PCI-DSS)

# compliance/pci-dss-automation.yml
---
- name: PCI-DSS Compliance Automation
  hosts: all
  become: true
  vars:
    pci_dss_requirements:
      - req_2_default_passwords
      - req_2_2_system_hardening
      - req_6_2_vulnerability_management
      - req_8_access_control
      - req_10_logging_monitoring
    
    compliance_scan_schedule: "daily"
    alert_email: "security@company.com"

  pre_tasks:
    - name: Verify PCI-DSS scope
      assert:
        that:
          - "'pci_scope' in group_names"
        fail_msg: "This playbook should only run on PCI-DSS scoped systems"

  roles:
    - role: pci_dss_hardening
      vars:
        enable_aide: true
        enable_auditd: true
        password_policy: "strict"
    
    - role: vulnerability_scanner
      vars:
        scan_frequency: "{{ compliance_scan_schedule }}"
        report_email: "{{ alert_email }}"
    
    - role: log_aggregation
      vars:
        siem_endpoint: "https://siem.company.com"
        retention_days: 365

  post_tasks:
    - name: Generate compliance report
      template:
        src: compliance_report.j2
        dest: "/var/log/pci-compliance-{{ ansible_date_time.epoch }}.json"
        mode: '0600'
      
    - name: Upload compliance report to central system
      uri:
        url: "https://compliance.company.com/api/reports"
        method: POST
        body: "{{ lookup('file', '/var/log/pci-compliance-' + ansible_date_time.epoch + '.json') }}"
        headers:
          Content-Type: "application/json"
          Authorization: "Bearer {{ compliance_api_token }}"

Conclusión y Tendencias Futuras

La automatización de infraestructura con IaC ha evolucionado de ser una práctica recomendada a una necesidad crítica para la supervivencia competitiva. Las organizaciones que dominan estas herramientas y patrones pueden:

Beneficios Cuantificables Demostrados

  • Velocidad: Reducción del 95% en tiempo de aprovisionamiento
  • Confiabilidad: 90% menos errores de configuración
  • Costos: 30-45% de optimización en gastos de infraestructura
  • Seguridad: Compliance automático y auditable
  • Escalabilidad: Capacidad para manejar millones de recursos

Tendencias Emergentes

  1. Infrastructure AI/ML: Optimización autónoma basada en patrones
  2. Policy as Code: Governance automatizada y compliance continuo
  3. Cross-Cloud Abstraction: APIs unificadas para multi-cloud
  4. Sustainable Infrastructure: Optimización automática para huella de carbono

El futuro de IaC está en la infraestructura autónoma que se auto-gestiona, auto-optimiza y auto-protege. Las organizaciones que adopten estos patrones avanzados tendrán una ventaja competitiva significativa en la era digital.

La inversión en automatización de infraestructura no es opcional: es la diferencia entre liderar y quedar obsoleto.