Files
2026-04-14 14:53:05 -06:00

9.3 KiB

07 - Terraform (IaC)

Cómo creamos la infraestructura de proyectosacc con código.


1. ¿Qué es Terraform?

Terraform es una herramienta que permite crear infraestructura en la nube escribiendo código en lugar de hacer clic en la consola de AWS.

A esto se le llama IaC (Infrastructure as Code) o "Infraestructura como Código".

Ventajas de usar Terraform

  • Repetible: puedes crear la misma infraestructura 10 veces y siempre será idéntica.
  • Versionable: el código se guarda en Git, así que sabes quién hizo qué cambio y cuándo.
  • Rápido: un solo comando crea servidor, base de datos, DNS y seguridad.
  • Sin sorpresas: todo está documentado en archivos de texto.

2. ¿Qué crea Terraform en proyectosacc?

Terraform se encarga de crear todos estos recursos en AWS:

  1. VPC y subredes: la red virtual donde vive todo.
  2. Security Groups: las reglas de firewall.
  3. EC2 T3.small: el servidor de la API backend.
  4. RDS MariaDB: la base de datos gestionada.
  5. S3 Bucket (artefactos): el almacén de archivos de la API.
  6. S3 Bucket (frontend): el sitio web estático que sirve el React app.
  7. CloudFront Distribution: la CDN que distribuye el frontend globalmente con HTTPS.
  8. Route 53 Record: el dominio sacc.ccsoft.mx apunta a CloudFront.
  9. ACM Certificate: certificado SSL para CloudFront.
  10. IAM Role: permisos para que la EC2 y CloudFront accedan a S3.

3. Archivos principales de Terraform

main.tf

Este es el archivo central. Aquí se definen los recursos que Terraform va a crear.

Ejemplo de la EC2 para la API:

resource "aws_instance" "sacc_api" {
  ami           = "ami-0c02fb55956c7d316"  # Ubuntu 22.04
  instance_type = "t3.small"

  vpc_security_group_ids = [aws_security_group.sacc_sg.id]

  user_data = file("user-data.sh")

  tags = {
    Name        = "proyectosacc-api"
    Environment = "develop"
    Project     = "proyectosacc"
  }
}

Ejemplo del bucket S3 para el frontend React:

resource "aws_s3_bucket" "sacc_frontend" {
  bucket = "ccsoft-proyectosacc-frontend"
}

resource "aws_s3_bucket_website_configuration" "sacc_frontend" {
  bucket = aws_s3_bucket.sacc_frontend.id

  index_document {
    suffix = "index.html"
  }

  error_document {
    key = "index.html"
  }
}

Ejemplo de CloudFront:

resource "aws_cloudfront_distribution" "sacc_cdn" {
  enabled             = true
  default_root_object = "index.html"
  aliases             = ["sacc.ccsoft.mx"]

  # Origin 1: S3 bucket para el frontend React
  origin {
    domain_name = aws_s3_bucket.sacc_frontend.bucket_regional_domain_name
    origin_id   = "saccS3Origin"

    s3_origin_config {
      origin_access_identity = aws_cloudfront_origin_access_identity.sacc_oai.cloudfront_access_identity_path
    }
  }

  # Origin 2: EC2 para la API backend
  origin {
    domain_name = aws_instance.sacc_api.public_dns
    origin_id   = "saccApiOrigin"

    custom_origin_config {
      http_port              = 80
      https_port             = 443
      origin_protocol_policy = "http-only"
      origin_ssl_protocols   = ["TLSv1.2"]
    }
  }

  default_cache_behavior {
    target_origin_id       = "saccS3Origin"
    viewer_protocol_policy = "redirect-to-https"
    allowed_methods        = ["GET", "HEAD", "OPTIONS"]
    cached_methods         = ["GET", "HEAD"]
  }

  # API requests go to the EC2 origin
  ordered_cache_behavior {
    path_pattern     = "/api/*"
    target_origin_id = "saccApiOrigin"
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    viewer_protocol_policy = "redirect-to-https"

    forwarded_values {
      query_string = true
      headers      = ["Origin", "Access-Control-Request-Headers", "Access-Control-Request-Method"]
      cookies {
        forward = "all"
      }
    }
  }

  viewer_certificate {
    acm_certificate_arn      = aws_acm_certificate.sacc_cert.arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }
}

💡 ¿Por qué dos origins? CloudFront sirve el frontend desde S3 por defecto. Pero cuando el navegador pide algo bajo /api/*, CloudFront lo envía a la EC2 donde Nginx lo redirige al backend en el puerto 8080. Así todo comparte el mismo dominio sacc.ccsoft.mx.


variables.tf

Aquí se definen las variables que personalizan la infraestructura.

Ejemplo:

variable "region" {
  description = "Región de AWS"
  type        = string
  default     = "us-east-1"
}

variable "instance_type" {
  description = "Tipo de instancia EC2"
  type        = string
  default     = "t3.small"
}

variable "db_password" {
  description = "Contraseña de la base de datos"
  type        = string
  sensitive   = true
}

⚠️ La variable db_password tiene sensitive = true para que no aparezca en los logs.


user-data.sh

Este archivo contiene los comandos que la EC2 ejecuta la primera vez que se enciende.

Ejemplo de lo que hace:

  1. Actualiza el sistema operativo.
  2. Instala Nginx.
  3. Instala Java (para correr la aplicación SACC).
  4. Instala AWS CLI.
  5. Crea los usuarios thoth y osiris.
  6. Inyecta la llave pública SSH dedicada para el pipeline en ~/.ssh/authorized_keys del usuario thoth.
  7. Crea los directorios necesarios para el despliegue.
#!/bin/bash
set -euo pipefail

# Actualizar sistema
apt-get update -y
apt-get install -y nginx openjdk-21-jdk awscli

# Crear usuarios
useradd -m -s /bin/bash thoth || true
useradd -m -s /bin/bash osiris || true

# Crear directorios de despliegue
mkdir -p /home/thoth/deploy/artifacts/{backup,current,logs,pids}
chown -R thoth:thoth /home/thoth/deploy

# Configurar Nginx como proxy SOLO para la API (no para el frontend)
cat > /etc/nginx/sites-available/proyectosacc-api <<'EOF'
server {
    listen 80;
    server_name _;

    location /api/ {
        proxy_pass http://localhost:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
EOF
ln -sf /etc/nginx/sites-available/proyectosacc-api /etc/nginx/sites-enabled/default

# Iniciar Nginx
systemctl enable nginx
systemctl start nginx

outputs.tf

Aquí definimos qué información queremos que Terraform nos muestre al final.

Ejemplo:

output "ec2_public_ip" {
  description = "IP pública del servidor de la API"
  value       = aws_instance.sacc_api.public_ip
}

output "rds_endpoint" {
  description = "Dirección de conexión a la base de datos"
  value       = aws_db_instance.sacc_db.endpoint
}

output "s3_artifacts_bucket" {
  description = "Nombre del bucket de artefactos de la API"
  value       = aws_s3_bucket.artifacts.bucket
}

output "s3_frontend_bucket" {
  description = "Nombre del bucket del frontend React"
  value       = aws_s3_bucket.sacc_frontend.bucket
}

output "cloudfront_domain" {
  description = "Dominio de CloudFront"
  value       = aws_cloudfront_distribution.sacc_cdn.domain_name
}

Después de ejecutar terraform apply, verás estos valores en la pantalla.


4. Cómo ejecutar Terraform paso a paso

Paso 1: Ir a la carpeta de Terraform

cd /home/evert/Servidores/Nuve/AWS/proyectosacc/terraform

Paso 2: Inicializar Terraform

terraform init

Esto descarga los plugins necesarios para hablar con AWS.

Paso 3: Revisar el plan

terraform plan

Terraform te muestra todo lo que va a crear, modificar o destruir. Revísalo con cuidado.

Paso 4: Aplicar los cambios

terraform apply

Terraform te pedirá que escribas yes para confirmar. Luego empezará a crear los recursos en AWS.

Paso 5: Ver los outputs

Al finalizar, Terraform mostrará los valores definidos en outputs.tf:

  • IP pública de la EC2 (API backend)
  • Endpoint de la base de datos
  • Nombre del bucket S3 de artefactos
  • Nombre del bucket S3 del frontend
  • Dominio de CloudFront

5. Comandos útiles adicionales

Comando Para qué sirve
terraform validate Revisa que la sintaxis de los archivos esté correcta.
terraform fmt Ordena y da formato a los archivos .tf.
terraform show Muestra el estado actual de la infraestructura.
terraform destroy Elimina todos los recursos creados. ¡Úsalo con cuidado!

6. Checklist antes de ejecutar Terraform

  • Tienes configuradas las credenciales de AWS (aws configure).
  • La variable db_password no está escrita en el código, viene de un archivo seguro o variable de entorno.
  • Revisaste el terraform plan antes de aplicar.
  • Sabes en qué región de AWS se va a crear todo (us-east-1, us-west-2, etc.).
  • El bucket S3 para el estado remoto ya existe (si usas backend remoto).
  • El user_data.sh incluye la llave pública SSH dedicada para el pipeline.
  • El bucket S3 del frontend tiene habilitado el versionamiento de objetos (para rollback del frontend).
  • CloudFront tiene configurado el Origin Access Identity (OAI) u Origin Access Control (OAC) para leer de S3.
  • Route 53 apunta a CloudFront, no a la IP de la EC2.
  • CloudFront tiene un behavior /api/* que apunta a la EC2.
  • La EC2 tiene el Security Group configurado para recibir tráfico HTTP desde CloudFront.

Anterior: 06-scripts-de-despliegue.md
Siguiente: 08-seguridad-y-secretos.md