tutoriales.com

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.

Intermedio25 min de lectura17 views
Reportar error

🚀 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.
💡 **Consejo:** Es recomendable tener conocimientos básicos de AWS, Terraform y GitHub para aprovechar al máximo este tutorial.

🛠️ 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.
  • 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:

  1. Fuente: Los cambios se envían a un repositorio de GitHub.
  2. Compilación/Construcción: AWS CodeBuild ejecuta los comandos para preparar la aplicación y/o el código Terraform.
  3. Despliegue de Infraestructura (Terraform Plan/Apply): CodeBuild genera un plan de Terraform y, tras una aprobación manual, lo aplica.
  4. Despliegue de Aplicación (S3): Si la aplicación es web estática, CodeBuild sincroniza los archivos con un bucket S3.
GitHub Evento: Push / Commit AWS CodePipeline Trigger del Pipeline Source Stage Descarga de GitHub Build Stage (CodeBuild) Terraform Plan App Compilation & Artifacts Approval Stage Aprobación Manual Deploy Stage (CodeBuild) Terraform Apply S3 Static Sync

📝 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.

⚠️ **Advertencia:** El bucket S3 para el estado de Terraform debe ser creado antes de que Terraform intente inicializarse para el estado remoto.
# 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
  }
}
📌 **Nota:** Asegúrate de que los nombres de los buckets S3 sean globalmente únicos.

📜 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'
🔥 **Importante:** Reemplaza `my-terraform-state-bucket-unique-name-123` y `my-unique-app-bucket-name-12345` con los nombres reales de tus buckets.

👷 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)
💡 **Consejo:** En un entorno de producción, siempre aplica el principio de **mínimos privilegios** a tus roles de IAM.

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
  • Buildspec: Usar un archivo buildspec y especificar buildspec_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
  • Buildspec: Usar un archivo buildspec y especificar buildspec_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
  • Buildspec: Usar un archivo buildspec y especificar buildspec_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 bucket my-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 el tfplan.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
Pipeline Completo

▶️ 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.

Iniciar Pipeline Ejecución Build (Plan) Revisar tfplan.out (Descarga, terraform show) Aprobar Manualmente Ejecución Build (Apply) Despliegue App S3 Verificar en Navegador

💡 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 staging y production con 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.
Paso 1: **Modularizar Terraform** - Divide tu configuración de Terraform en módulos reutilizables.
Paso 2: **Añadir Etapa de Pruebas** - Inserta una etapa de CodeBuild para ejecutar tests después del despliegue en un entorno de pruebas.
Paso 3: **Implementar Notificaciones** - Configura SNS para alertar sobre el estado del pipeline.
Paso 4: **Integrar CloudFront** - Utiliza CloudFront para servir el contenido de S3 con HTTPS, caché y menor latencia.

❓ 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

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!