Día 63 - Desafío Final Semana 9
Desafío Final: Tu primera infraestructura con Terraform
🏆 ¡El Gran Finale de la Semana 9!
¡Hola Roxs! Llegamos al último día de nuestra semana con LocalStack. Hoy vamos a usar Terraform para desplegar automáticamente toda la infraestructura que hemos estado creando manualmente.
¿Listos para automatizar como verdaderos DevOps Engineers? 🚀
🎯 ¿Qué es Terraform?
Terraform es una herramienta que te permite escribir tu infraestructura como código. En lugar de crear buckets, tablas y funciones manualmente, escribes un archivo que describe lo que quieres y Terraform lo crea automáticamente.
🌟 ¿Por qué usar Terraform?
- ✅ Reproducible: La misma infraestructura siempre
- ✅ Versionado: Cambios en Git como cualquier código
- ✅ Colaborativo: Todo el equipo usa la misma definición
- ✅ Destructible: Eliminar todo con un comando
- ✅ Documentado: El código ES la documentación
🛠️ Instalación de Terraform
📦 Instalar Terraform
# Ubuntu/Debian
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
# macOS con Homebrew
brew install terraform
# Verificar instalación
terraform --version
🚀 Verificar LocalStack
# Asegurar que LocalStack esté corriendo
localstack start
# Verificar que está funcionando
curl http://localhost:4566/health
📁 Estructura del proyecto
# Crear carpeta del proyecto final
mkdir mi-infraestructura-terraform
cd mi-infraestructura-terraform
# Crear estructura simple
mkdir terraform
touch terraform/main.tf
touch terraform/outputs.tf
# Estructura final:
# mi-infraestructura-terraform/
# └── terraform/
# ├── main.tf (configuración principal)
# └── outputs.tf (información de salida)
🏗️ Nuestro primer archivo Terraform
⚙️ Configuración principal (main.tf)
# terraform/main.tf
# Configuración de Terraform y el provider de AWS
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Configuración para conectar con LocalStack
provider "aws" {
region = "us-east-1"
access_key = "test"
secret_key = "test"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
# Endpoints para LocalStack
endpoints {
s3 = "http://localhost:4566"
lambda = "http://localhost:4566"
dynamodb = "http://localhost:4566"
apigateway = "http://localhost:4566"
iam = "http://localhost:4566"
}
}
# Variable para personalizar nombres
variable "student_name" {
description = "Tu nombre para personalizar los recursos"
type = string
default = "estudiante-roxs"
}
# ==========================================
# 1. BUCKETS S3
# ==========================================
# Bucket para archivos
resource "aws_s3_bucket" "mi_bucket_archivos" {
bucket = "mi-bucket-archivos-${var.student_name}"
tags = {
Name = "Bucket de Archivos"
Environment = "LocalStack"
CreatedBy = "Terraform"
Student = var.student_name
}
}
# Bucket para logs
resource "aws_s3_bucket" "mi_bucket_logs" {
bucket = "mi-bucket-logs-${var.student_name}"
tags = {
Name = "Bucket de Logs"
Environment = "LocalStack"
CreatedBy = "Terraform"
Student = var.student_name
}
}
# ==========================================
# 2. TABLAS DYNAMODB
# ==========================================
# Tabla de usuarios
resource "aws_dynamodb_table" "mi_tabla_usuarios" {
name = "mi-tabla-usuarios-${var.student_name}"
billing_mode = "PROVISIONED"
read_capacity = 5
write_capacity = 5
hash_key = "user_id"
attribute {
name = "user_id"
type = "S"
}
tags = {
Name = "Tabla de Usuarios"
Environment = "LocalStack"
CreatedBy = "Terraform"
Student = var.student_name
}
}
# Tabla de posts
resource "aws_dynamodb_table" "mi_tabla_posts" {
name = "mi-tabla-posts-${var.student_name}"
billing_mode = "PROVISIONED"
read_capacity = 5
write_capacity = 5
hash_key = "post_id"
attribute {
name = "post_id"
type = "S"
}
tags = {
Name = "Tabla de Posts"
Environment = "LocalStack"
CreatedBy = "Terraform"
Student = var.student_name
}
}
# ==========================================
# 3. ROL IAM PARA LAMBDA
# ==========================================
# Rol para las funciones Lambda
resource "aws_iam_role" "mi_lambda_role" {
name = "mi-lambda-role-${var.student_name}"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
tags = {
Name = "Lambda Role"
Environment = "LocalStack"
CreatedBy = "Terraform"
Student = var.student_name
}
}
# Política para el rol Lambda
resource "aws_iam_role_policy" "mi_lambda_policy" {
name = "mi-lambda-policy-${var.student_name}"
role = aws_iam_role.mi_lambda_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:Query",
"dynamodb:Scan"
]
Resource = [
aws_dynamodb_table.mi_tabla_usuarios.arn,
aws_dynamodb_table.mi_tabla_posts.arn
]
},
{
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
]
Resource = [
"${aws_s3_bucket.mi_bucket_archivos.arn}/*",
"${aws_s3_bucket.mi_bucket_logs.arn}/*"
]
},
{
Effect = "Allow"
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Resource = "arn:aws:logs:*:*:*"
}
]
})
}
# ==========================================
# 4. FUNCIÓN LAMBDA SIMPLE
# ==========================================
# Crear el archivo ZIP para Lambda
data "archive_file" "mi_lambda_zip" {
type = "zip"
output_path = "mi_lambda.zip"
source {
content = <<EOF
import json
import boto3
from datetime import datetime
def lambda_handler(event, context):
"""
Función Lambda simple creada con Terraform
"""
print(f"Evento recibido: {json.dumps(event)}")
# Información de la infraestructura
response = {
'statusCode': 200,
'body': json.dumps({
'message': '¡Hola desde Lambda creada con Terraform!',
'student': '${var.student_name}',
'timestamp': datetime.now().isoformat(),
'event_received': event,
'infrastructure': {
'created_with': 'Terraform',
'environment': 'LocalStack',
'tables': ['mi-tabla-usuarios', 'mi-tabla-posts'],
'buckets': ['mi-bucket-archivos', 'mi-bucket-logs']
}
})
}
print(f"Respuesta: {json.dumps(response)}")
return response
EOF
filename = "lambda_function.py"
}
}
# Función Lambda
resource "aws_lambda_function" "mi_lambda" {
filename = data.archive_file.mi_lambda_zip.output_path
function_name = "mi-lambda-${var.student_name}"
role = aws_iam_role.mi_lambda_role.arn
handler = "lambda_function.lambda_handler"
runtime = "python3.9"
timeout = 30
environment {
variables = {
TABLA_USUARIOS = aws_dynamodb_table.mi_tabla_usuarios.name
TABLA_POSTS = aws_dynamodb_table.mi_tabla_posts.name
BUCKET_ARCHIVOS = aws_s3_bucket.mi_bucket_archivos.bucket
BUCKET_LOGS = aws_s3_bucket.mi_bucket_logs.bucket
STUDENT_NAME = var.student_name
}
}
depends_on = [data.archive_file.mi_lambda_zip]
tags = {
Name = "Mi Lambda Function"
Environment = "LocalStack"
CreatedBy = "Terraform"
Student = var.student_name
}
}
📊 Información de salida (outputs.tf)
# terraform/outputs.tf
# Mostrar información importante después del despliegue
output "resumen_infraestructura" {
description = "Resumen de la infraestructura creada"
value = {
student_name = var.student_name
region = "us-east-1"
environment = "LocalStack"
}
}
output "buckets_s3" {
description = "Buckets S3 creados"
value = {
archivos = aws_s3_bucket.mi_bucket_archivos.bucket
logs = aws_s3_bucket.mi_bucket_logs.bucket
}
}
output "tablas_dynamodb" {
description = "Tablas DynamoDB creadas"
value = {
usuarios = aws_dynamodb_table.mi_tabla_usuarios.name
posts = aws_dynamodb_table.mi_tabla_posts.name
}
}
output "lambda_function" {
description = "Función Lambda creada"
value = {
name = aws_lambda_function.mi_lambda.function_name
arn = aws_lambda_function.mi_lambda.arn
}
}
output "comandos_para_probar" {
description = "Comandos para probar tu infraestructura"
value = {
listar_buckets = "awslocal s3 ls"
listar_tablas = "awslocal dynamodb list-tables"
invocar_lambda = "awslocal lambda invoke --function-name ${aws_lambda_function.mi_lambda.function_name} output.json"
ver_resultado = "cat output.json"
}
}
🚀 ¡Desplegar tu infraestructura!
📝 Paso 1: Inicializar Terraform
# Ir a la carpeta de terraform
cd terraform
# Inicializar Terraform (solo la primera vez)
terraform init
¿Qué hace terraform init
?
- Descarga el provider de AWS
- Configura el directorio de trabajo
- Prepara Terraform para funcionar
🔍 Paso 2: Validar la configuración
# Verificar que no hay errores de sintaxis
terraform validate
Si hay errores, Terraform te dirá exactamente qué está mal y en qué línea.
📋 Paso 3: Ver qué se va a crear
# Ver el plan de ejecución
terraform plan -var="student_name=TU_NOMBRE_AQUI"
¿Qué hace terraform plan
?
- Muestra exactamente qué recursos se van a crear
- No crea nada, solo te muestra el plan
- Es como un "preview" antes de aplicar cambios
🎯 Paso 4: ¡Crear la infraestructura!
# Aplicar la configuración
terraform apply -var="student_name=TU_NOMBRE_AQUI"
Terraform te preguntará si estás seguro. Escribe yes
para continuar.
📊 Paso 5: Ver la información de salida
# Ver todos los outputs
terraform output
🧪 Probar tu infraestructura
✅ Verificar que todo se creó correctamente
# 1. Verificar buckets S3
awslocal s3 ls
# 2. Verificar tablas DynamoDB
awslocal dynamodb list-tables
# 3. Verificar función Lambda
awslocal lambda list-functions --query 'Functions[].FunctionName'
# 4. Probar la función Lambda
awslocal lambda invoke \
--function-name mi-lambda-TU_NOMBRE \
--payload '{"test": "Hola desde Terraform!"}' \
output.json
# 5. Ver el resultado
cat output.json | jq
📝 Agregar datos de prueba
# Agregar un usuario a DynamoDB
awslocal dynamodb put-item \
--table-name mi-tabla-usuarios-TU_NOMBRE \
--item '{
"user_id": {"S": "user001"},
"name": {"S": "Usuario Terraform"},
"email": {"S": "usuario@terraform.local"},
"created_by": {"S": "Terraform"}
}'
# Verificar que se guardó
awslocal dynamodb get-item \
--table-name mi-tabla-usuarios-TU_NOMBRE \
--key '{"user_id": {"S": "user001"}}'
# Subir un archivo a S3
echo "Archivo creado con Terraform" > test-terraform.txt
awslocal s3 cp test-terraform.txt s3://mi-bucket-archivos-TU_NOMBRE/
# Verificar que se subió
awslocal s3 ls s3://mi-bucket-archivos-TU_NOMBRE/
🧹 Limpiar recursos
🗑️ Eliminar toda la infraestructura
# Eliminar TODO lo que creamos
terraform destroy -var="student_name=TU_NOMBRE_AQUI"
Terraform te preguntará si estás seguro. Escribe yes
para eliminar todo.
¡Esto es la magia de Terraform!
- Con un comando creates toda tu infraestructura
- Con otro comando la eliminas completamente
- Todo está documentado en código
📚 Script de automatización
🤖 Script para facilitar el proceso
#!/bin/bash
# deploy.sh - Script simple para desplegar con Terraform
# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo -e "${BLUE}🚀 Script de despliegue con Terraform${NC}"
echo "======================================"
# Verificar que se proporcionó el nombre
if [ -z "$1" ]; then
echo -e "${RED}❌ Error: Debes proporcionar tu nombre${NC}"
echo -e "${YELLOW}Uso: $0 TU_NOMBRE${NC}"
echo -e "${YELLOW}Ejemplo: $0 maria-rodriguez${NC}"
exit 1
fi
STUDENT_NAME=$1
echo -e "${BLUE}👤 Estudiante: $STUDENT_NAME${NC}"
# Verificar LocalStack
echo -e "${BLUE}🔍 Verificando LocalStack...${NC}"
if ! curl -s http://localhost:4566/health > /dev/null; then
echo -e "${RED}❌ LocalStack no está corriendo${NC}"
echo -e "${YELLOW}Inicia LocalStack con: localstack start${NC}"
exit 1
fi
echo -e "${GREEN}✅ LocalStack está funcionando${NC}"
# Verificar Terraform
echo -e "${BLUE}🔍 Verificando Terraform...${NC}"
if ! command -v terraform &> /dev/null; then
echo -e "${RED}❌ Terraform no está instalado${NC}"
exit 1
fi
echo -e "${GREEN}✅ Terraform está instalado${NC}"
# Ir al directorio de terraform
cd terraform
# Inicializar si es necesario
if [ ! -f ".terraform.lock.hcl" ]; then
echo -e "${BLUE}🏗️ Inicializando Terraform...${NC}"
terraform init
fi
# Validar configuración
echo -e "${BLUE}🔍 Validando configuración...${NC}"
if ! terraform validate; then
echo -e "${RED}❌ Error en la configuración de Terraform${NC}"
exit 1
fi
echo -e "${GREEN}✅ Configuración válida${NC}"
# Mostrar plan
echo -e "${BLUE}📋 Mostrando plan de ejecución...${NC}"
terraform plan -var="student_name=$STUDENT_NAME"
# Preguntar si continuar
echo -e "${YELLOW}¿Quieres aplicar estos cambios? (y/N)${NC}"
read -r response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}ℹ️ Operación cancelada${NC}"
exit 0
fi
# Aplicar configuración
echo -e "${BLUE}🚀 Desplegando infraestructura...${NC}"
if terraform apply -var="student_name=$STUDENT_NAME" -auto-approve; then
echo -e "${GREEN}✅ ¡Despliegue exitoso!${NC}"
# Mostrar outputs
echo -e "${BLUE}📊 Información de la infraestructura:${NC}"
terraform output
echo ""
echo -e "${GREEN}🎉 ¡Tu infraestructura está lista!${NC}"
echo -e "${YELLOW}🧪 Prueba los comandos que aparecen en 'comandos_para_probar'${NC}"
echo -e "${YELLOW}🧹 Para eliminar todo: terraform destroy -var=\"student_name=$STUDENT_NAME\"${NC}"
else
echo -e "${RED}❌ Error en el despliegue${NC}"
exit 1
fi
🎯 Usar el script
# Hacer el script ejecutable
chmod +x deploy.sh
# Ejecutar con tu nombre
./deploy.sh tu-nombre-aqui
💡 Tarea del Día
📝 Parte 1: Setup inicial (20 minutos)
-
Crear estructura del proyecto:
mkdir mi-primer-terraform
cd mi-primer-terraform
mkdir terraform -
Crear archivos Terraform:
- Copia el contenido de
main.tf
- Copia el contenido de
outputs.tf
- Copia el contenido de
-
Verificar LocalStack:
localstack start
curl http://localhost:4566/health
🚀 Parte 2: Primer despliegue (25 minutos)
-
Inicializar Terraform:
cd terraform
terraform init -
Validar configuración:
terraform validate
-
Ver el plan:
terraform plan -var="student_name=tu-nombre"
-
Desplegar infraestructura:
terraform apply -var="student_name=tu-nombre"
-
Ver los outputs:
terraform output
🧪 Parte 3: Probar la infraestructura (15 minutos)
-
Verificar recursos creados:
awslocal s3 ls
awslocal dynamodb list-tables
awslocal lambda list-functions -
Probar la función Lambda:
awslocal lambda invoke \
--function-name mi-lambda-tu-nombre \
--payload '{"mensaje": "Hola Terraform!"}' \
resultado.json
cat resultado.json -
Agregar datos de prueba:
# Usuario en DynamoDB
awslocal dynamodb put-item \
--table-name mi-tabla-usuarios-tu-nombre \
--item '{"user_id": {"S": "test001"}, "name": {"S": "Usuario Test"}}'
# Archivo en S3
echo "Mi primer archivo con Terraform" > test.txt
awslocal s3 cp test.txt s3://mi-bucket-archivos-tu-nombre/
📸 Parte 4: Evidencia y análisis
Screenshots a tomar:
- Terraform init exitoso
- Terraform plan output
- Terraform apply exitoso
- Terraform output con todos los recursos
- awslocal s3 ls mostrando tus buckets
- awslocal dynamodb list-tables mostrando tus tablas
- Resultado de invocar tu función Lambda
🧹 Parte 5: Limpieza
-
Eliminar toda la infraestructura:
terraform destroy -var="student_name=tu-nombre"
-
Verificar que todo se eliminó:
awslocal s3 ls
awslocal dynamodb list-tables
awslocal lambda list-functions
📤 Compartir en Discord
Mensaje para el canal #semana9-localstack:
🎉 ¡COMPLETÉ MI PRIMERA INFRAESTRUCTURA CON TERRAFORM! 🎉
👤 **Estudiante**: [TU_NOMBRE]
🏗️ **Herramientas**: Terraform + LocalStack
📊 **Mi infraestructura incluye**:
✅ 2 buckets S3 (archivos y logs)
✅ 2 tablas DynamoDB (usuarios y posts)
✅ 1 función Lambda funcional
✅ Rol IAM con permisos correctos
🔥 **Lo más emocionante**:
[Comparte qué te gustó más del proceso]
💡 **Lección clave**:
[Qué aprendiste sobre Infrastructure as Code]
🚀 **Próximo paso**:
¡Usar esto en un proyecto real!
#Terraform #LocalStack #InfrastructureAsCode #DevOps #Semana9
Incluir:
- Screenshot del
terraform apply
exitoso - Screenshot de
terraform output
- Screenshot mostrando recursos creados en LocalStack
🎓 Reflexión de la semana
🌟 Lo que lograste esta semana:
- ✅ Día 59: Dominaste S3 localmente
- ✅ Día 60: Creaste funciones Lambda
- ✅ Día 61: Trabajaste con DynamoDB
- ✅ Día 62: Integraste múltiples servicios
- ✅ Día 63: Automatizaste todo con Terraform
💪 Habilidades nuevas:
- Infrastructure as Code: Definir infraestructura en archivos
- Terraform: Herramienta líder en IaC
- LocalStack: Desarrollo cloud local
- AWS Services: S3, Lambda, DynamoDB
- Automation: Scripts y procesos automatizados
🚀 De cara al futuro:
Ahora puedes:
- Crear infraestructura AWS real con Terraform
- Trabajar en equipos con infraestructura versionada
- Implementar CI/CD para infraestructura
- Crear entornos reproducibles
- Escalar aplicaciones en la nube
🎉 ¡Felicitaciones!
Has completado exitosamente la Semana 9 de "90 días de DevOps con Roxs".
Pasaste de crear recursos manualmente a automatizar infraestructura completa con código. Esta es una habilidad súper valiosa en el mundo DevOps actual.
🔮 Próximos pasos sugeridos:
- Practica más Terraform con otros providers (Azure, GCP)
- Aprende Terraform Cloud para colaboración en equipo
- Implementa CI/CD para tu infraestructura
- Explora módulos de Terraform para reutilización
- Combina con Ansible para configuración de servidores
¡Sigue así! 🚀✨