Despliegue Continuo con Terraform y AWS CodePipeline: Automatización de Infraestructura y Aplicaciones
Este tutorial te guiará paso a paso en la creación de un sistema de despliegue continuo robusto utilizando Terraform para gestionar la infraestructura y AWS CodePipeline para orquestar el flujo de CI/CD. Automatiza la entrega de tus aplicaciones desde el código hasta la producción de manera eficiente y segura.
🚀 Introducción al Despliegue Continuo con Terraform y AWS CodePipeline
El Despliegue Continuo (CD) es una práctica fundamental en DevOps que permite entregar cambios de software de forma rápida, segura y sostenible. Integrar Terraform con AWS CodePipeline es una combinación poderosa para automatizar no solo el despliegue de tus aplicaciones, sino también la infraestructura que las soporta. Esto asegura que tu entorno esté siempre en un estado deseado, definido por código, y que cada cambio se propague de manera predecible.
En este tutorial, exploraremos cómo construir un pipeline de CD completo que se encargue de todo: desde la detección de cambios en tu repositorio de código hasta el despliegue de infraestructura con Terraform y la entrega de una aplicación sencilla en AWS S3.
🎯 Objetivos del Tutorial
Al finalizar este tutorial, serás capaz de:
- Comprender los conceptos clave de AWS CodePipeline y su integración con Terraform.
- Configurar un repositorio de código (GitHub) como fuente para tu pipeline.
- Crear buckets S3 para almacenar el estado de Terraform y los artefactos de CodePipeline.
- Diseñar un pipeline que compile una aplicación, construya el plan de Terraform y aplique los cambios.
- Desplegar una aplicación web estática básica usando Terraform en un bucket S3.
- Automatizar el proceso de aprobación manual en el pipeline.
🛠️ Herramientas Necesarias
Antes de comenzar, asegúrate de tener las siguientes herramientas y configuraciones:
- Cuenta de AWS: Con permisos para crear recursos como S3, IAM, CodeBuild, CodePipeline.
- AWS CLI: Configurado y autenticado en tu máquina local.
- Terraform CLI: Versión 1.0 o superior instalada.
- GitHub: Una cuenta y un repositorio para el código fuente de tu aplicación y de Terraform.
- Necesitarás generar un Token de Acceso Personal (PAT) con permisos
repopara que CodePipeline pueda acceder a tu repositorio.
- Necesitarás generar un Token de Acceso Personal (PAT) con permisos
- Código de Aplicación (Ejemplo): Una aplicación web estática simple (HTML, CSS, JS).
🧱 Arquitectura General del Pipeline
Nuestra arquitectura se basará en el siguiente flujo:
- Fuente: Los cambios se envían a un repositorio de GitHub.
- Compilación/Construcción: AWS CodeBuild ejecuta los comandos para preparar la aplicación y/o el código Terraform.
- Despliegue de Infraestructura (Terraform Plan/Apply): CodeBuild genera un plan de Terraform y, tras una aprobación manual, lo aplica.
- Despliegue de Aplicación (S3): Si la aplicación es web estática, CodeBuild sincroniza los archivos con un bucket S3.
📝 Preparación del Entorno
1. 📂 Estructura del Repositorio de GitHub
Crea un nuevo repositorio en GitHub (ej. my-cd-terraform-pipeline). La estructura de tu repositorio será crucial. Considera lo siguiente:
my-cd-terraform-pipeline/
├── terraform/
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ └── backend.tf
├── app/
│ ├── index.html
│ ├── style.css
│ └── script.js
├── buildspec_terraform_plan.yml
├── buildspec_terraform_apply.yml
└── buildspec_app_deploy.yml
2. 🔐 Configuración de Backend de Terraform (S3 y DynamoDB)
Para gestionar el estado de Terraform de forma segura y colaborativa, utilizaremos un bucket S3 para el archivo de estado y una tabla DynamoDB para el bloqueo de estado.
Crea los siguientes recursos manualmente una única vez desde la consola de AWS o con la AWS CLI.
# Crear bucket S3 para el estado de Terraform
aws s3 mb s3://my-terraform-state-bucket-unique-name-123 --region us-east-1
aws s3api put-bucket-versioning --bucket my-terraform-state-bucket-unique-name-123 --versioning-configuration Status=Enabled
# Crear tabla DynamoDB para el bloqueo de estado
aws dynamodb create-table \
--table-name my-terraform-lock-table \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region us-east-1
Ahora, configura terraform/backend.tf en tu repositorio:
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket-unique-name-123" # Reemplaza con el nombre de tu bucket
key = "terraform/state/app.tfstate"
region = "us-east-1"
dynamodb_table = "my-terraform-lock-table"
encrypt = true
}
}
📜 Definición de la Infraestructura con Terraform
Vamos a definir un bucket S3 que alojará nuestra aplicación web estática.
terraform/main.tf
resource "aws_s3_bucket" "app_bucket" {
bucket = var.app_bucket_name
acl = "public-read" # Ojo: para sitios públicos, considera CloudFront para mayor seguridad y rendimiento
website {
index_document = "index.html"
error_document = "error.html"
}
tags = {
Name = "${var.app_bucket_name}-static-website"
Environment = var.environment
ManagedBy = "Terraform"
}
}
resource "aws_s3_bucket_public_access_block" "app_bucket_public_access_block" {
bucket = aws_s3_bucket.app_bucket.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}
resource "aws_s3_bucket_policy" "app_bucket_policy" {
bucket = aws_s3_bucket.app_bucket.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "PublicReadGetObject",
Effect = "Allow",
Principal = "*",
Action = "s3:GetObject",
Resource = ["${aws_s3_bucket.app_bucket.arn}/*"]
}
]
})
}
output "website_endpoint" {
description = "El endpoint del sitio web estático."
value = aws_s3_bucket.app_bucket.website_endpoint
}
terraform/variables.tf
variable "app_bucket_name" {
description = "El nombre único del bucket S3 para la aplicación."
type = string
}
variable "environment" {
description = "El entorno de despliegue (dev, prod, etc.)."
type = string
default = "dev"
}
terraform/outputs.tf
output "s3_bucket_id" {
description = "ID del bucket S3 de la aplicación"
value = aws_s3_bucket.app_bucket.id
}
⚙️ Configuración de AWS CodeBuild (buildspec.yml)
Necesitaremos tres archivos buildspec para las diferentes fases:
buildspec_terraform_plan.yml
Este archivo generará el plan de Terraform. Es una buena práctica revisar el plan antes de aplicarlo.
version: 0.2
phases:
install:
runtime-versions:
python: 3.8
commands:
- yum install -y unzip # Necesario para descomprimir Terraform
- curl -LO https://releases.hashicorp.com/terraform/1.7.5/terraform_1.7.5_linux_amd64.zip
- unzip terraform_1.7.5_linux_amd64.zip
- mv terraform /usr/local/bin/
- terraform -v
pre_build:
commands:
- echo "Inicializando Terraform..."
- cd terraform
- terraform init -backend-config="bucket=my-terraform-state-bucket-unique-name-123,key=terraform/state/app.tfstate,region=us-east-1,dynamodb_table=my-terraform-lock-table,encrypt=true"
build:
commands:
- echo "Generando plan de Terraform..."
- terraform plan -out=tfplan.out -var="app_bucket_name=my-unique-app-bucket-name-12345" -var="environment=dev"
artifacts:
files:
- terraform/tfplan.out
base-directory: 'terraform'
buildspec_terraform_apply.yml
Este archivo aplicará el plan de Terraform generado previamente.
version: 0.2
phases:
install:
runtime-versions:
python: 3.8
commands:
- yum install -y unzip
- curl -LO https://releases.hashicorp.com/terraform/1.7.5/terraform_1.7.5_linux_amd64.zip
- unzip terraform_1.7.5_linux_amd64.zip
- mv terraform /usr/local/bin/
- terraform -v
pre_build:
commands:
- echo "Inicializando Terraform..."
- cd terraform
- terraform init -backend-config="bucket=my-terraform-state-bucket-unique-name-123,key=terraform/state/app.tfstate,region=us-east-1,dynamodb_table=my-terraform-lock-table,encrypt=true"
build:
commands:
- echo "Aplicando plan de Terraform..."
- terraform apply -auto-approve tfplan.out
buildspec_app_deploy.yml
Este archivo se encargará de copiar los archivos de la aplicación al bucket S3. Necesitará el nombre del bucket de la aplicación, que obtendremos dinámicamente o lo pasaremos como variable.
version: 0.2
phases:
install:
runtime-versions:
python: 3.8
build:
commands:
- echo "Copiando archivos de la aplicación a S3..."
- aws s3 sync app/ s3://my-unique-app-bucket-name-12345 --delete
artifacts:
files:
- '**/*'
base-directory: 'app'
👷 Creación de la Aplicación Web Estática
Crea los siguientes archivos dentro de la carpeta app/ de tu repositorio:
app/index.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mi Aplicación CD con Terraform</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>¡Hola desde el Despliegue Continuo!</h1>
<p>Esta aplicación ha sido desplegada automáticamente con Terraform y AWS CodePipeline.</p>
<p id="timestamp"></p>
</div>
<script src="script.js"></script>
</body>
</html>
app/style.css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background-color: #f0f2f5;
color: #333;
}
.container {
background-color: #fff;
padding: 30px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
}
h1 {
color: #007bff;
}
p {
font-size: 1.1em;
}
app/script.js
document.getElementById('timestamp').innerText = 'Último despliegue: ' + new Date().toLocaleString();
☁️ Configuración del Pipeline en AWS (CodePipeline y CodeBuild)
La configuración de CodePipeline y CodeBuild se puede hacer con la consola de AWS o, irónicamente, con Terraform. Para este tutorial, haremos la configuración inicial a través de la consola para mayor claridad y luego mencionaremos cómo se podría terraformar el pipeline mismo.
1. 🔑 Crear Roles de IAM
Necesitarás un rol de IAM para CodePipeline y otro para CodeBuild.
Rol para CodePipeline
- Nombre:
CodePipelineServiceRole - Políticas adjuntas:
AWSCodePipelineFullAccess(para simplicidad, en producción usa menos privilegios)AmazonS3FullAccess(para el bucket de artefactos)AWSCodeBuildFullAccess(para invocar CodeBuild)IAMFullAccess(para crear roles de CodeBuild, etc., en producción minimiza)
Rol para CodeBuild
- Nombre:
CodeBuildServiceRole - Políticas adjuntas:
AWSCodeBuildAdminAccess(para simplicidad)AmazonS3FullAccess(para el bucket de artefactos de CodePipeline y el bucket de la aplicación)AmazonDynamoDBFullAccess(para el bloqueo de estado de Terraform)CloudWatchLogsFullAccess(para logs)IAMFullAccess(para operaciones de IAM, si Terraform las necesita)
2. 🗄️ Crear un Bucket S3 para Artefactos de CodePipeline
CodePipeline necesita un bucket S3 para almacenar los artefactos entre las etapas. Crea uno manualmente:
aws s3 mb s3://my-codepipeline-artifacts-unique-name-123 --region us-east-1
3. 🏗️ Crear Proyectos de CodeBuild
Iremos a la consola de AWS CodeBuild y crearemos dos proyectos:
a) TerraformPlanProject
- Nombre del Proyecto:
TerraformPlanProject - Fuente:
No source(CodePipeline lo proporcionará) - Entorno:
- Imagen gestionada: Ubuntu,
aws/codebuild/standard:6.0(o la más reciente con soporte para Terraform y AWS CLI) - Privileged: Habilitar si necesitas Docker (no para este caso directo, pero buena práctica)
- Rol de servicio:
CodeBuildServiceRole
- Imagen gestionada: Ubuntu,
- Buildspec:
Usar un archivo buildspecy especificarbuildspec_terraform_plan.yml - Artefactos:
Tipo: Amazon S3,Nombre del bucket: my-codepipeline-artifacts-unique-name-123(este es el bucket de artefactos de CodePipeline),Ruta de salida: tfplan-artifact
b) TerraformApplyProject
- Nombre del Proyecto:
TerraformApplyProject - Fuente:
No source - Entorno:
- Imagen gestionada: Ubuntu,
aws/codebuild/standard:6.0 - Privileged: Habilitar
- Rol de servicio:
CodeBuildServiceRole
- Imagen gestionada: Ubuntu,
- Buildspec:
Usar un archivo buildspecy especificarbuildspec_terraform_apply.yml - Artefactos:
Ninguno
c) AppDeployProject
- Nombre del Proyecto:
AppDeployProject - Fuente:
No source - Entorno:
- Imagen gestionada: Ubuntu,
aws/codebuild/standard:6.0 - Privileged: Habilitar
- Rol de servicio:
CodeBuildServiceRole
- Imagen gestionada: Ubuntu,
- Buildspec:
Usar un archivo buildspecy especificarbuildspec_app_deploy.yml - Artefactos:
Ninguno
4. 🚀 Crear el Pipeline de CodePipeline
Ve a la consola de AWS CodePipeline y crea un nuevo pipeline.
Etapa 1: Configuración del Pipeline
- Nombre del pipeline:
MyTerraformAppCDPipeline - Rol de servicio:
CodePipelineServiceRole - Almacenamiento de artefactos:
Ubicación predeterminada(o especifica tu bucketmy-codepipeline-artifacts-unique-name-123)
Etapa 2: Etapa de Origen (Source Stage)
- Proveedor de origen:
GitHub (Version 2) - Conexión: Crea una nueva conexión a GitHub si no tienes una, usando tu PAT.
- Repositorio: Tu repositorio de GitHub (ej.
my-cd-terraform-pipeline) - Rama:
main(o la rama que uses) - Detectar cambios en GitHub: Marca la casilla para que el pipeline se inicie automáticamente.
- Nombre de salida de artefacto:
SourceArtifact
Etapa 3: Etapa de Compilación/Plan (Build/Plan Stage)
- Nombre de la etapa:
BuildTerraformPlanAndApp - Proveedor de acción:
AWS CodeBuild - Nombre de proyecto:
TerraformPlanProject - Entrada de artefactos:
SourceArtifact - Salida de artefactos:
TerraformPlanArtifact
Etapa 4: Etapa de Aprobación (Approval Stage)
- Nombre de la etapa:
ManualApproval - Proveedor de acción:
Aprobación manual - Nombre de la acción:
ReviewTerraformPlan - Revisor: Opcional (tu correo electrónico o SNS topic para notificaciones)
- Comentarios: "Revisa el plan de Terraform antes de aplicar. El plan se encuentra en el artefacto 'TerraformPlanArtifact'."
Etapa 5: Etapa de Aplicación de Infraestructura (Infrastructure Apply Stage)
- Nombre de la etapa:
ApplyTerraform - Proveedor de acción:
AWS CodeBuild - Nombre de proyecto:
TerraformApplyProject - Entrada de artefactos:
TerraformPlanArtifact(este es el artefacto que contiene eltfplan.out) - Salida de artefactos:
TerraformApplyArtifact(vacío, pero se usa para conectar)
Etapa 6: Etapa de Despliegue de Aplicación (App Deploy Stage)
- Nombre de la etapa:
DeployApplication - Proveedor de acción:
AWS CodeBuild - Nombre de proyecto:
AppDeployProject - Entrada de artefactos:
SourceArtifact(la aplicación está en el repositorio fuente original) - Salida de artefactos:
Ninguno
▶️ Probando el Pipeline
Una vez que hayas configurado todo, puedes iniciar el pipeline manualmente o empujar un cambio a tu rama main en GitHub.
1. Iniciar el Pipeline
Ve a la consola de CodePipeline y haz clic en Liberar cambio para iniciar la ejecución.
2. Observar la Etapa de BuildTerraformPlanAndApp
Esta etapa ejecutará buildspec_terraform_plan.yml. Deberías ver logs que indican la inicialización y la generación del plan de Terraform.
3. Aprobación Manual
El pipeline se pausará en la etapa ManualApproval. Para ver el plan de Terraform, haz clic en Detalles de la acción de aprobación. Esto te llevará al artefacto de salida de la etapa anterior (TerraformPlanArtifact), donde puedes descargar el archivo tfplan.out. Usa terraform show tfplan.out localmente para revisar los cambios propuestos.
Una vez revisado, haz clic en Aprobar para continuar el pipeline.
4. Observar la Etapa ApplyTerraform
Esta etapa ejecutará buildspec_terraform_apply.yml. Deberías ver logs que confirman que Terraform ha aplicado los cambios, creando el bucket S3 para la aplicación.
5. Observar la Etapa DeployApplication
Esta etapa ejecutará buildspec_app_deploy.yml, copiando el contenido de tu carpeta app/ al bucket S3 recién creado. Los logs confirmarán la sincronización.
6. Verificar el Despliegue
Después de que el pipeline finalice con éxito, ve a la consola de AWS S3, busca tu bucket de aplicación (ej. my-unique-app-bucket-name-12345). Ve a las propiedades del bucket y en la sección de Alojamiento de sitios web estáticos, copia el Endpoint del sitio web. Pégalo en tu navegador y deberías ver tu aplicación web estática desplegada.
💡 Mejoras y Consideraciones Adicionales
Este setup es una base sólida, pero aquí hay algunas ideas para llevarlo al siguiente nivel:
- Terraformar el Pipeline: Puedes definir el propio CodePipeline, CodeBuild Projects y roles de IAM usando Terraform. Esto es ideal para tener toda tu infraestructura como código.
- Múltiples Entornos: Extiende el pipeline para desplegar en entornos de
stagingyproductioncon aprobaciones manuales y variables de entorno específicas. - Pruebas Automatizadas: Introduce etapas adicionales en CodePipeline para ejecutar pruebas unitarias, de integración o e2e (end-to-end) con CodeBuild o AWS Device Farm.
- Seguridad y Permisos: Refina los roles de IAM para seguir el principio de mínimo privilegio estricto. Considera el uso de AWS Secrets Manager para variables sensibles.
- Notificaciones: Configura notificaciones de SNS para recibir alertas sobre el estado del pipeline.
- Rollbacks: Implementa estrategias de rollback automatizadas para revertir a versiones anteriores en caso de fallos.
- Infraestructura Compleja: Utiliza módulos de Terraform para gestionar infraestructuras más complejas (VPC, EC2, RDS, etc.) dentro del mismo pipeline.
❓ Preguntas Frecuentes (FAQ)
¿Por qué usar S3 y DynamoDB para el backend de Terraform?
Para mantener el estado de Terraform de forma remota, segura y colaborativa. S3 almacena el archivo de estado en un lugar centralizado, mientras que DynamoDB proporciona un bloqueo de estado para evitar que múltiples usuarios o procesos apliquen cambios concurrentemente y corrompan el estado.¿Cómo puedo manejar variables sensibles en mi pipeline?
Evita codificar valores sensibles directamente en tus archivos `buildspec` o `terraform`. Utiliza AWS Systems Manager Parameter Store o AWS Secrets Manager para almacenar estos valores y recuperarlos durante las etapas de CodeBuild. CodePipeline puede inyectar variables de entorno desde estas fuentes.¿Qué ocurre si el `terraform apply` falla?
Si un `terraform apply` falla, la etapa de CodeBuild fallará y el pipeline se detendrá. Deberás revisar los logs de CodeBuild para identificar el problema, corregirlo en tu código fuente y volver a iniciar el pipeline. Terraform mantendrá el estado del último `apply` exitoso o se quedará en un estado parcial si el fallo ocurrió a mitad del proceso.✅ Conclusión
Has completado un viaje completo por la implementación de un pipeline de Despliegue Continuo con Terraform y AWS CodePipeline. Esta poderosa combinación te permite automatizar la gestión de tu infraestructura y tus aplicaciones, fomentando la colaboración, la reproducibilidad y la velocidad de entrega. Al mantener tu infraestructura como código y automatizar su despliegue, reduces errores humanos y aseguras que tus entornos estén siempre sincronizados con tu repositorio de control de versiones.
¡Felicidades por dar un gran paso hacia la madurez de DevOps en tus proyectos!
Tutoriales relacionados
- Gestionando Redes y Conectividad con Terraform y AWS VPC: Una Guía Esencialintermediate20 min
- Gestionando Permisos de AWS con Terraform: IAM Roles, Políticas y Usuariosintermediate20 min
- Gestionando Servidores sin Estado con Terraform y AWS Auto Scaling Groupsintermediate20 min
- Automatización de la Configuración de Kubernetes con Terraform: Un Enfoque Declarativointermediate15 min
- Gestionando Secretos Sensibles en Terraform con HashiCorp Vault: Un Enfoque Segurointermediate25 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!