Não existe uma única forma certa de estruturar ambientes Terraform. Mas existem decisões que, se ignoradas, cobram o preço mais tarde.
Quando um time começa a trabalhar com Terraform, o foco é entregar. Um ambiente, um repositório, tudo funcionando. Simples.
O problema aparece quando o segundo ambiente chega. E o terceiro. E quando times diferentes começam a tocar na mesma infraestrutura.
Na minha experiência, alguns padrões se repetem — e o custo de não endereçá-los cedo também. Este post reúne as decisões que, na prática, mais impactaram a forma como ambientes Terraform evoluem. Não existe uma resposta única para cada uma delas. O contexto, a maturidade do time e as ferramentas disponíveis influenciam o caminho. Mas ignorá-las costuma cobrar o preço mais tarde.
Em organizações maiores, essas decisões deixam de ser apenas uma questão de organização de código. Elas passam a fazer parte da engenharia de plataforma: a forma como ambientes são estruturados define como equipes entregam infraestrutura, como padrões são aplicados e como riscos operacionais são controlados.
TL;DR
Independente de como você implementa, toda estrutura de ambientes Terraform precisa responder bem a quatro pilares:
- Design — o código é consistente entre ambientes?
- Governança — quem pode fazer o quê, e como os padrões são aplicados?
- Rastreabilidade — o que foi aplicado, quando, por quem e com qual código?
- Drift — quando o ambiente diverge do código, você sabe?
Cada decisão que você toma ao estruturar seus ambientes vai fortalecer ou enfraquecer esses pilares.
O problema que aparece com o tempo
A solução mais comum quando o segundo ambiente aparece é simples: copiar a pasta existente e ajustar algumas variáveis.
infra-dev/
infra-prod/
Isso funciona… até não funcionar mais.
- Correções feitas em prod nunca chegam em dev
- Ambientes começam a divergir silenciosamente
- Ninguém tem certeza de qual versão da infraestrutura está rodando
Infraestrutura vira um conjunto de variações não intencionais. Variação não controlada em infraestrutura vira complexidade acumulada — e complexidade acumulada vira risco operacional.
Decisão 1: como separar os ambientes?
Essa é a primeira decisão estrutural. E ela define boa parte do que vem depois.
Existem abordagens diferentes — pastas separadas por ambiente, workspaces do Terraform, repositórios distintos. Cada uma tem trade-offs.
O que considerar
A separação precisa garantir que dev e prod não interferem um no outro — nem no código, nem no state, nem nas credenciais. Ao mesmo tempo, precisa garantir que os dois rodam o mesmo código. Se a separação exige manter código duplicado, ela vai gerar divergência.
Uma abordagem que equilibra bem esses dois pontos é um único repositório com código compartilhado e variáveis separadas por ambiente, com a identificação do ambiente feita pela branch da pipeline.
repo-infra/
├── main.tf
├── variables.tf
├── versions.tf
└── envs/
├── dev.tfvars
├── hml.tfvars
└── prod.tfvars
Todos os ambientes executam exatamente o mesmo código. O que muda são os valores — tamanho de instâncias, número de réplicas, CIDRs, retenção de logs.
Exceções existem. Alguns recursos precisam de tratamento específico por ambiente. Isso pode ser resolvido com variáveis condicionais ou data sources. O importante é que exceções sejam realmente exceções — quanto mais genérico for o código, maior a consistência e a governança.
Decisão 2: onde ficam os módulos?
Módulos são onde as decisões arquiteturais da organização vivem. Essa decisão define como elas vão evoluir.
O que considerar
Módulos dentro do repositório de infra são convenientes, mas criam acoplamento. Uma mudança no módulo afeta imediatamente todos os ambientes — sem revisão, sem controle.
Módulos em repositórios próprios, com versionamento explícito, mudam esse cenário:
module "vpc" {
source = "git::https://github.com/org/terraform-module-vpc.git?ref=v2.3.0"
...
}
Dev pode testar v2.4.0 enquanto prod ainda roda v2.3.0. A adoção de qualquer mudança é uma decisão explícita — visível, rastreável e reversível.
Vale destacar um ponto importante: módulos corporativos não são apenas código reutilizável. Eles são o lugar onde a arquitetura da organização vive — padronização de nomenclatura, tags obrigatórias, configurações de segurança, decisões de rede. A forma como uma VPC é criada, como ela se conecta à organização, quais rotas existem por padrão — tudo isso pode e deve estar no módulo, não espalhado por cada repositório de infra. Quem consome o módulo herda essas decisões automaticamente, sem precisar conhecê-las em detalhe.
O impacto vai além da organização. Quando uma vulnerabilidade de segurança é corrigida no módulo, todos que adotarem a nova versão já nascem corrigidos. Para o legado, a pergunta é objetiva: quem ainda está na versão antiga? Você tem a lista — sem precisar investigar o ambiente inteiro.
Sem versionamento, essa mesma pergunta vira uma investigação.
Decisão 3: onde e como o state é gerenciado?
O state é onde o Terraform mantém o mapa da infraestrutura real. Uma decisão errada aqui pode causar desde inconsistências até destruição acidental de recursos.
O que considerar
Cada ambiente precisa do seu próprio state. Misturar ambientes no mesmo state aumenta o risco de impacto acidental — um apply em dev pode afetar prod.
Uma estrutura que funciona bem é manter o state centralizado em uma conta dedicada — shared services — separado por prefixo:
tfstate-org/
├── dev/app01/terraform.tfstate
├── hml/app01/terraform.tfstate
├── preprod/app01/terraform.tfstate
└── prod/app01/terraform.tfstate
O backend não deve ficar hardcoded no código. Com partial configuration, os valores são passados pela pipeline em tempo de execução — o código não sabe qual ambiente está rodando. Quem sabe é a pipeline.
O lock — para evitar applies concorrentes no mesmo ambiente — pode ser feito via DynamoDB ou pelo recurso nativo de locking do S3, disponível nas versões mais recentes do Terraform.
Decisão 4: quem controla quando e como o apply acontece?
Essa decisão define a rastreabilidade da sua infraestrutura. Apply manual, direto da máquina do engenheiro, remove qualquer possibilidade de auditoria.
O que considerar
Infraestrutura deve ser aplicada via pipeline. Não por rigidez, mas porque pipeline é o único mecanismo que garante que toda mudança passou por validação, foi revisada e foi registrada.
Um fluxo de promoção entre ambientes torna isso explícito:
feature → dev → hml → preprod → prod
Cada merge dispara a pipeline no ambiente correspondente. Prod exige aprovação manual antes do apply.
Em ambientes mais maduros, o plan gerado na pipeline também pode ser tratado como um artefato imutável — o mesmo plan aprovado é o que será aplicado em produção.
O fluxo é o padrão, mas pode ser adaptado. Se uma aplicação só tem dois ambientes, as etapas do meio não existem. O que não se pode abrir mão é o princípio: mudanças passam pela pipeline.
A pipeline também é onde a segurança é verificada — ferramentas como Checkov cobrem boas práticas e benchmarks conhecidos. Para políticas específicas da organização, OPA + Conftest permite escrever regras customizadas em cima do plan do Terraform.
Decisão 5: o que você vai fazer quando o ambiente divergir?
Drift é inevitável em qualquer ambiente real. Troubleshooting de madrugada, intervenção emergencial, mudança manual que “era só temporária”. Acontece.
O que considerar
A questão não é se o drift vai acontecer — é se você vai saber quando ele aconteceu.
Sem um processo para detectar drift, o ambiente começa a divergir silenciosamente do código. Com o tempo, ninguém sabe mais qual é a fonte da verdade.
Uma prática simples é executar terraform plan periodicamente — não para aplicar, só para detectar divergências entre o state e o ambiente real. Algumas organizações automatizam isso com pipelines programadas que alertam quando o plan retorna diferenças.
Detectar drift cedo é muito mais barato do que descobrir em produção.
Anti-patterns que criam problemas em escala
Alguns padrões parecem funcionar no início, mas cobram o preço mais tarde:
- Copiar infraestrutura por ambiente — gera divergência inevitável. O código de dev e prod vão divergir, e ninguém vai saber exatamente quando ou como.
- State compartilhado entre ambientes — aumenta o risco de impacto acidental. Um erro de configuração pode afetar o ambiente errado.
- Apply manual — executado diretamente da máquina do engenheiro, remove rastreabilidade e dificulta auditoria. Sem pipeline, não há como saber o que foi aplicado, quando e por quem.
- Módulos sem versionamento — qualquer atualização no módulo afeta todos os consumidores imediatamente. Mudanças chegam sem aviso e sem controle.
- Segredos em tfvars — variáveis de ambiente não são o lugar para segredos. Use AWS Secrets Manager, Parameter Store ou Vault.
Conclusão
Não existe uma estrutura de ambientes Terraform que funcione para todo mundo. O contexto importa — o tamanho do time, a maturidade da organização, as ferramentas disponíveis.
Se as decisões discutidas aqui forem tomadas de forma consistente, os quatro pilares mencionados no início — design, governança, rastreabilidade e visibilidade de drift — passam a se reforçar mutuamente.
A infraestrutura deixa de depender de conhecimento implícito e passa a ser operada de forma previsível.
