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:

.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 initPlanificacion
terraform plan -out=tfplanAplicacion
terraform apply tfplanVerificacion
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:

Vemos en la web de prueba el resultado esperado:

Limpieza
Para no generar costos innecesarios realizamos una limpieza de todos los recursos creados con un comando:
terraform destroyEsto 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
Este es el fin del laboratorio numero 1, si quieres dejame un comentario o tu opinion por Linkedin.
Saludos!