Skill de Padronização Terraform
Esta skill guia o agente na refatoração de código Terraform bruto, brownfield, importado (por exemplo, via Terraformer ou terraform import), ou legado, para uma arquitetura modular e padronizada pronta para produção, seguindo os padrões HashiCorp.
Quando Usar Esta Skill
Ative esta skill quando:
- O usuário pedir para "padronizar", "refatorar", "limpar" ou "modularizar" configurações Terraform.
- O código Terraform bruto contiver strings fixas, números mágicos ou IDs estáticos de nuvem (
ami-*,vpc-*,subnet-*,sg-*,arn:*). - Configurações monolíticas em arquivo único (
main.tfcom tudo) precisarem ser separadas em uma estrutura padrão de múltiplos arquivos (provider.tf,variables.tf,locals.tf,outputs.tf,backend.tf). - Recursos de nuvem precisarem de taxonomias de marcação padronizadas (por exemplo,
Environment,Project,ManagedBy = "terraform"). - A base de código não tiver definições de variáveis, restrições de tipo ou blocos de descrição.
Fluxo de Trabalho de Padronização
Siga este procedimento de 5 fases ao padronizar qualquer base de código Terraform:
┌────────────────────────────────────────────────────────┐
│ Fase 1: Auditoria e Identificação de Code Smells │
│ • Detectar literais fixos, IDs, regiões e CIDRs │
│ • Inspecionar estrutura de arquivos vs layout padrão │
└──────────────────────────┬─────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Fase 2: Estabelecer Arquitetura Padrão de Arquivos │
│ • Extrair provider.tf, versions.tf e backend.tf │
│ • Criar variables.tf, locals.tf e outputs.tf │
└──────────────────────────┬─────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Fase 3: Parametrizar Literais em Variáveis e Locals │
│ • Extrair variáveis de ambiente/região com tipos │
│ • Construir local.common_tags para marcação unificada │
│ • Criar terraform.tfvars.example │
└──────────────────────────┬─────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Fase 4: Vinculação de Referências Dinâmicas e Renomeio │
│ • Substituir IDs estáticos de recursos por refs dinâmicas│
│ • Substituir AMIs/sub-redes fixas por data sources │
│ • Padronizar rótulos de recursos (snake_case, sem tipos)│
└──────────────────────────┬─────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Fase 5: Formatar, Validar e Verificar │
│ • Executar `terraform fmt -recursive` │
│ • Executar `terraform validate` │
│ • Verificar com `terraform plan` (drift zero) │
└────────────────────────────────────────────────────────┘
Instruções Passo a Passo
1. Fase 1: Auditoria e Descoberta
Antes de fazer edições, escaneie o diretório alvo para identificar:
- Disponibilidade da CLI: Execute
terraform version(outofu versionpara OpenTofu) para determinar se a formatação, validação e verificações de plan podem ser executadas na Fase 5. - Arquivos padrão ausentes: Verifique se
versions.tf,provider.tf,variables.tf,locals.tfeoutputs.tfexistem. - IDs fixos e literais:
- Identificadores de nuvem:
ami-[a-f0-9]+,vpc-[a-f0-9]+,subnet-[a-f0-9]+,sg-[a-f0-9]+,igw-[a-f0-9]+, self-links completos do GCP (https://www.googleapis.com/compute/...), IDsprojectestáticos - Blocos CIDR:
"10.0.0.0/16","0.0.0.0/0" - Tipos de instância:
"t3.micro","m5.large","e2-standard-4" - Regiões e números de conta estáticos:
"us-east-1","us-central1","123456789012" - Credenciais brutas ou segredos não criptografados
- Identificadores de nuvem:
- Linha de base de formatação: Se o Terraform estiver instalado, verifique se os arquivos estão em conformidade com
terraform fmt -check -recursive.
2. Fase 2: Criar Arquivos Centrais Padrão
Garanta que a configuração alvo seja dividida em arquivos distintos e com responsabilidade única:
provider.tf / versions.tf
Centralize as configurações do Terraform, versão mínima da CLI e provedores obrigatórios (adapte para AWS, GCP ou Azure):
# versions.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
# Exemplo 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
# }
Estratégia Única de Marcação/Rotulagem: Use ou
default_tags/default_labelsno nível do provedor (colocando apenas chaves específicas do recurso nos recursos individuais) oumerge(local.common_tags, ...)/merge(local.common_labels, ...)em cada recurso — nunca duplique ambos.
backend.tf (se estado remoto for usado)
Configure o armazenamento de estado separado (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
}
}
# Exemplo GCP:
# terraform {
# backend "gcs" {
# bucket = "myorg-terraform-state"
# prefix = "services/app"
# }
# }
3. Fase 3: Converter Strings Simples em Variáveis e Locals
Extraia todos os parâmetros fixos para variables.tf e locals.tf:
Regras de variables.tf:
- Sempre declare
type: (string,number,bool,list(string),map(string)). - Sempre inclua
description: Explique o propósito e o formato esperado. - Forneça
defaultapenas quando sensato: Opções de configuração comoenvironmentouaws_regionpodem ter padrões, mas valores sensíveis não devem ter padrões fixos. - Marque segredos como
sensitive = true: Variáveis de senha, token e segredo devem ser marcadas como sensíveis.
Exemplo de variables.tf:
variable "aws_region" {
type = string
description = "A região AWS onde os recursos serão provisionados."
default = "us-east-1"
}
variable "environment" {
type = string
description = "Nome do ambiente de implantação (ex.: dev, staging, prod)."
default = "production"
validation {
condition = contains(["dev", "staging", "production"], var.environment)
error_message = "O ambiente deve ser um de: dev, staging, production."
}
}
variable "instance_type" {
type = string
description = "Tamanho da instância EC2 para o cluster da aplicação."
default = "t3.medium"
}
variable "vpc_cidr" {
type = string
description = "Bloco CIDR base para a Virtual Private Cloud."
default = "10.0.0.0/16"
}
Regras de locals.tf:
Use locals para valores derivados internamente, prefixos de ambiente e tags comuns:
locals {
name_prefix = "${var.project_name}-${var.environment}"
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "terraform"
}
}
terraform.tfvars.example
Sempre crie um arquivo de exemplo com valores de variáveis para que os colegas saibam como fornecer valores:
aws_region = "us-east-1"
environment = "production"
project_name = "checkout-service"
4. Fase 4: Substituir Valores Estáticos por Referências Dinâmicas (Guardrails Zero-Drift)
Converta IDs estáticos de nuvem e referências de recursos desconexas em expressões HCL nativas, protegendo a infraestrutura ativa contra substituição acidental:
Guardrail de Imutabilidade e ForceNew (Crítico para Brownfield / Terraformer)
- Distinga endereços Terraform de atributos
nameda nuvem: Renomear um rótulo de recurso Terraform (tfer--prod-main-vpc→main) com um blocomoved {}é seguro e sem tempo de inatividade. Alterar um atributonameda nuvem (ex.:google_compute_network.name,google_compute_subnetwork.name,google_compute_firewall.name,google_compute_instance.nameouaws_s3_bucket.bucket) éForceNewe destrói e recria a infraestrutura ativa. Parametrize nomes de nuvem existentes emvariables.tf/terraform.tfvarsem vez de reescrevê-los como"${local.name_prefix}-...". - Fixar imagens/AMIs importadas em importações brownfield: Trocar um
ami-*fixo oudebian-12-bookworm-v20240515por uma consultamost_recent = trueoudatapor família substituirá VMs existentes se uma imagem mais nova existir. Fixe o nome/ID exato da imagem/AMI importada através de uma variável/filtrodataquando drift zero for necessário, e só habilitemost_recent = truequando o usuário optar por atualizações de imagem. - Mantenha o endurecimento de segurança separado da refatoração zero-drift: Não altere silenciosamente atributos comportamentais/de segurança (ex.:
http_tokens"optional"→"required",public_access_prevention"inherited"→"enforced", ou escopos OAuth de VM) durante uma migração zero-drift. Trate o endurecimento como uma opção explícita ou liste claramente cada diff intencional. - Preserve chaves de tag/label legadas ao migrar: Se os recursos existentes usam
env = "prod", preserve as chaves existentes (ou adicione chaves padronizadas junto com elas) a menos que a substituição de chaves seja aprovada.
Checklist de Limpeza Específico para Terraformer
- Remova atributos de estado computados somente leitura (
tags_allna AWS;effective_labelseterraform_labelsno GCP) e atributos vazios/padrão ("",[],{},false,0). - Remova sub-blocos conflitantes despejados do estado (ex.:
network_interfaceprimário do EC2 quandosubnet_idestá definido). - Substitua interpolações legadas
${data.terraform_remote_state.*.outputs.*}e URLs de self-link completas por referências diretas a recursos (ex.:google_compute_network.main.id) ou consultasdata. - Agregue recursos duplicados numerados (
vm-01,vm-02) em um único recursofor_eachemparelhado com blocosmoved(to = google_compute_instance.api["01"]). - Extraia strings
user_data/startup-scriptescapadas com\npara arquivos emscripts/carregados viafile()outemplatefile(). - Ao alterar
scopesoumachine_typede VMs GCP, garanta queallow_stopping_for_update = trueesteja definido se atualizações in-loco forem pretendidas.
Anti-padrão (Cru / Fixo):
# RUIM: IDs fixos e strings literais
resource "aws_instance" "tfer--i-0123456789abcdef0" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.medium"
subnet_id = "subnet-01234567"
vpc_security_group_ids = [
"sg-0abcdef1234567890"
]
}
Padrão (Refatorado):
# 1. Preservação de estado: Diga ao Terraform que este recurso foi renomeado (evita destruir e recriar)
moved {
from = aws_instance.tfer--i-0123456789abcdef0
to = aws_instance.web
}
# 2. Consulta dinâmica para imagem base (fixe o nome exato para brownfield zero-drift, ou most_recent para greenfield)
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = [var.ami_name_pattern]
}
}
# 3. Definição de recurso padronizada com referências 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]
# Omita common_tags aqui se provider.tf já definir default_tags
tags = {
Name = var.instance_name
}
}
5. Fase 5: Formatação, Auditoria de Drift e Validação
-
Checklist de Auditoria de Drift Pré-Plano (sempre execute, especialmente se a CLI
terraformnão estiver instalada localmente):- Compare o
name,image/ami,location/zone,tags/labelse configurações de segurança/rede de cada recurso com a origem importada. - Verifique se nenhum campo
ForceNewfoi alterado acidentalmente e se cada recursotfer--*tem um blocomoved {}correspondente. - Separe e relate quaisquer diffs intencionais (ex.: endurecimento de segurança) da refatoração zero-drift.
- Compare o
-
Execute verificações da CLI do Terraform (quando instalada):
# 1. Formate o código para a indentação HCL canônica padrão
terraform fmt -recursive
# 2. Inicialize os plugins do provedor (use -backend=false ao validar offline/sem estado) e valide a sintaxe
terraform init -backend=false
terraform validate
# 3. Se estiver trabalhando em um ambiente ativo com estado, garanta que o plan não produza diffs não intencionais
terraform plan
- Resuma as Mudanças do
terraform plan(quando qualquer diff for detectado): Se oterraform planrelatar algo diferente deNo changes, pare antes de recomendarapplye apresente um resumo estruturado:- Totais do plan: Indique as contagens exatas de recursos (
X to add, Y to change, Z to destroy) e quaisquer transições de estadomoved. - Mudanças de alto risco primeiro (
-/+replace,-destroy): Destaque cada recurso marcado para substituição ou exclusão, nomeie o atributoForceNewque o causa (ex.:name,image,boot_disk) e explique como corrigi-lo (como adicionar um blocomoved {}ausente ou restaurar o valor original emtfvars). - Atualizações in-loco (
~) e criações (+): Liste cada recurso afetado e os atributos exatos que estão mudando (ex.:labels,tags,scopes,public_access_prevention). - Drift intencional vs. não intencional: Marque claramente quais diffs são intencionais (endurecimento de segurança opt-in ou padronização de tags) versus drift brownfield não intencional, para que o usuário possa aprová-los ou revertê-los.
- Totais do plan: Indique as contagens exatas de recursos (
Diretrizes e Referências Detalhadas
- Consulte Estrutura de Arquivos Padrão para padrões de agrupamento por domínio.
- Consulte Regras de Refatoração para nomenclatura, guardrails
ForceNew, tags/labels e checklists de anti-padrões.