← Back to list

Terraform no Azure com Terragrunt: menos repetição, mais previsibilidade

No Azure, criar recursos “na mão” pelo portal parece rápido no começo. Você abre o portal, cria uma VNET, sobe uma VM, ajusta uma NSG… e em…

Wallace Maia in nddtech · 2026-02-23 12:44 · 0 claps · 5.0 min read
#terraform #iac #ansible #devops #tecnologia
Open on Medium ↗
Wiki topics: RAG · RAG & Retrieval ☁️ · DevOps & Cloud

Terraform no Azure com Terragrunt: menos repetição, mais previsibilidade

No Azure, criar recursos “na mão” pelo portal parece rápido no começo. Você abre o portal, cria uma VNET, sobe uma VM, ajusta uma NSG… e em poucos minutos tudo está “de pé”.

O problema aparece quando essa infraestrutura vira rotina.

Quando você precisa repetir o mesmo desenho em outro ambiente (dev/hml/prod), manter padrões, auditar “quem mudou o quê” e recriar tudo com segurança (especialmente em POCs que nascem e morrem o tempo todo), o portal começa a virar um gargalo. A cada ajuste feito “só aqui”, os ambientes inevitavelmente divergem. E, quando você percebe, a diferença entre dev e prod não é mais “configuração”: virou um conjunto de decisões implícitas que ninguém consegue explicar direito.

É aqui que Terraform muda o jogo. E, quando o projeto cresce, Terragrunt entra como uma camada que resolve um problema muito específico: reduzir repetição, organizar estados e padronizar ambientes sem você copiar e colar providers.tf​, backend.tf​, versions.tf​ em todo lugar.

Infraestrutura como código (de verdade)

A ideia continua simples: infraestrutura vira código.

Em vez de clicar no portal, você descreve o que precisa existir rede, AKS, Key Vault, ACR, VMs, discos e aplica isso de forma padronizada e rastreável.

O ganho real de IaC não é “criar mais rápido”. É:

  • eliminar drift (a divergência silenciosa entre ambientes)
  • dar previsibilidade (a mesma entrada gera o mesmo resultado)
  • criar histórico confiável (git + revisão + rastreabilidade)
  • reduzir risco em mudanças (plan antes do apply, revisão antes do merge)

O ponto é que, com o tempo, o desafio deixa de ser “escrever Terraform” e passa a ser “manter dezenas de stacks consistentes”. E é justamente aí que Terragrunt brilha.

Por que Terragrunt entra na conversa

Terragrunt não substitui Terraform. Ele organiza o uso do Terraform.

Na prática, ele resolve três dores comuns em ambientes Azure que crescem rápido:

  • ​DRY de verdade​: você para de repetir provider/backend/versions em toda stack.
  • ​Estados bem organizados​: um state por componente, com naming consistente.
  • ​Orquestração por dependências​: você aplica infraestrutura em ordem (e com segurança) usando outputs como contratos.

O resultado é uma estrutura que escala melhor quando você começa a ter fundação, plataforma e workloads evoluindo ao mesmo tempo.

Exemplo prático: DefectDojo e Dependency-Track (infra + banco)

Ao implantar DefectDojo e ​Dependency-Track​, existe uma base mínima no Azure antes da aplicação em si:

  • Resource Group
  • Rede (VNET, subnets, NSGs)
  • VM Linux dedicada para PostgreSQL
  • Public IP / DNS (quando necessário)
  • Storage
  • AKS

Com Terraform + Terragrunt, isso vira um fluxo claro:

  1. Provisiona a infraestrutura base (rede, compute, storage, AKS).
  2. Entra o Ansible para instalar e configurar o PostgreSQL (extensões, parâmetros, acesso, bancos e usuários).
  3. A camada de aplicação vem depois (Helm, manifests, pipelines), consumindo outputs e secrets de forma padronizada.

A divisão de responsabilidades continua objetiva:

  • ​Terraform​: ciclo de vida da infraestrutura e estado.
  • ​Terragrunt​: organização, padronização, backends, inputs por ambiente e dependências.
  • ​Ansible​: configuração do sistema e do software de forma idempotente.

Organização do repositório: módulos e stacks (com Terragrunt)

Uma organização prática e escalável separa:

  • o que é reutilizável (módulos Terraform)
  • do que é específico de ambiente (stacks com Terragrunt)

A lógica lembra a separação entre “código reutilizável” e “orquestração”.

Estrutura base (exemplo)

infra/
├── catalog/
│   └── modules/
│       ├── azurerm_resource_group/
│       ├── azurerm_virtual_network/
│       ├── azurerm_subnet/
│       ├── azurerm_public_ip/
│       ├── azurerm_network_interface/
│       ├── azurerm_linux_virtual_machine/
│       ├── azurerm_managed_disk/
│       ├── azurerm_key_vault/
│       ├── azurerm_storage_account/
│       ├── azurerm_container_registry/
│       └── azurerm_kubernetes/
│
└── stacks/
    └── nde/
        ├── root.hcl
        ├── env.hcl
        ├── dev/
        │   ├── foundation/
        │   │   └── network/
        │   │       └── terragrunt.hcl
        │   ├── platform/
        │   │   └── keyvault/
        │   │       └── terragrunt.hcl
        │   └── workloads/
        │       └── defectdojo/
        │           ├── compute/
        │           │   └── terragrunt.hcl
        │           └── aks/
        │               └── terragrunt.hcl
        ├── hml/
        │   └── ...
        └── prd/
            └── ...

Como ler essa estrutura

catalog/modules/ Módulos Terraform reutilizáveis. Pequenos, com contrato claro (inputs/outputs), fáceis de versionar e reaproveitar.

stacks/ O “live” do Terragrunt: cada pasta representa uma unidade de execução com seu próprio terragrunt.hcl​ apontando para um módulo e definindo inputs, dependências e estado.

root.hcl / env.hcl Arquivos de “configuração base” que evitam repetição: backend, provider, tags, naming, subscription/tenant, padrões de rede e qualquer coisa compartilhada.

O coração do Terragrunt: herança e padrão de state

Uma prática que funciona muito bem é ter um root.hcl​ com:

  • ​remote_state​ centralizado (Azure Storage)
  • ​generate​ para provider e versões
  • padrões comuns (tags, naming, features)
  • convenções para o key do state baseado no caminho

Exemplo de root.hcl​:

locals {
  project = "nde"
}
remote_state {
  backend = "azurerm"
  config = {
    resource_group_name  = "rg-tfstate"
    storage_account_name = "sttfstateprod001"
    container_name       = "tfstate"
    key                  = "${path_relative_to_include()}/terraform.tfstate"
  }
}
generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
provider "azurerm" {
  features {}
}
EOF
}
generate "versions" {
  path      = "versions.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
terraform {
  required_version = ">= 1.6.0"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.0.0"
    }
  }
}
EOF
}

E um env.hcl​ para variáveis do ambiente:

locals {
  location    = "eastus"
  environment = "dev"
  tags = {
    env     = "dev"
    project = "nde"
  }
}

Uma stack por componente (e estados pequenos)

No Terragrunt, cada pasta com terragrunt.hcl​ normalmente representa:

  • um módulo Terraform
  • um state isolado
  • uma unidade que faz sentido evoluir e aplicar separadamente

Isso ajuda muito quando você precisa:

  • reaplicar só um pedaço
  • dar rollback com menor blast radius
  • evoluir rede e compute em ritmos diferentes
  • reduzir tempo de plan/apply

Dependências como contratos (sem acoplamento implícito)

Quando uma stack depende de outra, Terragrunt permite consumir outputs como contrato explícito.

Exemplo: o AKS depende da rede.

​stacks/nde/dev/foundation/network/terragrunt.hcl​:

include "root" {
  path = find_in_parent_folders("root.hcl")
}
locals {
  env = read_terragrunt_config(find_in_parent_folders("env.hcl")).locals
}
terraform {
  source = "${get_repo_root()}//catalog/modules/azurerm_virtual_network"
}
inputs = {
  location = local.env.location
  tags     = local.env.tags
}

​stacks/nde/dev/workloads/defectdojo/aks/terragrunt.hcl​:

include "root" {
  path = find_in_parent_folders("root.hcl")
}
locals {
  env = read_terragrunt_config(find_in_parent_folders("env.hcl")).locals
}
dependency "network" {
  config_path = "../../../foundation/network"
}
terraform {
  source = "${get_repo_root()}//catalog/modules/azurerm_kubernetes"
}
inputs = {
  location      = local.env.location
  tags          = local.env.tags
  subnet_id     = dependency.network.outputs.snet_aks_id
}

Isso evita o “estado mental” de lembrar IDs na mão, evita terraform_remote_state​ espalhado e deixa a ordem de execução explícita.

Executando por camadas com segurança

Com Terragrunt, você consegue aplicar por nível:

  • ​foundation​ primeiro
  • depois platform​
  • depois workloads​

E consegue fazer isso com:

  • ​terragrunt run-all plan​
  • ​terragrunt run-all apply​

Cada componente com seu state, e as dependências guiando a ordem quando necessário.

Ambientes (dev / hml / prd) sem workspaces

O padrão mais previsível continua sendo:

  • pastas separadas por ambiente
  • mesmo código, inputs diferentes
  • state isolado por ambiente
  • backend padronizado e automático

Isso reduz muito o risco de “aplicar no lugar errado” e facilita auditoria e rollback.

O que foi configurado para DefectDojo e Dependency-Track

Para essas duas aplicações, normalmente precisamos de:

  • bancos dedicados (ex.: defectdojo​, dependencytrack​)
  • usuários dedicados para cada aplicação
  • permissões corretas
  • ajustes e acesso (ex.: pg_hba.conf​, listen_addresses​, parâmetros)

Com Terraform + Terragrunt, a infraestrutura que suporta isso vira padrão. Com Ansible, a configuração do PostgreSQL vira código versionado. Se amanhã precisar recriar tudo, o processo deixa de depender de “memória” e vira execução.

Ganhos na prática

Os ganhos mais claros ao organizar Terraform com Terragrunt foram:

  • menos repetição de arquivos e configurações entre stacks
  • estados menores, mais rápidos de planejar e aplicar
  • padronização forte entre ambientes
  • dependências explícitas via outputs (contratos claros)
  • execução por camadas, com menos risco
  • reuso real: mesmos módulos, inputs por ambiente

Resumo final

Terraform resolve o “o que” da infraestrutura. Terragrunt resolve o “como manter isso organizado” quando o ambiente cresce.

No caso de DefectDojo e ​Dependency-Track​, essa combinação ajudou a criar uma base consistente no Azure rede, compute, storage e AKS e permitiu recriar ambientes em minutos, algo essencial em POCs e ciclos de teste frequentes. No fim, a infraestrutura fica mais previsível, o estado fica mais controlado e o repositório deixa de virar uma coleção de arquivos repetidos.


메타데이터
post_id
c87f5e4d4c65
slug
terraform-no-azure-com-terragrunt-menos-repetição-mais-previsibilidade-c87f5e4d4c65
url
https://making.ndd.tech/terraform-no-azure-com-terragrunt-menos-repeti%C3%A7%C3%A3o-mais-previsibilidade-c87f5e4d4c65
canonical_url
https://making.ndd.tech/terraform-no-azure-com-terragrunt-menos-repeti%C3%A7%C3%A3o-mais-previsibilidade-c87f5e4d4c65
author_url
https://medium.com/@wallacemaiadev
status
ok
fetched_at
2026-06-10 08:17:25