Pular para o conteúdo

State: onde tudo dá errado

Avançado18 min de leituraiac

O state é o mapa entre o que está no seu código e o que existe no provedor. Sem ele, o Terraform não sabe que aws_s3_bucket.relatorios corresponde àquele bucket específico — e propõe criar tudo de novo. Praticamente todo desastre com Terraform é, na origem, um problema de state.

Além do mapeamento, ele guarda os atributos lidos do provedor — em texto claro. Senha inicial de banco, chave gerada, certificado: se passou pelo Terraform, está lá, mesmo que o output esteja marcado como sensitive.

Consequências práticas: nunca versione o state no Git; use backend com criptografia em repouso e acesso restrito; e trate quem tem leitura do state como quem tem os segredos.

Janela do terminal
terraform show -json | jq '.values.root_module.resources[].values | keys' | head

Backend remoto resolve dois problemas de uma vez: compartilhar o state entre pipeline e pessoas, e impedir dois apply simultâneos.

terraform {
backend "s3" {
bucket = "empresa-tfstate-prod"
key = "loja/prod/terraform.tfstate"
region = "sa-east-1"
encrypt = true
use_lockfile = true # locking nativo no S3 (versões recentes)
# dynamodb_table = "tf-locks" # alternativa clássica de locking
}
}

Ative versionamento no bucket do state e proteja contra exclusão. É a sua rede de segurança para tudo que vem abaixo: um state corrompido com versionamento é um inconveniente de dez minutos; sem versionamento, é uma reconstrução manual.

O lock é o que evita corrupção por concorrência. Quando o Terraform morre no meio, o lock fica preso — e force-unlock só depois de confirmar que nenhum apply está rodando:

Janela do terminal
terraform force-unlock <ID-DO-LOCK> # confirme antes; destravar durante um apply corrompe

Workspaces criam vários states com a mesma configuração. Servem para ambientes efêmeros e testes paralelos.

Não use workspace para separar produção de desenvolvimento. Motivos concretos: um erro de terraform workspace select aplica em produção; a configuração é idêntica, então diferenças legítimas viram condicional espalhada pelo código; e as permissões não se separam — quem aplica em dev tem o mesmo acesso ao state de prod.

Prefira diretórios separados, com backend, credencial e pipeline próprios por ambiente.

State único para tudo significa: plan lento, lock disputado por vários times e um erro capaz de destruir a infraestrutura inteira. Divida por fronteira de mudança e de dono:

infra/
rede/prod/ # muda raramente, dono: plataforma
cluster/prod/ # muda por trimestre
dados/prod/ # bancos, com proteção contra destruição
loja/prod/ # muda toda semana, dono: time do produto

A ligação entre eles é feita por leitura, não por acoplamento de escrita:

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.subnet_privada_ids

Alternativa mais desacoplada: publicar identificadores em um data source (tags, SSM Parameter Store) e consultá-los — assim um time não precisa de acesso de leitura ao state do outro.

Três operações que editam o mapa sem tocar na infraestrutura:

Janela do terminal
terraform state list # comece sempre por aqui
terraform state mv aws_s3_bucket.antigo aws_s3_bucket.novo # renomear sem recriar
terraform state rm aws_db_instance.legado # esquece o recurso; NÃO o destrói
terraform import aws_s3_bucket.relatorios empresa-relatorios-prod

state rm é a ferramenta certa para passar um recurso para outro state (remove de um, importa no outro). Ele não apaga nada no provedor — o recurso continua existindo, agora sem gestão. Fazer rm e esquecer de importar é como se cria recurso órfão pagando fatura por anos.

Faça backup antes de qualquer uma delas:

Janela do terminal
terraform state pull > backup-$(date +%F-%H%M).tfstate

Na ordem, do mais barato para o mais caro:

  1. Restaure a versão anterior do bucket versionado. Resolve a maioria dos casos.
    Janela do terminal
    aws s3api list-object-versions --bucket empresa-tfstate-prod --prefix loja/prod/
    aws s3api get-object --bucket empresa-tfstate-prod --key loja/prod/terraform.tfstate \
    --version-id <ID> restaurado.tfstate
    terraform state push restaurado.tfstate
  2. Refresh para reconciliar com a realidade: terraform apply -refresh-only, lendo cada mudança proposta antes de aceitar.
  3. Reimporte os recursos perdidos, um a um, até o plano ficar vazio.
  4. Último recurso: editar o JSON à mão, com backup, incrementando o campo serial e depois terraform state push. Faça só com o lock adquirido e ninguém mais aplicando.

O objetivo em todos os caminhos é o mesmo: chegar a um terraform plan sem mudanças. Enquanto o plano propuser criar o que já existe, o state ainda está errado.

Próximo passo: organize o código que compartilha esse state em Módulos que outros times usam. Para o passo a passo de recuperação, veja o guia State corrompido.