TL;DR — Resumo Rápido

Configure backends de estado remoto do Terraform no S3, Azure Blob e GCS. Aprenda locking, migração, workspaces, segurança e referências entre projetos.

O Terraform mantém um arquivo de estado — um registro JSON de cada recurso que gerencia — para mapear sua configuração HCL à infraestrutura real. Quando você trabalha sozinho, um arquivo de estado local é suficiente. No momento em que um segundo engenheiro ou pipeline de CI executa terraform apply contra a mesma infraestrutura, o estado local torna-se um risco compartilhado sem bloqueio, sem versionamento e sem controle de acesso. Este guia cobre todos os aspectos dos backends de estado remoto do Terraform: escolha do provedor correto, configuração do bloqueio de estado, migração do estado local, manipulação do estado com comandos CLI, proteção de dados sensíveis e configuração de referências entre projetos.

Pré-requisitos

  • Terraform CLI v1.6 ou posterior instalado localmente
  • Uma conta AWS com permissões para criar buckets S3 e tabelas DynamoDB (para exemplos AWS)
  • Azure CLI autenticado em uma assinatura (para exemplos com Azure Blob)
  • Familiaridade básica com blocos de recursos Terraform e o fluxo terraform apply
  • Git para versionar suas configurações Terraform (nunca confirme terraform.tfstate)

Por Que o Estado Local É Perigoso para Equipes

O backend local do Terraform escreve terraform.tfstate no diretório atual. Isso cria três problemas críticos assim que mais de uma pessoa ou processo toca a mesma infraestrutura:

Sem bloqueio. Dois engenheiros executam terraform apply ao mesmo tempo. O segundo apply lê estado desatualizado, calcula um plano contra dados obsoletos e então sobrescreve o estado do primeiro apply. Os recursos criados pelo primeiro apply ficam órfãos — existem na nuvem mas são invisíveis para o Terraform.

Sem compartilhamento. Cada engenheiro tem sua própria cópia do estado em seu notebook. Mudanças feitas por uma pessoa são invisíveis para outros até que troquem manualmente os arquivos de estado.

Exclusão acidental. Um rm -rf no diretório errado, falha de disco ou notebook perdido apaga o arquivo de estado completamente. Sem ele, o Terraform não sabe o que gerencia e tentará recriar todos os recursos.

Configurar um Backend S3 (AWS)

O backend S3 é a escolha mais comum para equipes AWS. Ele combina um bucket S3 (para armazenamento durável e versionamento) com uma tabela DynamoDB (para bloqueio distribuído).

Criar a infraestrutura do backend

# Bucket S3 para armazenamento de estado
aws s3api create-bucket \
  --bucket acme-terraform-state \
  --region us-east-1

# Habilitar versionamento para recuperar versões anteriores
aws s3api put-bucket-versioning \
  --bucket acme-terraform-state \
  --versioning-configuration Status=Enabled

# Bloquear todo acesso público
aws s3api put-public-access-block \
  --bucket acme-terraform-state \
  --public-access-block-configuration \
    BlockPublicAcls=true,IgnorePublicAcls=true,\
BlockPublicPolicy=true,RestrictPublicBuckets=true

# Tabela DynamoDB para bloqueio de estado
aws dynamodb create-table \
  --table-name terraform-state-locks \
  --attribute-definitions AttributeName=LockID,AttributeType=S \
  --key-schema AttributeName=LockID,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

Configurar o bloco backend

terraform {
  backend "s3" {
    bucket         = "acme-terraform-state"
    key            = "networking/prod/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-state-locks"
    encrypt        = true
  }
}

A key é o caminho do objeto S3. Use uma convenção de nomenclatura consistente — <projeto>/<ambiente>/terraform.tfstate — para que todas as equipes possam encontrar os arquivos de estado umas das outras.

Configurar um Backend Azure Blob Storage

Para equipes centradas no Azure, use uma conta de armazenamento Azure. O Azure Blob Storage fornece bloqueio nativo via blob lease — nenhum recurso de bloqueio separado é necessário.

az group create --name rg-terraform-state --location eastus

az storage account create \
  --name acmetfstate2026 \
  --resource-group rg-terraform-state \
  --sku Standard_LRS \
  --encryption-services blob \
  --min-tls-version TLS1_2

az storage container create \
  --name tfstate \
  --account-name acmetfstate2026
terraform {
  backend "azurerm" {
    resource_group_name  = "rg-terraform-state"
    storage_account_name = "acmetfstate2026"
    container_name       = "tfstate"
    key                  = "networking/prod/terraform.tfstate"
  }
}

Bloqueio de Estado e Consistência

Quando o Terraform inicia qualquer operação que modifica estado (plan, apply, destroy), ele tenta adquirir um bloqueio. Com o backend S3, o DynamoDB fornece o bloqueio. Qualquer processo concorrente tentando adquirir o mesmo bloqueio recebe uma mensagem de erro com informações sobre quem o detém.

Para liberar um bloqueio deixado por um processo que travou:

terraform force-unlock a3f2b1

Nunca use force-unlock enquanto outra operação estiver ativa. Use este comando apenas quando tiver certeza de que o processo que detinha o bloqueio terminou.

Migrando do Estado Local para Remoto

Migração inicial

Após adicionar um bloco backend à sua configuração pela primeira vez, execute:

terraform init -migrate-state

O Terraform detecta o novo backend, solicita que você copie o estado local existente e o escreve na localização remota. Um backup é salvo como terraform.tfstate.backup.

Alternando entre backends remotos

Para mover estado de um backend remoto para outro (por exemplo, de S3 para Terraform Cloud):

  1. Atualize o bloco backend para o novo destino
  2. Execute terraform init -migrate-state
  3. Confirme o prompt de migração

Comandos de Estado do Terraform

# Listar todos os recursos rastreados no estado
terraform state list

# Mostrar atributos completos de um recurso específico
terraform state show aws_instance.web

# Renomear um recurso (após refatorar HCL)
terraform state mv aws_instance.old_name aws_instance.new_name

# Mover um recurso para outro arquivo de estado
terraform state mv \
  -state-out=../other-project/terraform.tfstate \
  aws_vpc.main aws_vpc.main

# Remover um recurso do estado sem destruí-lo na nuvem
terraform state rm aws_instance.legacy_server

# Baixar estado como JSON
terraform state pull > current-state.json

# Substituir estado por um arquivo modificado (perigoso)
terraform state push modified-state.json

Segurança do Arquivo de Estado

Os arquivos de estado do Terraform contêm cada valor de atributo de cada recurso — incluindo senhas de banco de dados, chaves TLS privadas e tokens de API. Mesmo que você marque uma variável ou output como sensitive = true, o valor ainda aparece no JSON de estado em texto simples.

Criptografia em repouso. Habilite criptografia no backend de armazenamento:

# S3: aplicar SSE-KMS com uma chave gerenciada pelo cliente
aws s3api put-bucket-encryption \
  --bucket acme-terraform-state \
  --server-side-encryption-configuration '{
    "Rules": [{
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "aws:kms",
        "KMSMasterKeyID": "arn:aws:kms:us-east-1:123456789:key/abc-123"
      }
    }]
  }'

IAM com mínimo de privilégio. Conceda aos pipelines de CI apenas as permissões necessárias sobre o bucket S3 e a tabela DynamoDB.

Workspaces para Separação de Ambientes

Os workspaces do Terraform criam arquivos de estado isolados dentro da mesma configuração de backend:

terraform workspace new dev
terraform workspace new staging
terraform workspace new production

terraform workspace list
terraform workspace select production
terraform apply

Referencie o workspace atual na configuração para variar tamanhos ou contagens de recursos por ambiente:

locals {
  env_config = {
    dev        = { instance_type = "t3.micro",  min_size = 1 }
    staging    = { instance_type = "t3.small",  min_size = 2 }
    production = { instance_type = "t3.medium", min_size = 3 }
  }
}

resource "aws_instance" "web" {
  instance_type = local.env_config[terraform.workspace].instance_type
}

Referências Entre Projetos com terraform_remote_state

O data source terraform_remote_state permite que um projeto Terraform leia outputs do arquivo de estado de outro projeto sem duplicar declarações de recursos:

# O projeto de networking expõe seu ID de VPC
output "vpc_id" {
  value = aws_vpc.main.id
}

# O projeto de aplicação lê o ID de VPC do estado de networking
data "terraform_remote_state" "networking" {
  backend = "s3"
  config = {
    bucket = "acme-terraform-state"
    key    = "networking/prod/terraform.tfstate"
    region = "us-east-1"
  }
}

resource "aws_instance" "app" {
  subnet_id = data.terraform_remote_state.networking.outputs.vpc_id
}

Configuração Parcial de Backend para CI/CD

Blocos backend não podem usar interpolação de variáveis. Use configuração parcial com flags -backend-config:

# main.tf — sem valores especificados
terraform {
  backend "s3" {}
}
# O pipeline CI passa os valores no momento do init
terraform init \
  -backend-config="bucket=acme-terraform-state" \
  -backend-config="key=networking/prod/terraform.tfstate" \
  -backend-config="region=us-east-1" \
  -backend-config="dynamodb_table=terraform-state-locks"

Comparativo de Backends

CaracterísticaS3 + DynamoDBAzure BlobGCSTerraform CloudConsul
Bloqueio de estadoTabela DynamoDBBlob lease nativoBloqueio de objeto nativoIntegradoKV nativo
Criptografia em repousoSSE-S3 / SSE-KMSChaves AzureChaves GoogleAES-256 integradoTLS manual
VersionamentoVersionamento S3Versionamento blobVersionamento de objetoHistórico integradoSnapshots manuais
Custo (uso baixo)~$1/mês~$1/mês~$1/mêsGrátis (500 recursos)Ops self-hosted
Controle de acessoPolíticas IAMAzure RBACGCP IAMTeams + SSOTokens ACL
ComplexidadeMédia (2 recursos)Média (3 recursos)Baixa (1 bucket)Baixa (SaaS)Alta (cluster)
Ideal paraEquipes AWSEquipes AzureEquipes GCPMulti-cloud + políticasOn-premises

Cenário Real Multi-Ambiente

Sua equipe gerencia 300+ recursos AWS em dev, staging e produção. Os engenheiros têm confirmado terraform.tfstate no Git. Em uma quarta-feira de manhã, dois engenheiros aplicam mudanças simultaneamente à VPC de produção — um adiciona uma sub-rede privada, o outro modifica um grupo de segurança. O segundo apply lê o estado anterior, computa um plano que ignora a nova sub-rede e sobrescreve o estado. A sub-rede existe na AWS mas é invisível para o Terraform: um recurso fantasma que gera diffs confusos em cada plano futuro.

A arquitetura alvo usa um backend S3 com bloqueio DynamoDB e chaves separadas por ambiente:

# Inicialização por ambiente com configuração parcial
terraform init \
  -backend-config="bucket=acme-terraform-state" \
  -backend-config="key=acme/production/terraform.tfstate" \
  -backend-config="region=us-east-1" \
  -backend-config="dynamodb_table=terraform-state-locks"

Cada ambiente tem seu próprio arquivo de estado sob uma chave distinta. O bloqueio DynamoDB impede applies concorrentes. O versionamento S3 permite reverter estado corrompido.

Armadilhas e Casos Limite

Dados sensíveis no estado estão sempre em texto simples. Mesmo com sensitive = true em outputs, os valores aparecem no JSON de estado. Trate o arquivo de estado como um cofre de segredos e restrinja o acesso adequadamente.

Blocos backend não podem usar variáveis ou locals. Apenas valores literais e flags -backend-config são suportados.

Colisões de chaves de workspace. Se a chave de backend não incluir o nome do workspace, todos os workspaces compartilham um arquivo de estado.

Desvio de estado por mudanças externas. Recursos modificados pelo console AWS ou Portal Azure não são refletidos no estado até que você execute terraform refresh.

Falhas de apply parcial. Se um apply falhar no meio do caminho, o estado reflete o que foi criado com sucesso. Não exclua nem restaure o estado — apenas execute terraform apply novamente.

Solução de Problemas

Erros de aquisição de bloqueio

Error: Error acquiring the state lock

Causa: Outro processo detém o bloqueio ou um processo anterior travou sem liberá-lo. Solução: Verifique que nenhum outro apply ou plan está ativo. Se o bloqueio estiver obsoleto, execute terraform force-unlock <LOCK_ID>.

Acesso negado em operações de estado

Error: Failed to load state: AccessDenied: Access Denied

Causa: A identidade IAM executora não tem s3:GetObject, s3:PutObject ou dynamodb:PutItem. Solução: Audite a política IAM anexada à função CI e adicione as permissões faltantes.

Corrupção do arquivo de estado

Sintomas: terraform plan mostra todos os recursos sendo recriados embora existam na nuvem. Solução: Restaure uma versão anterior do estado do versionamento S3 e use terraform state push para aplicá-la.

Resumo

  • Estado local é inseguro para equipes: sem bloqueio, sem compartilhamento e uma exclusão apaga o conhecimento da infraestrutura
  • S3 + DynamoDB, Azure Blob, GCS e Terraform Cloud fornecem backends remotos duráveis e com bloqueio
  • O bloqueio de estado previne modificações concorrentes e corrupção
  • Migre de estado local para remoto com terraform init -migrate-state
  • Use terraform state list, show, mv e rm para inspecionar e refatorar o estado sem executar applies
  • Trate arquivos de estado como segredos — habilite criptografia em repouso e restrinja acesso IAM
  • Use workspaces ou módulos raiz separados com backends independentes para isolar ambientes
  • terraform_remote_state permite compartilhamento de dados entre projetos; desacople com SSM ou Vault para menor acoplamento
  • Configuração parcial de backend com -backend-config mantém segredos fora do controle de versão

Artigos Relacionados