Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

03 - Terraform Workspaces e Environments

#environments#estrutura#promocao#tfvars#workspaces
video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

Terraform Workspaces e Environments

A pergunta estrutural central de qualquer operação de Terraform madura é como organizar o mesmo desenho de infraestrutura para dev, staging e produção sem duplicar código e sem acoplar ambientes que devem evoluir separados. Terraform oferece três respostas principais: workspaces, que isolam states sob o mesmo código; diretórios separados por ambiente, que copiam a raiz e divergem por arquivos; e módulos raiz por ambiente, que mantêm raízes finas consumindo módulos versionados com parâmetros. Nenhuma abordagem vence em todas as dimensões, e a escolha envolve trade-offs reais de isolamento, divergência de código, custo de coordenação e segurança de execução. A decisão importa mais do que parece: ela define como uma mudança é promovida entre ambientes, como uma variável diverge por ambiente, como um incidente em produção fica contido e como a recuperação de desastre executa. Este arquivo cobre o funcionamento dos workspaces, as três abordagens com suas consequências práticas, os critérios de trade-off e a estrutura de repositório que operações maduras costumam adotar.

Conceito: ambiente como unidade de isolamento

Ambiente, no contexto de IaC, é um conjunto de recursos com ciclo de vida próprio, own state, own credentials, own risk: dev se destrói e recria livremente, staging se recicla para testar promoção, produção tem acesso restrito e gate humano. O IaC precisa expressar "o mesmo desenho, ambientes diferentes", e a forma de expressar isso é exatamente a decisão deste arquivo.

Dois riscos organizam a avaliação das abordagens. O risco de acoplamento: quando dev e produção compartilham demais, uma mudança de dev aplica em produção por acidente, e a recuperação de produção depende de dev. O risco de divergência: quando os ambientes copiam código em vez de parametrizar, o desenho evolui em produção e staging fica para trás, e a promoção para de testar o que será aplicado. Toda abordagem é uma posição nesse eixo entre acoplamento e divergência.

A variável de calibragem é o porte da operação e o grau de separação exigido. Um time pequeno com um produto simples convive bem com workspaces; uma operação com múltiplos times, compliance e contas separadas raramente convive com menos que diretórios ou raízes por ambiente, e frequentemente com backends e pipelines separados também.

Vale fixar o vocabulário antes das comparações: state, o registro por ambiente; backend, o mecanismo remoto que guarda e tranca o state; raiz, o diretório de execução; e ambiente, a fronteira de risco. A discussão de workspaces e ambientes é, no fundo, uma discussão de quantas fronteiras o desenho precisa e onde cada uma vive.

Workspaces: isolamento de state no mesmo código

Um workspace é um state separado sob o mesmo código e o mesmo backend: terraform.workspace devolve o nome do workspace corrente, e a CLI gerencia a lista e a seleção. O mesmo código aplicado em dois workspaces cria dois conjuntos completos de recursos, com dois estados independentes e dois locks independentes. A promoção, nesse modelo, é literalmente trocar de workspace e aplicar, pois o código é idêntico por construção.

A abordagem tem um ganho imediato: divergência de desenho é impossível por construção, e o custo de adicionar ambiente é uma lista de valores por workspace. O preço vem em duas formas. A primeira é a lógica condicional que o workspace obriga a plantar no código: terraform.workspace == "producao" ? "t3.large" : "t3.small" escala mal e espalha decisão de ambiente pelo meio da implementação. A segunda é o isolamento de segurança fraco: mesmo código, mesmo backend e mesma pipeline significam que a credencial que aplica em dev frequentemente alcança produção.

O uso saudável dos workspaces é o de escala pequena e risco uniforme: ambientes efêmeros, ramificações de teste, regiões irmãs com o mesmo desenho. Como mecanismo principal de separação entre dev e produção em operação real, o padrão é considerado frágil, e a literatura operacional converge para diretórios ou raízes separadas quando há riscos e donos diferentes por ambiente.

# Ciclo de workspaces: mesmíssimo código, states independentes
terraform workspace list                 # lista os workspaces existentes
terraform workspace new staging          # cria workspace com state próprio
terraform workspace select producao      # troca o state corrente
terraform plan -var-file=env/producao.tfvars
terraform apply                          # aplica só sobre o state selecionado

Diretórios separados por ambiente

A segunda abordagem mantém uma pasta por ambiente, cada uma com a própria raiz, o próprio backend e o próprio arquivo de variáveis. O desenho é compartilhado por módulos versionados, e a pasta do ambiente é fina: chama módulos, passa parâmetros e conecta outputs. A divergência de desenho continua impossível, pois desenho vive em módulo; a divergência de parametrização é explícita e revisada em cada pasta.

Os ganhos são de operação e segurança: cada pasta tem backend e credenciais próprios, o que materializa a fronteira entre ambientes; pipelines separadas por pasta dão independência de execução e gate específico de produção; e o plano de cada ambiente é pequeno e legível. Os custos são de coordenação: uma mudança que atravessa ambientes abre vários pull requests ou um PR multilinha com arquivos por ambiente, e o risco de drift de versão de módulo entre pastas exige disciplina de bump coordenado.

O anti-padrão do modelo é a pasta gorda: ambiente que copia desenho em vez de consumir módulo. Quando a pasta de produção tem trezentas linhas de recursos próprios, a abordagem degenerou em cópia com pasta, e a divergência que os diretórios prometiam evitar voltou pela porta de trás. A regra é simples: pasta de ambiente é parametrização, nunca desenho.

No eixo de execução, o modelo também simplifica a automação: cada pasta é um job de pipeline com permissão e gate próprios, e a adição de ambiente é copiar a estrutura, ajustar backend e tfvars e abrir a pipeline correspondente. O custo de coordenação aparece nas mudanças transversais, e por isso times que adotam pastas costumam acompanhar com convenções de PR multilinha ou automação que propaga o bump de versão por todas as pastas de uma vez.

Módulos raiz por ambiente: a composição canônica

A terceira abordagem generaliza a segunda e é a convergência típica de operações grandes: um repositório de módulos versionado, e raízes por ambiente que compõem os módulos com parâmetros. A raiz é a unidade de execução e de state, e o conjunto de raízes é o mapa da infraestrutura. A promoção de código é a troca da tag de versão do módulo, primeiro em staging, validada, depois em produção, com planos provando a equivalência do desenho.

O modelo explicita as responsabilidades: o repositório de módulos é produto, com changelog, testes e dono; as raízes por ambiente são consumo, com política de versão e pipeline própria. O custo é o mais alto dos três modelos em coordenação, e o benefício é o mais forte em isolamento e auditabilidade, pois cada ambiente é uma fronteira física de backend, credencial e pipeline.

A migração para esse modelo a partir de qualquer dos outros segue o mesmo ritual: extrair o desenho das raízes para módulos, versionar, converter cada ambiente em raiz fina e validar com plan de equivalência em cada passo. É trabalho de refactor com produção em pé, executado por componente, e o plano sem diff é a prova de cada etapa.

A estrutura de repositório abaixo ilustra a composição típica, com biblioteca separada e raízes finas:

infra/
  modules/                # biblioteca compartilhada, versionada por tag
    vpc/                  # desenho de rede completo e testado
    cluster/              # desenho de computação com auto scaling
    banco/                # desenho de dados com backup e réplica
  envs/
    dev/                  # raiz fina: só parâmetros e composição
      main.tf
      backend.tf          # backend próprio do ambiente
      dev.tfvars
    staging/
      main.tf
      backend.tf
      staging.tfvars
    producao/
      main.tf
      backend.tf          # credencial e pipeline restritas
      producao.tfvars
  pipelines/              # definição de CI por raiz de ambiente

Trade-offs entre as abordagens

Dimensão Workspaces Diretórios por ambiente Raízes com módulos versionados
Divergência de desenho Impossível por construção Baixa, desenho em módulo Baixa, desenho em módulo
Divergência de versão Inexistente Risco entre pastas Controlada por bump de tag
Isolamento de segurança Fraco, mesmo backend Forte, backend por pasta Forte, backend por raiz
Gate e pipeline Compartilhados Por pasta Por raiz
Parametrização por ambiente Condicionais no código tfvars por pasta tfvars e parâmetros na raiz
Custo de novo ambiente Baixo Médio Médio-alto
Custo de coordenação Baixo Médio Alto
Escala da operação Times pequenos Média Grande, multi-time

A leitura de decisão: comece simples e migre quando o custo do isolamento ficar menor que o custo dos incidentes. Workspaces servem enquanto os ambientes têm o mesmo dono e o mesmo risco; a primeira diferença séria de compliance, credencial ou ritmo de mudança entre ambientes é o gatilho clássico de migração para diretórios ou raízes separadas.

A migração entre abordagens é, ela mesma, um procedimento com plano: os recursos precisam ser movidos entre states sem serem recriados, e o teste de cada passo é um plan vazio, sem diff, sobre o ambiente migrado. Fazer essa migração com plano executável e checkpoints é o que a separa de uma reconstrução disfarçada.

Promoção de código entre ambientes

Promoção é o processo que leva o mesmo código do ambiente de menor risco ao de maior risco, e ela é a prova viva de que a abordagem escolhida funciona. O fluxo canônico: mudança entra por pull request com plano de dev anexado; aplicada em dev e validada, a mesma versão é aplicada em staging, com o plano provando que o desenho é idêntico; aprovada em staging, a produção recebe a mesma versão com gate humano. O que promove é a versão, não o copy de arquivo.

Com módulos versionados, a promoção é um bump de tag consumido por cada raiz, e a disciplina central é a janela: staging e produção não podem viver em versões distintas por muito tempo, pois a janela é exatamente o que a promoção existe para reduzir. Times maduros automatizam a promoção com pipeline que detecta a versão em staging e abre o PR de produção, e mantêm o gate humano no merge.

A promoção também é o momento de testar o rollback: a versão anterior permanece aplicável, e revertê-la em staging é parte do exercício. Promoção sem rollback testado não é promoção, é estreia, e produção não é lugar de estreia.

Variáveis por ambiente

O mecanismo de parametrização canônico é o arquivo de variáveis por ambiente, o .tfvars, carregado explicitamente na execução. O contrato saudável mantém o conjunto de variáveis igual entre ambientes, com valores divergentes, e trata a diferença de estrutura como sinal de que a variável pertence ao módulo, não ao ambiente. Variáveis obrigatórias sem padrão exigem valor em cada tfvars, o que é bom: ninguém esquece de declarar o tamanho do banco de produção.

Valores sensíveis não vivem em tfvars comitados: credenciais entram por gerenciador de segredos ou por injeção da pipeline, e o tfvars guarda referência, não valor. A validação de variáveis, tipos e restrições, executa no plan, e é a primeira barreira contra o parâmetro absurdo que só explodiria no apply de produção.

# envs/producao/producao.tfvars: parâmetros do ambiente
ambiente   = "producao"
regiao     = "sa-east-1"
tamanho    = "large"
replicas   = 3
versao_cluster = "v4.2.1"   # tag do módulo promovida

O Inventário de valores por ambiente também é documento vivo: quando dev e produção divergem em uma variável, a divergência deve ter dono e motivo, senão é bug de parametrização esperando incidente. Times com governança madura revisam o diff entre arquivos de variáveis como parte da revisão de promoção, pois é ali que a diferença entre "mesmo desenho" e "desenho parecido" aparece primeiro.

Erros comuns e boas práticas

  • Usar workspaces como separação de segurança entre dev e produção; mesmo backend e mesma credencial invalidam a fronteira que o ambiente deveria ser.
  • Deixar lógica de ambiente espalhada em condicionais pelo código; parametrização por arquivo é legível, auditável e aplicável por pipeline.
  • Permitir que pasta de ambiente cresça com desenho próprio; pasta é parametrização, e desenho pertence a módulo versionado.
  • Promover copiando arquivos entre ambientes; promoção é versão, e a mesma tag precisa ser consumida por cada raiz.
  • Comitar segredos em tfvars; valor sensível vive em cofre, e o arquivo carrega referência.
  • Manter janelas longas de versão entre staging e produção; a janela é onde o teste deixa de representar o que será aplicado.
  • Não isolar pipelines por ambiente; pipeline única que aplica tudo é o acoplamento mais caro do modelo.
  • Criar ambiente novo por cópia de pasta sem revisar parâmetros; ambiente herdado por cópia carrega divergência silenciosa desde o primeiro dia.
  • Esquecer o rollback no processo de promoção; versão anterior precisa continuar aplicável, e o teste de reversão faz parte do exercício de promoção.