Agent-almanac provision-infrastructure-terraform
git clone https://github.com/pjt222/agent-almanac
T=$(mktemp -d) && git clone --depth=1 https://github.com/pjt222/agent-almanac "$T" && mkdir -p ~/.claude/skills && cp -r "$T/i18n/es/skills/provision-infrastructure-terraform" ~/.claude/skills/pjt222-agent-almanac-provision-infrastructure-terraform-e2bfe5 && rm -rf "$T"
i18n/es/skills/provision-infrastructure-terraform/SKILL.mdAprovisionar Infraestructura con Terraform
Implementa infraestructura como código usando Terraform para aprovisionar, versionar y gestionar recursos en la nube en AWS, Azure, GCP y otros proveedores.
Cuándo Usar
- Al aprovisionar nueva infraestructura en la nube (VPCs, cómputo, almacenamiento, bases de datos)
- Al migrar de ClickOps o CloudFormation a IaC declarativa
- Al gestionar infraestructura multientorno (dev, staging, producción)
- Al implementar patrones de infraestructura reproducibles entre equipos
- Al versionar cambios de infraestructura junto al código de aplicación
- Al aplicar estándares de infraestructura mediante módulos reutilizables
Entradas
- Requerido: CLI de Terraform instalado (
)terraform --version - Requerido: Credenciales del proveedor de nube (cuentas de servicio AWS, Azure, GCP)
- Requerido: Configuración del backend de estado remoto (S3, Azure Storage, Terraform Cloud)
- Opcional: Infraestructura existente para importar o migrar
- Opcional: Terraform Cloud/Enterprise para colaboración en equipo
- Opcional: Hooks de pre-commit para validación y formateo
Procedimiento
Consulta Ejemplos Extendidos para archivos de configuración completos y plantillas.
Paso 1: Inicializar la Estructura del Proyecto Terraform
Crea una estructura de directorios organizada con configuración del backend y configuración del proveedor.
# Create project structure mkdir -p terraform/{modules,environments/{dev,staging,prod}} cd terraform # Create backend configuration cat > backend.tf <<'EOF' terraform { required_version = ">= 1.6" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } backend "s3" { bucket = "my-terraform-state" key = "infrastructure/terraform.tfstate" region = "us-east-1" encrypt = true dynamodb_table = "terraform-lock" # Workspace-specific state files workspace_key_prefix = "env" } } provider "aws" { region = var.aws_region default_tags { tags = { ManagedBy = "Terraform" Environment = terraform.workspace Project = var.project_name } } } EOF # Create variables file cat > variables.tf <<'EOF' variable "aws_region" { description = "AWS region for resources" type = string default = "us-east-1" } variable "project_name" { description = "Project name for resource naming and tagging" type = string validation { condition = length(var.project_name) > 0 && length(var.project_name) <= 32 error_message = "Project name must be 1-32 characters" } } variable "environment" { description = "Environment name (dev, staging, prod)" type = string validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "Environment must be dev, staging, or prod" } } EOF # Initialize Terraform terraform init
Esperado: Terraform se inicializa exitosamente, descarga plugins del proveedor, configura el backend remoto. El directorio
.terraform/ se crea con los binarios del proveedor. Se verifica la conexión al backend de estado.
En caso de fallo: Si la inicialización del backend falla, verifica que el bucket S3 existe y que los permisos IAM permiten
s3:GetObject, s3:PutObject, dynamodb:GetItem, dynamodb:PutItem. Para fallos de descarga del proveedor, comprueba la conectividad de red y la configuración de proxy corporativo. Ejecuta terraform init -upgrade para actualizar los proveedores.
Paso 2: Crear Módulos de Infraestructura Reutilizables
Construye módulos componibles para VPC, cómputo e infraestructura de datos con validación de entradas.
# modules/vpc/main.tf variable "vpc_cidr" { description = "CIDR block for VPC" type = string default = "10.0.0.0/16" } variable "availability_zones" { description = "List of AZs to use" type = list(string) } variable "project_name" { description = "Project name for resource naming" type = string } variable "environment" { description = "Environment name" type = string } locals { common_tags = { Project = var.project_name Environment = var.environment Module = "vpc" } } resource "aws_vpc" "main" { cidr_block = var.vpc_cidr enable_dns_hostnames = true enable_dns_support = true tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-vpc" }) } resource "aws_subnet" "public" { count = length(var.availability_zones) vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index) availability_zone = var.availability_zones[count.index] map_public_ip_on_launch = true tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-public-${var.availability_zones[count.index]}" Type = "public" }) } resource "aws_subnet" "private" { count = length(var.availability_zones) vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + 100) availability_zone = var.availability_zones[count.index] tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-private-${var.availability_zones[count.index]}" Type = "private" }) } resource "aws_internet_gateway" "main" { vpc_id = aws_vpc.main.id tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-igw" }) } resource "aws_eip" "nat" { count = length(var.availability_zones) domain = "vpc" tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-nat-eip-${var.availability_zones[count.index]}" }) depends_on = [aws_internet_gateway.main] } resource "aws_nat_gateway" "main" { count = length(var.availability_zones) allocation_id = aws_eip.nat[count.index].id subnet_id = aws_subnet.public[count.index].id tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-nat-${var.availability_zones[count.index]}" }) depends_on = [aws_internet_gateway.main] } # modules/vpc/outputs.tf output "vpc_id" { description = "VPC ID" value = aws_vpc.main.id } output "public_subnet_ids" { description = "List of public subnet IDs" value = aws_subnet.public[*].id } output "private_subnet_ids" { description = "List of private subnet IDs" value = aws_subnet.private[*].id } output "nat_gateway_ips" { description = "List of NAT Gateway public IPs" value = aws_eip.nat[*].public_ip }
Esperado: El módulo crea una VPC con subredes públicas/privadas en múltiples AZs, una puerta de enlace de internet y puertas de enlace NAT con EIPs. Los valores de salida exponen los IDs de recursos para módulos descendentes.
En caso de fallo: Para errores de superposición de CIDR, ajusta el cálculo de
cidrsubnet() o valida que el CIDR de la VPC no entre en conflicto con redes existentes. Para errores de dependencia, verifica que los bloques depends_on garantizan el orden correcto de creación de recursos. Usa terraform graph | dot -Tpng > graph.png para visualizar las dependencias.
Paso 3: Implementar Configuraciones Específicas por Entorno
Crea espacios de trabajo de entorno con anulaciones de variables y fuentes de datos.
# environments/prod/main.tf terraform { required_version = ">= 1.6" } # Import shared backend and provider config # ... (see EXAMPLES.md for complete configuration)
Esperado: La configuración específica del entorno crea infraestructura de tamaño productivo con 3 AZs, tipos de instancias más grandes y configuraciones de seguridad de producción. Las fuentes de datos resuelven la última AMI. Los archivos de plantilla se renderizan con variables de entorno.
En caso de fallo: Para errores de espacio de trabajo, crea el espacio de trabajo con
terraform workspace new prod. Para fallos de fuente de datos, verifica que las credenciales AWS tengan permisos ec2:DescribeImages. Para errores de renderizado de plantillas, valida que los tipos de variables coincidan con las expectativas de la plantilla.
Paso 4: Ejecutar el Flujo de Trabajo Plan y Apply
Ejecuta el plan de Terraform, revisa los cambios y aplica con flujo de aprobación.
# Format code terraform fmt -recursive # Validate configuration terraform validate # ... (see EXAMPLES.md for complete configuration)
Para integración automatizada de CI/CD:
# .github/workflows/terraform.yml name: Terraform on: pull_request: paths: # ... (see EXAMPLES.md for complete configuration)
Esperado: El plan muestra adiciones/cambios/eliminaciones de recursos. No se detecta desvío. Apply crea/actualiza recursos sin errores. Las salidas contienen los valores esperados. El flujo de trabajo CI comenta el plan en los PRs y aplica automáticamente en fusiones a la rama main.
En caso de fallo: Para fallos del plan, ejecuta
terraform validate para detectar errores de sintaxis. Para errores de bloqueo de estado, identifica al titular del bloqueo con aws dynamodb get-item --table-name terraform-lock --key '{"LockID":{"S":"terraform-state-bucket/key"}}' y fuerza el desbloqueo si está obsoleto. Para fallos de apply, comprueba los registros de CloudWatch para errores específicos del proveedor. Usa terraform show para inspeccionar el estado actual.
Paso 5: Gestionar el Estado e Implementar Detección de Desvío
Configura el bloqueo de estado, la copia de seguridad y la detección automatizada de desvío.
# Create DynamoDB table for state locking cat > state-backend.tf <<'EOF' resource "aws_dynamodb_table" "terraform_lock" { name = "terraform-lock" billing_mode = "PAY_PER_REQUEST" hash_key = "LockID" # ... (see EXAMPLES.md for complete configuration)
Para detección automatizada de desvío:
# Create drift detection script cat > scripts/detect-drift.sh <<'EOF' #!/bin/bash set -euo pipefail cd terraform # ... (see EXAMPLES.md for complete configuration)
Esperado: El backend de estado configurado con versionado y cifrado. La detección de desvío identifica cambios fuera de banda. Las operaciones de estado (list, show, mv, import) se ejecutan sin errores. Las verificaciones automáticas de desvío se ejecutan según programación y envían alertas.
En caso de fallo: Para tiempos de espera de bloqueo de estado, verifica que la tabla DynamoDB existe y tiene el esquema de clave correcto. Para problemas de versionado, comprueba el estado del versionado del bucket S3 con
aws s3api get-bucket-versioning --bucket bucket-name. Para fallos de importación, verifica que el recurso existe y que la configuración de Terraform coincide con los atributos reales del recurso.
Paso 6: Implementar Pruebas y Documentación de Módulos
Agrega pruebas automatizadas con Terratest y genera documentación.
// test/vpc_test.go package test import ( "testing" # ... (see EXAMPLES.md for complete configuration)
Generar documentación:
# Install terraform-docs go install github.com/terraform-docs/terraform-docs@latest # Generate module documentation terraform-docs markdown table modules/vpc > modules/vpc/README.md # ... (see EXAMPLES.md for complete configuration)
Esperado: Terratest valida que el módulo crea los recursos esperados con la configuración correcta. La documentación se genera automáticamente a partir de descripciones de variables y definiciones de salidas. Los hooks de pre-commit aplican el formateo y la validación antes de los commits.
En caso de fallo: Para fallos de Terratest, verifica las credenciales AWS y las cuotas. Para pruebas de larga duración, implementa ejecución paralela con
t.Parallel(). Para errores de generación de documentación, verifica que todas las variables tienen atributos description. Para fallos de pre-commit, ejecuta terraform fmt manualmente y corrige los errores de validación.
Validación
- Backend configurado con cifrado, versionado y bloqueo de estado
- Todos los módulos tienen validación de entradas y valores de salida
- Los espacios de trabajo aíslan el estado específico del entorno
-
no muestra cambios inesperados después del applyterraform plan - La detección de desvío se ejecuta automáticamente y alerta sobre cambios
- Los módulos se prueban con Terratest o un marco similar
- La documentación se genera automáticamente y se mantiene actualizada
- Los secretos se gestionan a través de AWS Secrets Manager, no codificados
- La estimación de costos está integrada (Infracost o similar)
- El radio de explosión se minimiza con estado separado por entorno
Errores Comunes
-
Valores codificados: Evita codificar IDs de AMI, AZs o valores específicos de cuenta. Usa fuentes de datos y variables.
-
Bloques lifecycle faltantes: Los recursos se recrean inesperadamente. Agrega
para prevenir tiempo de inactividad durante las actualizaciones.lifecycle { create_before_destroy = true } -
Sin bloqueo de estado: Los applies concurrentes corrompen el estado. Siempre usa una tabla DynamoDB para el bloqueo con el backend S3.
-
IAM demasiado permisivo: La cuenta de servicio de Terraform tiene acceso de administrador completo. Implementa políticas de mínimo privilegio limitadas a los recursos gestionados.
-
Sin restricciones de versión: Las actualizaciones del proveedor rompen la infraestructura. Fija las versiones del proveedor con restricciones
.version = "~> 5.0" -
Secretos en el estado: Los valores sensibles se almacenan en texto plano en el archivo de estado. Usa
en las salidas, almacena los secretos en AWS Secrets Manager y referencialos mediante fuentes de datos.sensitive = true -
Sin estrategia de copia de seguridad: El archivo de estado se pierde o corrompe sin un plan de recuperación. Habilita el versionado S3, implementa copias de seguridad regulares del estado y prueba los procedimientos de recuperación.
-
Configuración monolítica: Un único archivo de estado gestiona toda la infraestructura. Divídelo en límites lógicos (redes, cómputo, datos) para reducir el radio de explosión.
Habilidades Relacionadas
- Control de versiones para código Terraformconfigure-git-repository
- Flujos de trabajo automatizados de Terraform con GitHub Actionsbuild-ci-cd-pipeline
- Integración de ArgoCD/Flux con Terraformimplement-gitops-workflow
- Gestión de secretos en clústeres aprovisionados por Terraformmanage-kubernetes-secrets
- Uso del proveedor Kubernetes de Terraformdeploy-to-kubernetes