# 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)*
