daily-dreamer/terraform-standardize

Padroniza código Terraform bruto, brownfield, ou importado via Terraformer (AWS, GCP, Azure) para os padrões HashiCorp de produção: isola provider.tf, versions.tf e backend.tf, parametriza strings fixas em variables.tf e locals.tf, substitui IDs estáticos por referências dinâmicas, aplica guardrails ForceNew e zero-drift com blocos moved, e executa formatação HCL, validação e sumário de diff do plan.

O que é terraform-standardize?

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 as melhores práticas da HashiCorp. O processo é dividido em cinco fases: auditoria inicial, estabelecimento da estrutura padrão de arquivos (separando `provider.tf`, `versions.tf`, `backend.tf`, `variables.tf`, `locals.tf`, `outputs.tf`), parametrização de literais em variáveis e locais com tipos, descrições e validações, substituição de IDs estáticos e referências fixas por fontes de dados dinâmicas e expressões HCL nativas, e finalmente formatação, validação e verificação de drift com `terraform fmt`, `terraform validate` e `terraform plan`. A skill é compatível com AWS, GCP e Azure e inclui diretrizes específicas para lidar com código originado do Terraformer, como remoção de atributos computados de estado, tratamento de blocos `moved` para evitar recriação de recursos, e preservação de nomes e tags existentes para garantir migração sem drift. Também enfatiza a separação entre refatoração zero-drift e alterações de segurança intencionais, fornecendo um checklist de auditoria pré-plano e um formato estruturado para sumarizar mudanças detectadas pelo `terraform plan`.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/daily-dreamer/terraform-standardize/tree/HEAD/skills/terraform-standardize

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

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.tf com 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:

  1. Disponibilidade da CLI: Execute terraform version (ou tofu version para OpenTofu) para determinar se a formatação, validação e verificações de plan podem ser executadas na Fase 5.
  2. Arquivos padrão ausentes: Verifique se versions.tf, provider.tf, variables.tf, locals.tf e outputs.tf existem.
  3. 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/...), IDs project está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
  4. 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_labels no nível do provedor (colocando apenas chaves específicas do recurso nos recursos individuais) ou merge(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 default apenas quando sensato: Opções de configuração como environment ou aws_region podem 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 name da nuvem: Renomear um rótulo de recurso Terraform (tfer--prod-main-vpc → main) com um bloco moved {} é seguro e sem tempo de inatividade. Alterar um atributo name da nuvem (ex.: google_compute_network.name, google_compute_subnetwork.name, google_compute_firewall.name, google_compute_instance.name ou aws_s3_bucket.bucket) é ForceNew e destrói e recria a infraestrutura ativa. Parametrize nomes de nuvem existentes em variables.tf / terraform.tfvars em vez de reescrevê-los como "${local.name_prefix}-...".
  • Fixar imagens/AMIs importadas em importações brownfield: Trocar um ami-* fixo ou debian-12-bookworm-v20240515 por uma consulta most_recent = true ou data por 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/filtro data quando drift zero for necessário, e só habilite most_recent = true quando 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_all na AWS; effective_labels e terraform_labels no GCP) e atributos vazios/padrão ("", [], {}, false, 0).
  • Remova sub-blocos conflitantes despejados do estado (ex.: network_interface primário do EC2 quando subnet_id está 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 consultas data.
  • Agregue recursos duplicados numerados (vm-01, vm-02) em um único recurso for_each emparelhado com blocos moved (to = google_compute_instance.api["01"]).
  • Extraia strings user_data / startup-script escapadas com \n para arquivos em scripts/ carregados via file() ou templatefile().
  • Ao alterar scopes ou machine_type de VMs GCP, garanta que allow_stopping_for_update = true esteja 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

  1. Checklist de Auditoria de Drift Pré-Plano (sempre execute, especialmente se a CLI terraform não estiver instalada localmente):

    • Compare o name, image/ami, location/zone, tags/labels e configurações de segurança/rede de cada recurso com a origem importada.
    • Verifique se nenhum campo ForceNew foi alterado acidentalmente e se cada recurso tfer--* tem um bloco moved {} correspondente.
    • Separe e relate quaisquer diffs intencionais (ex.: endurecimento de segurança) da refatoração zero-drift.
  2. 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
  1. Resuma as Mudanças do terraform plan (quando qualquer diff for detectado): Se o terraform plan relatar algo diferente de No changes, pare antes de recomendar apply e 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 estado moved.
    • Mudanças de alto risco primeiro (-/+ replace, - destroy): Destaque cada recurso marcado para substituição ou exclusão, nomeie o atributo ForceNew que o causa (ex.: name, image, boot_disk) e explique como corrigi-lo (como adicionar um bloco moved {} ausente ou restaurar o valor original em tfvars).
    • 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.

Diretrizes e Referências Detalhadas

Habilidades Relacionadas