Contenido

Laboratorio 1

Enunciado

Un equipo de frontend necesita un hosting rápido, seguro y de bajo coste para su sitio web estático (HTML, CSS, JS). Debemos provisionar toda la infraestructura necesaria utilizando Terraform: un bucket S3 configurado para hosting web estático, una distribución CloudFront para servir el contenido globalmente con HTTPS y un certificado SSL gestionado por AWS Certificate Manager.

El sitio web debe ser accesible a través de un dominio personalizado. Todo el código debe estar versionado en GitHub.

Herramientas

  • AWS S3
  • AWS Cloudfront
  • AWS ACM
  • AWS Route 53
  • Terraform

Requisitos previos

Para resolver esta solicitud debemos tener:

  • Una cuenta no Root con permisos administrativos.
  • AWS CLI configurado en el equipo
  • Terraform instalado.

Primero creamos un proyecto en GitHub llamado laboratorio1 donde guardaremos todos los artefactos necesarios para cumplir con la tarea. El editor preferido para trabajar es Visual Studio Code, pero puede ser cualquier otro editor de texto.

Ejecución

Clonamos el repositorio creado en github, con VSCode nos colocamos en la carpeta y creamos los siguientes archivos en la raiz del proyecto:

/img/posts/proyectos/laboratorio1/archivos.png

.gitignore

Sirve para no subir al repositorio archivos sensibles, de estado o temporales

# Local .terraform directories
**/.terraform/*

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log
crash.*.log

# Exclude all .tfvars files, which are likely to contain sensitive data
*.tfvars
*.tfvars.json

# Ignore override files as they are usually used to override resources locally
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Ignore CLI configuration files
.terraformrc
terraform.rc

# Ignore plan files
tfplan*

providers-aws.tf

En este archivo se declaran los proveedores que usara el proyectoy configura la autenticacion, la informacion se obtiene de la pagina de registro de Terraform.

Línea Significado
terraform { … } Bloque de configuración global del proyecto Terraform.
required_providers { … } Especifica qué proveedores necesita el proyecto y de dónde descargarlos.
aws = { source = “hashicorp/aws” } Define el proveedor AWS desde el registro oficial de HashiCorp.
version = “~> 6.0” Restricción de versión. El operador ~> permite actualizaciones de parche y menores, pero no mayores.
required_version = “>= 6.0” Asegura que la persona que ejecuta el código tenga una versión de Terraform compatible con las características usadas.
provider “aws” { … } Configura el proveedor por defecto.
region = var.aws_region Usa una variable para que la región sea configurable sin modificar el código.
profile = “kernelops” Indica a Terraform que use las credenciales del perfil kernelops de la AWS CLI. Nunca pongas access_key o secret_key aquí.
provider “aws” { alias = “virginia” … } Crea un segundo proveedor para la región us-east-1. El alias permite usarlo en recursos específicos (ej. provider = aws.virginia).

variables.tf

Define todas las variables de entrada para hacer el codigo configurable y reutilizable.

Línea Significado
variable “nombre” { … } Declara una variable de entrada.
description Texto que aparece en la documentación y en terraform plan para explicar el propósito de la variable.
type Tipo de dato esperado (string, number, bool, list(…), map(…), etc.).
default Valor por defecto. Si no se proporciona, la variable es obligatoria y Terraform la pedirá interactivamente o fallará si no se pasa.
site_domain No tiene default. El usuario deberá definirla en terraform.tfvars o mediante -var.

terraform.tfvars

Asigna valores concretos a las variables definidas en variables.tf, Terraform las carga automaticamente de este archivo, no subirlo al repositorio si contiene claves secretas.

Línea Significado
aws_region = “us-east-2” Asigna el valor a la variable aws_region. Coincide con el nombre de la variable.
site_domain = “laboratorio1.polyforma3d.com” Define el subdominio que usaremos para el laboratorio.
environment = “lab” Valor para la variable environment.
domain_base = “polyforma3d.com” Dominio base de la zona Route53 existente.

outputs.tf

Muestra informacion util despues de ejecutar terraform apply como la URL del sitio.

Línea Significado
output “nombre” { … } Define una salida que se mostrará al final de terraform apply.
description Describe qué representa la salida.
value Expresión que calcula el valor a mostrar. Usa referencias a recursos (ej. aws_cloudfront_distribution.website.domain_name).
“https://${var.site_domain}” Interpolación de cadena: combina texto fijo con el valor de una variable.

s3.tf

Todos los recursos relacionados con S3: buckets, configuracion de sitio web, politicas de acceso y subida de archivos.

Línea Significado
resource “aws_s3_bucket” “website” Crea un bucket S3. El tipo de recurso es aws_s3_bucket y el nombre local (solo dentro de Terraform) es “website”.
bucket = var.site_domain El nombre del bucket será igual al dominio. Debe ser único globalmente.
force_destroy = true Útil en laboratorios: permite borrar el bucket aunque contenga objetos. En producción se suele poner false.
tags = { … } Metadatos en forma de clave-valor. Ayudan a identificar y filtrar recursos en la consola AWS.
aws_s3_bucket_website_configuration Recurso que habilita la funcionalidad de “Static website hosting” en S3.
index_document { suffix = “index.html” } Define index.html como página por defecto al acceder a la raíz.
error_document { key = “error.html” } Define error.html como página de error 404.
aws_s3_bucket_public_access_block Recurso que aplica los cuatro bloqueos de acceso público recomendados por AWS.
aws_s3_object Recurso para subir un archivo al bucket.
source = “${path.module}/site/index.html” path.module es una variable de Terraform que apunta al directorio del módulo actual.
etag = filemd5(…) Calcula el hash MD5 del archivo local. Si el archivo cambia, el etag cambia y Terraform detecta que debe volver a subirlo.
data “aws_iam_policy_document” Un “data source” no crea un recurso, sino que obtiene información o genera datos. En este caso, genera un documento de política IAM en JSON.
principals { type = “Service” … } Indica que el permiso se otorga a un servicio de AWS (CloudFront).
condition { … } Restringe el permiso: solo si la solicitud viene de la distribución especificada. Esto evita que otra distribución de CloudFront acceda a nuestro bucket.
aws_s3_bucket_policy Adjunta la política generada al bucket.

acm.tf

Solicitar y validar el certificado SSL/TLS necesario para servir el sitio con HTTPS en CloudFront.

Línea Significado
provider = aws.virginia Fuerza a que este recurso se cree usando el proveedor con alias virginia (región us-east-1). Obligatorio para CloudFront.
validation_method = “DNS” ACM generará unos registros DNS que deberemos crear en Route53 para demostrar que controlamos el dominio.
lifecycle { create_before_destroy = true } Si el certificado se modifica o renueva, Terraform creará primero el nuevo y luego destruirá el antiguo, evitando tiempos de inactividad.
aws_acm_certificate_validation Este recurso no crea nada en AWS; simplemente espera hasta que el certificado pase a estado “Issued” (validado).
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn] Es una expresión for que recorre los registros DNS de validación (creados en route53.tf) y extrae su FQDN. Se los pasa a ACM para que sepa qué registros debe verificar.

cloudfront.md

Crear la distribución de CloudFront que servirá el contenido del bucket S3 de forma rápida, segura y con HTTPS.

Línea Significado
aws_cloudfront_origin_access_control Recurso OAC (reemplaza al antiguo OAI). Permite que CloudFront acceda a S3 sin hacer público el bucket.
origin_access_control_origin_type = “s3” Indica que el origen es S3.
signing_behavior = “always” CloudFront siempre firmará las solicitudes con AWS Signature V4.
aws_cloudfront_distribution Recurso principal de CloudFront.
aliases = [var.site_domain] Lista de dominios alternativos. Deben coincidir con el certificado.
origin { domain_name = aws_s3_bucket.website.bucket_regional_domain_name } Usamos el dominio regional (bucket.s3.us-east-2.amazonaws.com) en lugar del endpoint de sitio web. Es necesario para OAC.
origin_access_control_id = … Vincula el OAC para que la distribución tenga permisos de lectura sobre el bucket.
default_cache_behavior Define cómo se comporta CloudFront para las solicitudes que no coinciden con otros comportamientos más específicos.
viewer_protocol_policy = “redirect-to-https” Si un usuario intenta acceder por HTTP, CloudFront lo redirige a HTTPS.
forwarded_values Configura qué partes de la solicitud se reenvían al origen. Para un sitio estático, no necesitamos query strings ni cookies.
viewer_certificate Asocia el certificado ACM. ssl_support_method = “sni-only” es la opción gratuita.
price_class = “PriceClass_100” Reduce costos limitando las ubicaciones de borde. Para producción global se usaría PriceClass_All.

route53.tf

Gestionar los registros DNS: validación del certificado ACM y registro A (alias) a CloudFront.

Línea Significado
data “aws_route53_zone” “main” Busca una zona alojada existente en Route53. Debe existir previamente.
name = “${var.domain_base}.” El nombre de la zona debe terminar con un punto (FQDN).
aws_route53_record.cert_validation Crea los registros DNS que ACM necesita para validar el dominio.
for_each = { … } Itera sobre domain_validation_options del certificado ACM. Crea un registro por cada opción.
allow_overwrite = true Si ya existe un registro con el mismo nombre, lo sobrescribe. Útil en laboratorios.
aws_route53_record.site Crea un registro A de tipo Alias. Los Alias son gratuitos y específicos de AWS.
alias { name = aws_cloudfront_distribution.website.domain_name } Apunta al dominio de CloudFront (ej. d123.cloudfront.net).
zone_id = aws_cloudfront_distribution.website.hosted_zone_id El hosted_zone_id de CloudFront es una constante (Z2FDTNDATAQYW2). Terraform la proporciona automáticamente.
evaluate_target_health = false No evalúa la salud del target porque CloudFront no expone un health check para este propósito.

Archivos del sitio (site/index.html y site/error.html)

Archivos para el sitio web de demostracion

Ejecucion

Teniendo configurados los accesos correspondientes en AWS usando el perfil kernelops y con una zona en Route53 con el dominio procedemos a ajecutar los comandos en la raiz del proyecto.

Inicializacion

terraform init

Planificacion

terraform plan -out=tfplan

Aplicacion

terraform apply tfplan

Verificacion

Para verificar la creacion de los recursos en AWS y la pagina web en funcionamiento vemos en la consola el resultado de las variables de salida:

/img/posts/proyectos/laboratorio1/salida.png

Vemos en la web de prueba el resultado esperado:

/img/posts/proyectos/laboratorio1/web.png

Limpieza

Para no generar costos innecesarios realizamos una limpieza de todos los recursos creados con un comando:

terraform destroy

Esto ELIMINARÁ automáticamente:

  • El bucket S3 (laboratorio1.polyforma3d.com) y todos sus objetos (porque configuramos force_destroy = true).

  • La distribución de CloudFront.

  • El certificado ACM.

  • Los registros DNS en Route 53 (tanto el de validación como el registro A).

  • La política del bucket y la configuración de acceso público.

La zona alojada (polyforma3d.com) no fue creada por este proyecto y no sera eliminada.

Repositorio

Laboratorio 1

Este es el fin del laboratorio numero 1, si quieres dejame un comentario o tu opinion por Linkedin.

Saludos!