Habilidad de Estandarización Terraform
Esta habilidad guía al agente en la refactorización de código Terraform crudo, brownfield, importado (por ejemplo, desde Terraformer o terraform import), o heredado en una arquitectura modular y lista para producción según los estándares de HashiCorp.
Cuándo Usar Esta Habilidad
Activa esta habilidad cuando:
- El usuario solicite "estandarizar", "refactorizar", "limpiar" o "modularizar" configuraciones Terraform.
- El código Terraform contenga cadenas hardcodeadas, números mágicos o IDs estáticos de la nube (
ami-*,vpc-*,subnet-*,sg-*,arn:*). - Configuraciones monolíticas de un solo archivo (
main.tfcon todo) necesiten separarse en diseños estándar de múltiples archivos (provider.tf,variables.tf,locals.tf,outputs.tf,backend.tf). - Los recursos de la nube necesiten taxonomías de etiquetado estandarizadas (por ejemplo,
Environment,Project,ManagedBy = "terraform"). - El código base carezca de definiciones de variables, restricciones de tipo o bloques de descripción.
Flujo de Trabajo de Estandarización
Sigue este procedimiento de 5 fases al estandarizar cualquier código base Terraform:
┌────────────────────────────────────────────────────────┐
│ Fase 1: Auditoría y Detección de Malas Prácticas │
│ • Detectar literales hardcodeados, IDs, regiones y CIDRs│
│ • Inspeccionar la estructura de archivos existente │
└──────────────────────────┬─────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Fase 2: Establecer Arquitectura de Archivos Estándar │
│ • Extraer provider.tf, versions.tf y backend.tf │
│ • Crear variables.tf, locals.tf y outputs.tf │
└──────────────────────────┬─────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Fase 3: Parametrizar Literales en Variables y Locales │
│ • Extraer variables de entorno/región con tipos │
│ • Construir local.common_tags para etiquetado unificado │
│ • Crear terraform.tfvars.example │
└──────────────────────────┬─────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Fase 4: Enlace de Referencias Dinámicas y Renombrado │
│ • Reemplazar IDs de recursos estáticos por referencias │
│ • Reemplazar AMIs/subnets hardcodeados con data sources│
│ • Estandarizar etiquetas de recursos (snake_case, sin │
│ tipos) │
└──────────────────────────┬─────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Fase 5: Formatear, Validar y Verificar │
│ • Ejecutar `terraform fmt -recursive` │
│ • Ejecutar `terraform validate` │
│ • Verificar con `terraform plan` (sin desvíos) │
└────────────────────────────────────────────────────────┘
Instrucciones Paso a Paso
1. Fase 1: Auditoría y Descubrimiento
Antes de hacer ediciones, escanea el directorio destino para identificar:
- Disponibilidad de CLI: Ejecuta
terraform version(otofu versionpara OpenTofu) para determinar si las comprobaciones de formateo, validación y planificación de CLI pueden ejecutarse en la Fase 5. - Archivos estándar faltantes: Verifica si existen
versions.tf,provider.tf,variables.tf,locals.tfyoutputs.tf. - IDs y literales hardcodeados:
- Identificadores de nube:
ami-[a-f0-9]+,vpc-[a-f0-9]+,subnet-[a-f0-9]+,sg-[a-f0-9]+,igw-[a-f0-9]+, enlaces completos de GCP (https://www.googleapis.com/compute/...), IDs deprojectestáticos - Bloques CIDR:
"10.0.0.0/16","0.0.0.0/0" - Tipos de instancia:
"t3.micro","m5.large","e2-standard-4" - Regiones y números de cuenta estáticos:
"us-east-1","us-central1","123456789012" - Credenciales sin procesar o secretos no cifrados
- Identificadores de nube:
- Línea base de formateo: Si Terraform está instalado, verifica si los archivos cumplen con
terraform fmt -check -recursive.
2. Fase 2: Crear Archivos Centrales Estándar
Asegúrate de que la configuración destino esté dividida en archivos distintos y de responsabilidad única:
provider.tf / versions.tf
Centraliza la configuración de Terraform, la versión mínima de CLI y los proveedores requeridos (adapta para AWS, GCP o Azure):
# versions.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
# Ejemplo GCP:
# google = {
# source = "hashicorp/google"
# version = "~> 5.5"
# }
}
}
# provider.tf (AWS)
provider "aws" {
region = var.aws_region
default_tags {
tags = local.common_tags
}
}
# provider.tf (alternativa GCP)
# provider "google" {
# project = var.project_id
# region = var.region
# default_labels = local.common_labels
# }
Estrategia de Etiquetado Único: Usa o
default_tags/default_labelsa nivel de proveedor (poniendo solo claves específicas del recurso en recursos individuales) omerge(local.common_tags, ...)/merge(local.common_labels, ...)en cada recurso — nunca dupliques ambos.
backend.tf (si se usa estado remoto)
Configuración de almacenamiento de estado separada (s3 para AWS, gcs para GCP, azurerm para Azure):
terraform {
backend "s3" {
bucket = "myorg-terraform-state"
key = "services/app/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
# Ejemplo GCP:
# terraform {
# backend "gcs" {
# bucket = "myorg-terraform-state"
# prefix = "services/app"
# }
# }
3. Fase 3: Convertir Cadenas Simples en Variables y Locales
Extrae todos los parámetros hardcodeados en variables.tf y locals.tf:
Reglas de variables.tf:
- Declara siempre
type: (string,number,bool,list(string),map(string)). - Incluye siempre
description: Explica el propósito y el formato esperado. - Proporciona
defaultsolo cuando tenga sentido: Opciones de configuración comoenvironmentoaws_regionpueden tener valores por defecto, pero los valores sensibles no deben tener valores por defecto hardcodeados. - Marca secretos como
sensitive = true: Variables de contraseñas, tokens y secretos deben marcarse como sensibles.
Ejemplo de variables.tf:
variable "aws_region" {
type = string
description = "La región de AWS donde se aprovisionarán los recursos."
default = "us-east-1"
}
variable "environment" {
type = string
description = "Nombre del entorno de despliegue (ej., dev, staging, prod)."
default = "production"
validation {
condition = contains(["dev", "staging", "production"], var.environment)
error_message = "El entorno debe ser uno de: dev, staging, production."
}
}
variable "instance_type" {
type = string
description = "Tamaño de la instancia EC2 para el clúster de la aplicación."
default = "t3.medium"
}
variable "vpc_cidr" {
type = string
description = "Bloque CIDR base para la Nube Privada Virtual."
default = "10.0.0.0/16"
}
Reglas de locals.tf:
Usa locals para valores derivados internamente, prefijos de entorno y etiquetas comunes:
locals {
name_prefix = "${var.project_name}-${var.environment}"
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "terraform"
}
}
terraform.tfvars.example
Crea siempre un archivo de ejemplo de valores de variables para que los compañeros sepan cómo proporcionar valores:
aws_region = "us-east-1"
environment = "production"
project_name = "checkout-service"
4. Fase 4: Reemplazar Valores Estáticos con Referencias Dinámicas (Guardarraíles de Desvío Cero)
Convierte IDs de nube estáticos y referencias a recursos inconexas en expresiones HCL nativas mientras proteges la infraestructura en vivo de reemplazos accidentales:
Guardarraíl de Inmutabilidad y ForceNew (Crítico para Brownfield / Terraformer)
- Distingue las direcciones Terraform de los atributos de
nameen la nube: Renombrar una etiqueta de recurso Terraform (tfer--prod-main-vpc→main) con un bloquemoved {}es seguro y sin tiempo de inactividad. Cambiar un atributo denameen la nube (por ejemplo,google_compute_network.name,google_compute_subnetwork.name,google_compute_firewall.name,google_compute_instance.name, oaws_s3_bucket.bucket) esForceNewy destruye y recrea la infraestructura en vivo. Parametriza los nombres de nube existentes envariables.tf/terraform.tfvarsen lugar de reescribirlos como"${local.name_prefix}-...". - Fija imágenes/AMIs importadas en importaciones brownfield: Cambiar un
ami-*hardcodeado odebian-12-bookworm-v20240515a una búsquedamost_recent = true/ de familiadatareemplazará las VMs existentes si existe una imagen más nueva. Fija el nombre o ID exacto de la imagen/AMI importada a través de una variable/filtrodatacuando se requiera desvío cero, y solo habilitamost_recent = truecuando el usuario opte por actualizaciones de imagen. - Mantén el endurecimiento de seguridad separado de la refactorización sin desvíos: No cambies silenciosamente atributos de comportamiento/seguridad (por ejemplo,
http_tokens"optional"→"required",public_access_prevention"inherited"→"enforced", o ámbitos OAuth de VM) durante una migración sin desvíos. Trata el endurecimiento como una opción explícita o enumera claramente cada diferencia intencional. - Preserva claves de etiquetas heredadas al migrar: Si los recursos existentes usan
env = "prod", preserva las claves existentes (o agrega claves estandarizadas junto a ellas) a menos que se apruebe el reemplazo de claves.
Lista de Verificación de Limpieza Específica de Terraformer
- Elimina atributos de estado de solo lectura calculados (
tags_allen AWS;effective_labelsyterraform_labelsen GCP) y atributos vacíos/por defecto ("",[],{},false,0). - Elimina sub-bloques conflictivos volcados del estado (por ejemplo,
network_interfaceprimario de EC2 cuando se establecesubnet_id). - Reemplaza interpolaciones heredadas
${data.terraform_remote_state.*.outputs.*}y URLs completas de auto-enlace con referencias directas a recursos (por ejemplo,google_compute_network.main.id) o búsquedasdata. - Colapsa recursos duplicados numerados (
vm-01,vm-02) en un solo recursofor_eachemparejado con bloquesmoved(to = google_compute_instance.api["01"]). - Extrae cadenas de
user_data/startup-scriptescapadas con\nen línea en archivosscripts/cargados mediantefile()otemplatefile(). - Al mutar
scopesomachine_typede VM en GCP, asegúrate de queallow_stopping_for_update = trueesté configurado si se pretenden actualizaciones en el lugar.
Anti-patrón (Crudo / Hardcodeado):
# MAL: IDs hardcodeados y cadenas literales
resource "aws_instance" "tfer--i-0123456789abcdef0" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.medium"
subnet_id = "subnet-01234567"
vpc_security_group_ids = [
"sg-0abcdef1234567890"
]
}
Patrón Estándar (Refactorizado):
# 1. Preservación del estado: Indica a Terraform que este recurso fue renombrado (evita destruir y recrear)
moved {
from = aws_instance.tfer--i-0123456789abcdef0
to = aws_instance.web
}
# 2. Búsqueda dinámica de imagen base (fija el nombre exacto para brownfield sin desvíos, o most_recent para greenfield)
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = [var.ami_name_pattern]
}
}
# 3. Definición estandarizada del recurso con referencias dinámicas
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
subnet_id = aws_subnet.private[0].id
vpc_security_group_ids = [aws_security_group.web.id]
# Omite common_tags aquí si provider.tf ya establece default_tags
tags = {
Name = var.instance_name
}
}
5. Fase 5: Formateo, Auditoría de Desvíos y Validación
-
Lista de Verificación de Auditoría de Desvíos Pre-Plan (realizar siempre, especialmente si la CLI
terraformno está instalada localmente):- Compara el
namede cada recurso en la nube,image/ami,location/zone,tags/labels, y configuraciones de seguridad/red contra la fuente importada. - Verifica que no se hayan cambiado campos
ForceNewaccidentalmente y que cada recursotfer--*tenga un bloquemoved {}coincidente. - Separa e informa cualquier diferencia intencional (por ejemplo, endurecimiento de seguridad) de la refactorización sin desvíos.
- Compara el
-
Ejecuta comprobaciones de CLI de Terraform (cuando esté instalado):
# 1. Formatear código a la sangría HCL canónica estándar
terraform fmt -recursive
# 2. Inicializar plugins de proveedores (usa -backend=false al validar sin conexión/sin estado) y validar sintaxis
terraform init -backend=false
terraform validate
# 3. Si se trabaja en un entorno activo con estado, asegúrate de que el plan no produzca diferencias no intencionadas
terraform plan
- Resume los cambios de
terraform plan(cuando se detecte alguna diferencia): Siterraform planinforma algo distinto aNo changes, detente antes de recomendarapplyy presenta un resumen estructurado:- Totales del plan: Indica los recuentos exactos de recursos (
X para agregar, Y para cambiar, Z para destruir) y cualquier transición de estadomoved. - Cambios de alto riesgo primero (
-/+reemplazar,-destruir): Destaca cada recurso marcado para reemplazo o eliminación, nombra el atributoForceNewque lo causa (por ejemplo,name,image,boot_disk) y explica cómo solucionarlo (como agregar un bloquemoved {}faltante o restaurar el valor original entfvars). - Actualizaciones en el lugar (
~) y creaciones (+): Enumera cada recurso afectado y los atributos exactos que cambian (por ejemplo,labels,tags,scopes,public_access_prevention). - Desvío intencional vs. no intencionado: Marca claramente qué diferencias son intencionales (endurecimiento de seguridad opt-in o estandarización de etiquetas) versus desvío brownfield no intencionado para que el usuario pueda aprobarlas o revertirlas.
- Totales del plan: Indica los recuentos exactos de recursos (
Guías Detalladas y Referencias
- Consulta Diseño de Archivos Estándar para patrones de agrupación por dominio.
- Consulta Reglas de Refactorización para nomenclatura, guardarraíles
ForceNew, etiquetas y listas de verificación de anti-patrones.