"Nunca toque no console AWS em producao" soa como uma regra extrema. Nao e. E a disciplina operacional mais importante em uma equipe cloud-native, e o custo de viola-la se acumula silenciosamente ate causar um incidente grave.

Este post explica o porque e como aplicar o desenvolvimento IaC-first em uma equipe real.

O Problema do State Drift

O Terraform (e o OpenTofu) mantem um arquivo de estado que representa qual infraestrutura existe. Quando voce aplica, o Terraform compara o arquivo de estado com sua configuracao e faz o conjunto minimo de alteracoes para alinhar a realidade com a configuracao.

Quando voce clica no console AWS e cria ou modifica recursos, voce esta alterando a realidade sem alterar o arquivo de estado.

Agora: O proximo plan mostrara drift: o Terraform quer destruir ou recriar recursos que "nao deveriam existir" (porque nao estao na configuracao).

Ou pior: O estado do Terraform diz que o recurso existe com a configuracao A, mas o console o alterou para a configuracao B. O plan mostra "sem alteracoes" mesmo que o recurso esteja mal configurado.

Ou: alguem adiciona um recurso no console, ele funciona, passa a ser dependencia de outros recursos e entao um apply e executado e o exclui porque nao esta no estado.

O console nao e a fonte da verdade. Sua IaC e. O console e uma mentira.

O Fluxo de Importacao: A Resposta Correta para "Ja Existe"

Quando um recurso existe na AWS mas nao na sua configuracao Terraform (criado por alguem no console, criado manualmente via CLI ou migrado de outra ferramenta), a acao correta e importa-lo:

Importar um recurso existente para o estado do Terraform

./deploy.sh --infra --import aws_s3_bucket.service existing-bucket-name
./deploy.sh --infra --import aws_dynamodb_table.service_files serviceFiles
./deploy.sh --infra --import aws_lambda_function.service_api myapp-service-api

Apos importar, execute plan:
./deploy.sh --infra

O plan mostra a diferenca entre o que existe na AWS e o que sua configuracao diz. Corrija a configuracao ate que o plan nao mostre alteracoes inesperadas. Depois aplique para sincronizar o arquivo de estado.

Nunca exclua um recurso do console e recrie-o via Terraform. Isso destroi dados e quebra dependencias. Importe → reconcilie → aplique.

O Unico Uso Legitimo do Console

O console e apropriado para:

  • Exploracao somente leitura. Logs do CloudWatch, metricas do CloudWatch, historico de invocacoes do Lambda, inspecao de itens do DynamoDB.
  • Operacoes de emergencia com importacao imediata. Se a producao estiver fora do ar e a correcao exigir uma alteracao no console, faça a alteracao; mas documente-a e importe-a para o Terraform na mesma sessao de trabalho.

E so isso. Tudo que cria, modifica ou exclui recursos pertence a IaC.

O Script de Deploy como Ponto de Entrada Unico

Comandos diretos de terraform apply ou tofu apply executados diretamente no host sao perigosos porque ignoram o fluxo de trabalho padrao de deploy da sua equipe (validacao, lint, revisao do plan). Crie um script de deploy que envolva todas as operacoes Terraform:

#!/bin/bash
# deploy.sh - a unica forma de interagir com a infraestrutura

case “$1” in –infra) case “$2” in –apply) run_apply ;; –plan) run_plan ;; –validate) run_validate ;; –tflint) run_tflint ;; –import) run_import “$3” “$4” ;; *) run_plan ;; # padrao: apenas plan (seguro) esac ;; esac

Propriedades-chave de um bom script de deploy:

  • Padrao para plan. Executar ./deploy.sh --infra sem segundo argumento deve mostrar o plan, nunca aplicar. Aplicar requer intencao explicita (--apply).
  • Sempre validar antes de aplicar. Execute terraform validate e tflint antes de qualquer apply. Rejeite o deploy se qualquer um falhar.
  • Sempre mostrar o plan antes de aplicar. Mesmo para --apply, mostre a saida do plan e exija confirmacao no modo interativo.
  • Var-file consistente. Sempre inclua o mesmo var-file (vars/production.tfvars). Nunca aplique sem ele.

Revisao do Plan como Portao

Antes de qualquer PR de infraestrutura ser mergeado, a saida do plan deve estar visivel no PR:
## Terraform Plan

Terraform will perform the following actions:
module.service_api.aws_lambda_function.service_api will be updated in-place
~ resource "aws_lambda_function" "service_api" {
~ timeout = 30 -> 60
}

Plan: 0 to add, 1 to change, 0 to destroy.

module.service_api.aws_lambda_function.service_api will be updated in-place

~ resource "aws_lambda_function" "service_api" {
~ timeout = 30 -> 60
}

Plan: 0 to add, 1 to change, 0 to destroy.

Isso realiza duas coisas:

  1. O revisor pode ver exatamente o que vai mudar em producao antes de aprovar.
  2. A saida do plan e um registro historico do que foi planejado no momento do merge.

Um plan que mostre destruicoes inesperadas (N to destroy) deve bloquear o PR ate que o autor explique o motivo.

A Regra do Nao-Excluir

Um erro comum quando um apply falha: excluir o recurso com falha e aplicar mesmo assim.

# ERRADO tofu apply -exclude='aws_bedrockagent_knowledge_base.kb'

Isso cria um apply parcial: alguns recursos foram atualizados, outros nao. O arquivo de estado agora reflete uma configuracao parcialmente aplicada. O recurso excluido esta fora de sincronia com tudo que depende dele.

A resposta correta para um recurso com falha:

  1. Deixe o apply terminar. Nao o interrompa.
  2. Entenda por que o recurso falhou.
  3. Corrija a configuracao ou importe o recurso existente.
  4. Execute apply novamente ate zero erros.

Se o erro for "recurso ja existe": importe-o.
Se o erro for uma incompatibilidade de configuracao: corrija a configuracao para corresponder ao recurso existente.
Se o erro for um problema de ordenacao de dependencias: corrija depends_on.

Nunca: exclua, comente ou pule recursos. A configuracao IaC deve sempre refletir a realidade.

O Force-Unlock

O Terraform usa uma tabela de bloqueio (DynamoDB) para evitar applies simultaneos. Se um bloqueio travar (Lambda expirou no meio do apply, processo foi morto), voce vera:

Error: Error locking state: Error acquiring the state lock

A correcao tentadora: terraform force-unlock. Isso e perigoso se outro apply estiver realmente rodando - desbloquear a forca um apply ativo causa corrupcao.

A resposta correta:

  1. Verifique se algum apply esta realmente rodando (verifique o CloudWatch para a Lambda de deploy, verifique com os colegas).
  2. Se nenhum apply estiver rodando e voce tiver certeza de que o bloqueio e obsoleto, entao force-unlock.
  3. Documente que voce forcou um desbloqueio - e um sinal para investigar por que o apply anterior nao fez a limpeza adequada.

Nunca force-unlock como primeira resposta. Sempre verifique primeiro se nao ha um apply ativo.

Deteccao de Drift

Mesmo com disciplina rigorosa de IaC, o drift se acumula. A AWS pode modificar recursos automaticamente (ex.: Cognito atualizando uma associacao de trigger Lambda), ou um membro da equipe pode fazer uma alteracao de emergencia no console e esquecer de importa-la.

Agende uma execucao regular de plan (semanalmente ou apos periodos de atividade significativa) sem apply:

# No CI/CD: plan semanal agendado ./deploy.sh --infra --plan 2>&1 | tee plan-output.txt # Alertar se o plan mostrar alteracoes inesperadas

Se o plan mostrar alteracoes que nao deveriam existir, investigue antes que o proximo apply as destrua.

Pontos-Chave

  • O console AWS e para ler, nao para escrever. Toda alteracao de infraestrutura pertence a IaC.
  • Quando um recurso existe na AWS mas nao no Terraform: importe-o, reconcilie a configuracao e depois aplique.
  • Nunca use -exclude para contornar um recurso com falha. Corrija a causa raiz.
  • Encapsule todas as operacoes Terraform em um script de deploy. Padrao para plan; exija intencao explicita para aplicar.
  • Coloque a saida do plan em todo PR de infraestrutura. Destruicoes inesperadas devem bloquear o PR.
  • Nunca force-unlock sem verificar que nenhum apply ativo esta rodando.
  • Execute verificacoes periodicas de plan para detectar drift cedo.