Terra Estrutura Interna - Estrutura interna da Terra: a divisão das Camadas da Terra - Toda Matéria
Estrutura interna da Terra: a divisão das Camadas da Terra - Toda Matéria

Como o Terraform funciona por baixo do capô

O Terraform não é apenas uma ferramenta de linha de comando que lê arquivos e cria recursos. Por trás dele existe uma arquitetura com camadas bem definidas que, se você entender, resolve metade dos problemas que aparecem na prática. O que muita gente chama de terra estrutura interna na verdade envolve três pilares principais: o motor de parsing, o cycle de plan/apply, e o estado. Vou explicar como cada um funciona sem enrolação.

O estado e o que ele realmente armazena

O arquivo de estado é o coração do Terraform, mas não é o que a maioria dos devs imagina. Ele guarda um mapa completo de cada recurso gerenciado, incluindo os atributos retornados pela API do provedor no momento do apply. Isso significa que o estado contém dados sensíveis, como IPs, nomes de buckets, IDs de instâncias, e — em versões mais antigas — valores em plaintext como senhas e chaves de API. O estado é versionado e precisa permanecer consistente entre todos os membros da equipe que roda o Terraform. Eu tive um problema específico uma vez em que um repositório compartilhado no S3 tinha o backend de estado configurado sem versioning ativado. Um colega fez um apply cancelado no meio do caminho, o que corrompeu o arquivo de estado. Como o S3 não tinha versioning, não havia como recuperar o estado anterior. A correção foi restaurar de um backup diário que tínhamos em outra região, mas o tempo perdido foi de cerca de quatro horas. Desde então, sempre configuro backend com versioning e lock antes de qualquer deploy. Não é opcional.

Terra estrutura interna: o ciclo de execução

O ciclo do Terraform segue quatro etapas sequenciais: init, plan, apply, e destroy. Cada uma delas opera sobre um grafo de dependência construído a partir da declaração dos recursos no código HCL. Quando você roda terraform plan, o Terraform lê todos os arquivos .tf, constrói o grafo, compara o estado atual com o desejado, e gera um plano de execução. O apply então executa as mudanças em ordem topológica, respeitando dependências entre recursos. Recursos que não têm dependência são criados em paralelo, o que explica por que alguns plans levam segundos enquanto outros levam minutos dependendo da quantidade de recursos independentes. Uma coisa que poucos sabem é que o Terraform não recarrega o estado a cada recurso individualmente durante o apply. Ele carrega o estado inteiro no início, faz o plan na memória, e depois aplica usando aquele snapshot. Se dois processos rodarem ao mesmo tempo contra o mesmo estado remoto sem locking adequado, o segundo apply sobrescreve dados do primeiro de forma silenciosa. O lock do DynamoDB no AWS resolve isso, mas em ambientes multi-cloud a situação fica mais complicada.

Providers e a abstração de recursos

Cada provedor no Terraform é um plugin escrito em Go que implementa uma interface padrão. O provider traduz operações CRUD entre a linguagem de configuração e as APIs nativas dos serviços cloud. Quando você declara um aws_instance, o provider da AWS converte aquela declaração para chamadas à API do EC2. Isso é importante porque significa que o comportamento de cada recurso depende diretamente da qualidade e do versionamento do provider, não apenas do código do Terraform em si. O versionamento de providers segue semver, mas há uma pegadinha: mudanças no provider podem causar drift no estado sem aviso. Eu vi um upgrade do provider AWS de 3.42 para 4.10 que mudou o formato de um campo no resource aws_security_group_rule. O apply não deu erro, mas o estado passou a guardar o novo formato. Quando alguém rodou plan semanas depois, o Terraform indicava que todos os security groups precisavam ser recriados, o que em produção seria catastrófico. A solução foi rodar terraform state mv para corrigir manualmente os recursos afetados antes de prosseguir.

👉 Clique no botão abaixo para saber mais sobre o assunto!

HCL e como o código é interpretado

O Terraform usa HCL2 como linguagem de configuração. HCL é legível por humanos, mas diferente de YAML ou JSON porque suporta metadados estruturados diretamente no syntax tree. Blocos como resource, variable, e module são parseados e transformados em estruturas de dados internas que alimentam o grafo de dependência. Variáveis não são simplesmente strings — elas têm tipos, validações, e default values que são resolvidos em tempo de parse, não de execução. Uma limitação importante do HCL é que ele não suporta lógica condicional tradicional. Você não pode fazer um if/else simples para decidir se um recurso existe ou não. A solução padrão é usar count e conditional expressions, mas isso tem um custo: count 0 ainda cria a entrada no estado como resource ausente, o que pode causar confusão em drift detection. Módulo que depende de variáveis boolianas para habilitar/desabilitar recursos inteiros frequentemente gera state entries órfãos que só são limpos com um destroy explícito.

Módulos e composição

Módulos no Terraform são basicamente funções que recebem inputs e produzem outputs. Eles permitem reutilização e encapsulamento, mas a forma como o Terraform resolve paths de módulos internos pode ser confusa. Quando você referencia um módulo local com source = ../outro-modulo, o Terraform copia o conteúdo daquele diretório para um cache interno. Isso significa que se você modificar o código do módulo fonte enquanto outro desenvolvedor já tiver feito init, o plan dele não refletem as alterações até que rode init novamente. Já perdi umas duas horas caçando bug que era só isso — código atualizado localmente mas não na cache do init.

Persistência de recursos e destroy safety

Quando um recurso é criado pelo Terraform, o ID dele é salvo no estado. No destroy, o Terraform consulta o estado para saber exatamente qual recurso destruir. Se o recurso foi criado manualmente fora do Terraform, ou se o ID foi alterado por outra ferramenta, o destroy simplesmente falha silenciosamente e o recurso permanece no estado. Isso é particularmente problemático com recursos que têm lifecycle policies, como buckets S3 com versioning ativo — deletar o bucket manualmente e rodar destroy depois deixa o estado inconsistentemente vinculado a um recurso que já não existe. A recomendação padrão é usar terraform import para ressincronizar recursos órfãos, mas importar manualmente dezenas de recursos é trabalhoso. Em um projeto real, acabei tendo que importar cerca de trinta instâncias de banco de dados que foram criadas via console AWS por um engenheiro de infraestrutura antes da adoção do Terraform. O processo levou um dia inteiro porque cada resource tinha diferentes provider configurations e dependências cruzadas. Isso mostra uma limitação séria do Terraform: ele funciona muito bem quando desde o início tudo é gerenciado por infraestrutura como código, mas entra em colapso quando há resources driftados no estado.

Alternativas como Pulumi ou Crossplane lidam com esse cenário de forma diferente porque não dependem exclusivamente de um arquivo de estado centralizado, mas cada uma tem seus próprios trade-offs em termos de curva de aprendizado e ecossistema de provedores.