Pular para o conteúdo

Dividir um state gigante

Avançado18 min de leituraiac

State único com toda a infraestrutura tem quatro sintomas conhecidos: plano lento, lock disputado, revisão difícil, e um erro capaz de destruir tudo de uma vez. Dividir resolve os quatro — e a divisão é feita sem tocar em nenhum recurso real, porque mover no state não altera a nuvem.

Sinais objetivos: terraform plan levando vários minutos, mais de 200 ou 300 recursos no mesmo state, times diferentes esperando o lock um do outro, e mudanças de aplicação convivendo com mudanças de rede no mesmo plano.

Janela do terminal
terraform state list | wc -l
time terraform plan

Corte por frequência de mudança e por dono, não por tipo de recurso.

infra/
rede/prod/ # muda por trimestre — dono: plataforma
cluster/prod/ # muda por trimestre
dados/prod/ # bancos, com prevent_destroy — dono: dados
loja/prod/ # muda toda semana — dono: time do produto

Uma boa fronteira minimiza as dependências entre states. Se dois grupos precisam trocar dez valores, provavelmente eles são um só.

Janela do terminal
mkdir -p infra/rede/prod && cd infra/rede/prod
# backend novo, chave própria
cat > backend.tf <<'EOF'
terraform {
backend "s3" {
bucket = "empresa-tfstate-prod"
key = "rede/prod/terraform.tfstate"
region = "sa-east-1"
encrypt = true
use_lockfile = true
}
}
EOF
terraform init
# backup dos dois lados, sempre
cd ../../monolito/prod && terraform state pull > backup-origem-$(date +%F).tfstate

Duas abordagens; a segunda é mais segura para volume grande.

Opção A — state mv entre states, direto:

Janela do terminal
cd infra/monolito/prod
terraform state mv \
-state-out=../../rede/prod/terraform.tfstate \
aws_vpc.principal aws_vpc.principal

Opção B — remover e importar, com controle explícito:

Janela do terminal
# 1. no destino: declare o recurso e importe
cd infra/rede/prod
terraform import aws_vpc.principal vpc-0a1b2c3d
terraform plan # precisa ficar limpo ANTES do próximo passo
# 2. só então, na origem: esqueça o recurso (sem destruir)
cd ../../monolito/prod
terraform state rm aws_vpc.principal

A ordem da opção B é o ponto crítico: importe primeiro, remova depois. Invertido, você fica com um recurso órfão se a importação falhar.

Mova o grupo inteiro de uma vez (VPC, subnets, rotas, gateways) — deixar metade em cada lado gera dependências cruzadas difíceis de desfazer.

O state de aplicação precisa dos IDs que agora vivem no state de rede.

# origem: publique os valores
output "subnet_privada_ids" { value = aws_subnet.privada[*].id }
output "vpc_id" { value = aws_vpc.principal.id }
# consumidor: leia o state remoto
data "terraform_remote_state" "rede" {
backend = "s3"
config = {
bucket = "empresa-tfstate-prod"
key = "rede/prod/terraform.tfstate"
region = "sa-east-1"
}
}
# uso: data.terraform_remote_state.rede.outputs.vpc_id

Alternativa mais desacoplada, e melhor quando os times são diferentes: publicar os identificadores em SSM Parameter Store (ou equivalente) e consultá-los por data source — assim ninguém precisa de acesso de leitura ao state alheio.

Este é o critério de aceite. Os dois precisam ficar limpos.

Janela do terminal
cd infra/rede/prod && terraform plan -detailed-exitcode # espera-se 0
cd ../../loja/prod && terraform plan -detailed-exitcode # espera-se 0

Se algum lado propõe criar o que já existe, o recurso não chegou ao state certo — restaure o backup e refaça o movimento.

Só depois de tudo validado, remova do código de origem as declarações dos recursos movidos, e atualize o pipeline: cada state passa a ter o seu job, com sua credencial.

Sintoma Causa provável
Destino quer criar recurso existente Falha no state mv / import não concluído
Origem quer destruir o que foi movido O código de origem ainda declara o recurso
Resource already managed Ficou nos dois states — remova de um
Dependência circular entre states O corte foi feito no lugar errado; revise a fronteira
Lock preso no meio da operação Ninguém mais deve estar aplicando; veja o guia de state quebrado
  • Corte definido por frequência de mudança e dono.
  • Backend novo configurado, com locking e versionamento.
  • Backup do state de origem e do destino.
  • Grupos movidos inteiros, importando antes de remover.
  • Valores compartilhados por output ou parameter store.
  • plan limpo nos dois lados.
  • Código de origem limpo e pipeline ajustado por state.