O gerenciamento de estado é um dos conceitos mais importantes no uso do Terraform. O estado é um arquivo que registra o mapeamento entre os recursos definidos no código e os recursos reais criados na infraestrutura. Sem ele, o Terraform não saberia o que já foi criado, o que precisa ser alterado ou destruído. Nesta aula, vamos explorar por que o state é fundamental, como movê-lo para um backend remoto, como o locking protege contra operações concorrentes e quais cuidados devemos tomar para evitar problemas.

O state do Terraform contém dados sensíveis, como IPs, IDs de recursos e até mesmo segredos. Portanto, seu armazenamento e gerenciamento devem ser tratados com atenção. Vamos ver na prática como configurar um backend remoto (como S3) e habilitar o locking com DynamoDB, além de discutir boas práticas para evitar corrupção ou perda de dados.

Por que o state importa

O arquivo de estado (terraform.tfstate) é o coração do Terraform. Ele mantém um inventário de todos os recursos gerenciados, permitindo que o Terraform planeje e aplique alterações de forma incremental. Sem o state, o Terraform não teria como saber se um recurso já existe, qual é a sua configuração atual e como ele se relaciona com outros recursos.

Além disso, o state é essencial para operações como terraform destroy, que precisa saber exatamente quais recursos devem ser destruídos. Ele também armazena metadados, como dependências entre recursos, o que permite ao Terraform ordenar corretamente as operações. Outro ponto crucial é que o state pode conter informações sensíveis (senhas, chaves), portanto, deve ser protegido e acessível apenas a quem precisa.

Vamos ver um exemplo simples: ao criar uma instância EC2, o Terraform registra seu ID no state. Quando você altera o código (por exemplo, muda o tipo de instância), o Terraform consulta o state para saber que a instância já existe e planeja uma atualização, em vez de tentar criar uma nova.

# Exemplo de conteúdo de um arquivo de estado (simplificado)
{
  "version": 4,
  "terraform_version": "1.5.0",
  "resources": [
    {
      "module": "",
      "mode": "managed",
      "type": "aws_instance",
      "name": "web",
      "instances": [
        {
          "attributes": {
            "id": "i-1234567890abcdef0",
            "instance_type": "t2.micro",
            "ami": "ami-0c55b159cbfafe1f0"
          }
        }
      ]
    }
  ]
}

O state também permite o uso de data sources e expressões que dependem de recursos existentes. Sem ele, o Terraform seria apenas um orquestrador de criação de recursos, sem capacidade de gerenciar o ciclo de vida completo.

Backend remoto

Por padrão, o Terraform armazena o estado em um arquivo local. Em projetos pessoais, isso pode ser suficiente, mas em equipes ou ambientes corporativos, o estado local é problemático: não é compartilhado, não é versionado e não é seguro. A solução é usar um backend remoto, que armazena o estado em um serviço externo, como AWS S3, Azure Storage, Google Cloud Storage ou Terraform Cloud.

O backend remoto oferece várias vantagens: permite que todos os membros da equipe acessem o mesmo estado, evita conflitos de versão, fornece um histórico de alterações e possibilita o locking (que veremos a seguir). Além disso, o estado fica centralizado e pode ser protegido com políticas de acesso.

Vamos configurar um backend S3 na AWS. Primeiro, precisamos de um bucket S3 e, opcionalmente, uma tabela DynamoDB para locking. O bloco terraform no código define o backend:

terraform {
  backend "s3" {
    bucket         = "meu-bucket-estado"
    key            = "prod/infra/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
  }
}

Após adicionar esse bloco, você precisa executar terraform init para inicializar o backend. O Terraform detectará a configuração e fará a migração do estado local para o remoto, se existir. A partir daí, todos os comandos (plan, apply, destroy) usarão o estado remoto.

É importante que o bucket S3 tenha versionamento habilitado para permitir recuperação de versões antigas do estado, e que a tabela DynamoDB seja criada com uma chave primária chamada LockID (tipo string). O Terraform usa essa tabela para adquirir locks, como veremos na próxima seção.

Locking

O locking é um mecanismo que impede que duas pessoas (ou processos) executem terraform apply simultaneamente sobre o mesmo estado. Sem locking, haveria corrida de dados: um usuário poderia sobrescrever as alterações do outro, resultando em estado corrompido ou inconsistente.

O Terraform implementa locking por meio de um recurso de backend. No caso do S3, ele usa uma tabela DynamoDB para registrar a operação em andamento. Quando um terraform apply é iniciado, o Terraform tenta criar um item na tabela com um ID único. Se o item já existir, significa que outra operação está em andamento, e o Terraform bloqueia a execução até que o lock seja liberado.

Vamos ver como isso funciona na prática. Suponha que dois desenvolvedores executem terraform apply ao mesmo tempo. O primeiro adquire o lock e começa a modificar a infraestrutura. O segundo tentará adquirir o lock, mas falhará, recebendo uma mensagem de erro:

Error: Error acquiring the state lock

Error message: ConditionalCheckFailedException: The conditional request failed
Lock Info:
  ID:        abc123...
  Path:      meu-bucket-estado/prod/infra/terraform.tfstate
  Operation: OperationTypeApply
  Who:       dev@example.com
  Created:   2023-10-01 12:00:00

Terraform acquires a state lock to protect the state from being written
by multiple users at the same time. Please resolve the issue above and try
again. For most commands, a lock can be disabled with the -lock=false flag,
but this is not recommended.

Para evitar esse problema, o segundo desenvolvedor deve aguardar o término do primeiro. O lock é liberado automaticamente ao final da operação (apply, plan, destroy). Em caso de falha, o lock pode ficar preso; nesse caso, é possível forçar a liberação com terraform force-unlock <LOCK_ID>, mas isso deve ser feito com cautela.

O locking é essencial em ambientes colaborativos, e o backend remoto com locking é considerado uma boa prática em qualquer projeto que envolva mais de uma pessoa.

Cuidados

Embora o gerenciamento de estado seja poderoso, ele requer cuidados para evitar problemas graves. Aqui estão alguns pontos críticos:

  • Nunca edite o arquivo de estado manualmente: O estado é um arquivo JSON que deve ser modificado apenas pelo Terraform. Alterações manuais podem causar divergências e erros imprevisíveis.
  • Proteja o estado: Como o estado pode conter dados sensíveis, use criptografia em repouso (por exemplo, S3 com SSE) e restrinja o acesso por meio de políticas IAM.
  • Habilite o versionamento do backend: Isso permite recuperar versões anteriores do estado em caso de corrupção ou exclusão acidental.
  • Use workspaces ou separação por ambiente: Crie estados separados para produção, desenvolvimento e staging, evitando misturar recursos e reduzindo o risco de alterações indesejadas.
  • Evite o -lock=false: Desabilitar o lock pode levar a conflitos e corrupção. Use apenas em situações de emergência, como quando um lock está preso.
  • Faça backup regular do estado: Mesmo com versionamento, é prudente ter cópias externas em locais seguros.

Além disso, ao usar backends remotos, é importante garantir que a configuração do backend seja idêntica para todos os membros da equipe. Variações podem causar a criação de estados separados, o que é um erro comum.

Outra boa prática é usar o comando terraform state list e terraform state show para inspecionar o estado, e terraform state pull e terraform state push para manipular o estado quando necessário, mas sempre com cuidado.

Boas práticas e observações finais

Para encerrar, resumimos algumas recomendações:

  • Configure um backend remoto desde o início do projeto, mesmo em projetos pessoais, para se acostumar.
  • Use uma solução de locking sempre que o backend suportar (S3 + DynamoDB, Terraform Cloud, etc.).
  • Documente a configuração do backend e os procedimentos de recuperação em caso de desastres.
  • Considere usar o Terraform Cloud ou o Enterprise, que oferecem gerenciamento de estado gerenciado e histórico de execuções.
  • Revise periodicamente o estado para garantir que não há recursos órfãos ou divergências.

Lembre-se de que o estado é um ativo crítico: perda ou corrupção pode significar perda de controle sobre a infraestrutura. Portanto, trate-o com a mesma seriedade que trataria um banco de dados de produção.

Exercícios

  1. Explique por que o arquivo de estado é necessário para o funcionamento do Terraform e o que aconteceria se ele fosse excluído.
  2. Descreva as vantagens de usar um backend remoto em comparação com o estado local.
  3. Como o locking evita problemas em ambientes colaborativos? Dê um exemplo de uma situação sem locking.
  4. Cite três cuidados que você deve ter ao gerenciar o estado do Terraform.
  5. Escreva um bloco de configuração de backend para S3 com locking via DynamoDB, explicando cada parâmetro.

✓ Resposta: O arquivo de estado é necessário porque ele mantém o mapeamento entre os recursos declarados no código e os recursos reais na infraestrutura. Sem ele, o Terraform não saberia o que já foi criado, impossibilitando o planejamento de alterações e a destruição correta. Se o estado for excluído, o Terraform perderá o rastreamento de todos os recursos, e na próxima execução ele tentará recriar tudo, mas encontrará conflitos com recursos existentes, resultando em erros ou duplicação.

✓ Resposta: As vantagens incluem: compartilhamento do estado entre a equipe, evitando conflitos; centralização e segurança com criptografia e controle de acesso; versionamento do estado, permitindo recuperação de versões anteriores; e suporte a locking, prevenindo operações concorrentes. O estado local é limitado a um único desenvolvedor e não é seguro.

✓ Resposta: O locking impede que duas ou mais pessoas executem operações que modificam o estado ao mesmo tempo. Sem locking, se dois desenvolvedores rodarem `terraform apply` simultaneamente, ambos podem ler o mesmo estado, aplicar alterações diferentes e gravar o estado por último, sobrescrevendo o trabalho do outro. Isso pode levar a recursos inconsistentes e estado corrompido.

✓ Resposta: Três cuidados essenciais são: 1) Nunca editar o arquivo de estado manualmente, pois pode causar divergências; 2) Proteger o estado com criptografia e controle de acesso, pois contém dados sensíveis; 3) Habilitar versionamento no backend para recuperação em caso de corrupção ou exclusão acidental.

✓ Resposta:
terraform {
  backend "s3" {
    bucket         = "meu-bucket-estado"
    key            = "prod/infra/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
  }
}
O parâmetro `bucket` define o nome do bucket S3 onde o estado será armazenado. `key` é o caminho dentro do bucket. `region` especifica a região AWS. `dynamodb_table` é o nome da tabela DynamoDB usada para locking; ela deve ter uma chave primária chamada `LockID`.

Referências