# 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: ```hcl 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: ```hcl 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: ```hcl 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: ```hcl 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. ```bash #!/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: ```hcl 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 ```bash cd /home/evert/Servidores/Nuve/AWS/proyectosacc/terraform ``` ### Paso 2: Inicializar Terraform ```bash terraform init ``` Esto descarga los plugins necesarios para hablar con AWS. ### Paso 3: Revisar el plan ```bash terraform plan ``` Terraform te muestra **todo lo que va a crear, modificar o destruir**. Revísalo con cuidado. ### Paso 4: Aplicar los cambios ```bash 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`](06-scripts-de-despliegue.md)* *Siguiente: [`08-seguridad-y-secretos.md`](08-seguridad-y-secretos.md)*