Lab 2: Sitio Secundario (Fallback)
Lab 2: Sitio Secundario – S3 + CloudFront
Estructura del módulo
02_secondary/
├── main.tf S3 bucket, OAC, CloudFront distribution
├── variables.tf
└── outputs.tfPaso 1: Setup
source ~/route53-arc-tf/global.env
cd ~/route53-arc-tf/02_secondaryPaso 2: Crear los archivos
1. variables.tf
¿Para qué sirve este archivo?
Define las variables de entrada para el módulo del sitio de contingencia (región de AWS y tag identificador del workshop).
cat > variables.tf << 'EOF'
variable "aws_region" {
type = string
default = "us-east-1"
}
variable "project_tag" {
type = string
default = "route53-arc"
}
EOF2. main.tf
¿Para qué sirve este archivo?
Declara la arquitectura del sitio secundario de respuesta ante desastres:
- Bucket S3 totalmente privado: Almacena el archivo HTML estático de la página de mantenimiento.
- Origin Access Control (OAC): Configura una firma SigV4 para que solo CloudFront tenga permiso de leer los objetos del bucket.
- CloudFront Distribution: Provee entrega global con HTTPS, respuestas personalizadas de error (403/404 -> 200 index.html) y baja latencia.
- Bucket Policy y Objetos: Aplica la política de lectura restringida e inyecta la página
index.html.
cat > main.tf << 'EOF'
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Workshop = var.project_tag
ManagedBy = "Terraform"
}
}
}
data "aws_caller_identity" "current" {}
# ─── S3 Bucket ────────────────────────────────────────────────────────────────
resource "aws_s3_bucket" "fallback" {
bucket = "route53-arc-fallback-${data.aws_caller_identity.current.account_id}"
}
resource "aws_s3_bucket_public_access_block" "fallback" {
bucket = aws_s3_bucket.fallback.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# ─── CloudFront Origin Access Control ─────────────────────────────────────────
resource "aws_cloudfront_origin_access_control" "fallback" {
name = "route53-arc-fallback-oac"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
# ─── CloudFront Distribution ──────────────────────────────────────────────────
resource "aws_cloudfront_distribution" "fallback" {
enabled = true
default_root_object = "index.html"
comment = "AWS Route 53 ARC Fallback Site"
http_version = "http2"
origin {
domain_name = aws_s3_bucket.fallback.bucket_regional_domain_name
origin_id = "S3FallbackOrigin"
origin_access_control_id = aws_cloudfront_origin_access_control.fallback.id
}
default_cache_behavior {
target_origin_id = "S3FallbackOrigin"
viewer_protocol_policy = "redirect-to-https"
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
forwarded_values {
query_string = false
cookies { forward = "none" }
}
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}
custom_error_response {
error_code = 403
response_code = 200
response_page_path = "/index.html"
}
custom_error_response {
error_code = 404
response_code = 200
response_page_path = "/index.html"
}
restrictions {
geo_restriction { restriction_type = "none" }
}
viewer_certificate {
cloudfront_default_certificate = true
}
}
# ─── Bucket Policy – solo CloudFront puede leer ───────────────────────────────
resource "aws_s3_bucket_policy" "fallback" {
bucket = aws_s3_bucket.fallback.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "AllowCloudFrontServicePrincipal"
Effect = "Allow"
Principal = { Service = "cloudfront.amazonaws.com" }
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.fallback.arn}/*"
Condition = {
StringEquals = {
"AWS:SourceArn" = aws_cloudfront_distribution.fallback.arn
}
}
}]
})
}
# ─── Contenido: página de mantenimiento ───────────────────────────────────────
resource "aws_s3_object" "index" {
bucket = aws_s3_bucket.fallback.id
key = "index.html"
content_type = "text/html"
content = <<HTML
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Mantenimiento – AWS Workshop</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family:'Segoe UI',Arial,sans-serif; background:#0f172a;
color:#e2e8f0; display:flex; align-items:center;
justify-content:center; min-height:100vh; }
.container { text-align:center; padding:40px 60px; background:#1e293b;
border-radius:16px; box-shadow:0 8px 32px rgba(0,0,0,.5);
max-width:600px; width:90%; }
.icon { font-size:64px; margin-bottom:20px; }
h1 { font-size:2em; color:#f59e0b; margin-bottom:12px; }
p { color:#94a3b8; line-height:1.6; margin-bottom:8px; }
.badge { display:inline-block; background:#f59e0b; color:#0f172a;
padding:6px 20px; border-radius:20px; font-weight:bold;
font-size:.85em; margin:16px 0; }
.footer { margin-top:28px; padding-top:16px;
border-top:1px solid #334155; color:#475569; font-size:.8em; }
</style>
</head>
<body>
<div class="container">
<div class="icon">⚠️</div>
<h1>Sitio en Mantenimiento</h1>
<div class="badge">MANTENIMIENTO PROGRAMADO</div>
<p>Estamos realizando tareas de mantenimiento para mejorar nuestros servicios.</p>
<p><strong>Por favor, inténtelo de nuevo en unos minutos.</strong></p>
<div class="footer">
<p>AWS Route 53 ARC Workshop</p>
<p>Sitio de Contingencia · Terraform + CloudFront + S3</p>
</div>
</div>
</body>
</html>
HTML
# Forzar re-deploy si el contenido cambia
etag = md5(<<HTML
<!DOCTYPE html>
<html lang="es">fallback
HTML
)
}
EOF3. outputs.tf
¿Para qué sirve este archivo?
Exporta el dominio asignado por CloudFront (cloudfront_domain), el ID de la distribución y el nombre del bucket S3. El dominio de CloudFront se utilizará en el Lab 3 para apuntar el registro DNS de contingencia (CNAME).
cat > outputs.tf << 'EOF'
output "cloudfront_domain" {
description = "Dominio de la distribución CloudFront"
value = aws_cloudfront_distribution.fallback.domain_name
}
output "cloudfront_distribution_id" {
description = "ID de la distribución CloudFront"
value = aws_cloudfront_distribution.fallback.id
}
output "fallback_bucket_name" {
description = "Nombre del bucket S3"
value = aws_s3_bucket.fallback.id
}
EOFPaso 3: Inicializar y desplegar
terraform init
terraform plan
terraform apply -auto-approveInformación
La distribución CloudFront tarda 5-10 minutos en desplegarse. El apply completa cuando AWS confirma la creación, pero el DNS puede tardar un poco más en propagar.
Paso 4: Outputs
terraform output
export CF_DOMAIN=$(terraform output -raw cloudfront_domain)
export CF_DIST_ID=$(terraform output -raw cloudfront_distribution_id)
echo "CloudFront: https://$CF_DOMAIN"✅ Verificación del Lab 2
echo "=== Verificación Lab 2 ==="
terraform show | grep -q "aws_cloudfront_distribution.fallback" \
&& echo "✅ CloudFront en state" || echo "❌ No encontrado en state"
CF_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$CF_DOMAIN/)
[ "$CF_STATUS" = "200" ] \
&& echo "✅ CloudFront HTTPS $CF_STATUS" \
|| echo "⚠️ HTTP $CF_STATUS (puede tardar hasta 10 min)"
echo "CF Domain: $CF_DOMAIN"Siguiente paso → Lab 3: Route 53 ARC