Terragrunt Stacks por Dentro: Implícitos vs. Explícitos (e Quando Usar Cada Um)
Introdução
Existe um equívoco recorrente de que os Terragrunt Stacks constituem apenas uma renomeação conceitual para a prática, já consolidada, de agrupar múltiplas units em uma árvore de diretórios. Trata-se de uma simplificação que ignora a mudança arquitetural mais significativa introduzida no Terragrunt 1.0.
A confusão é compreensível, porque o termo “stack” descreve, de fato, duas realidades distintas dentro da ferramenta. Uma delas é implícita, derivada da estrutura do sistema de arquivos. A outra é explícita, declarada e gerada sob demanda. A diferença entre ambas não é semântica, e sim operacional: ela define se a sua infraestrutura é versionável como um artefato ou se permanece refém da disposição das pastas.
Este artigo disseca a anatomia dos Terragrunt Stacks, contrasta o modelo implícito com o explícito, detalha o fluxo de injeção de valores via terragrunt.stack.hcl e estabelece o critério de decisão para a adoção de cada abordagem.
A Dicotomia Fundamental: Implícito vs. Explícito
Antes de qualquer código, é necessário fixar a distinção que estrutura todo o restante do artigo. Um stack, no Terragrunt, é uma coleção de units tratada como uma unidade lógica de orquestração. Essa coleção pode ser definida de duas formas.
- Stack implícito. Qualquer diretório-pai que contenha units em seus subdiretórios. É o comportamento legado e padrão. Aqui, a estrutura de pastas é a fonte da verdade. Ao executar
terragrunt run --allna raiz, o Terragrunt percorre essa árvore e a trata como um stack. - Stack explícito. Uma coleção declarada em um arquivo
terragrunt.stack.hcl, que descreve quais units instanciar, de qual origem (source) buscá-las e quais valores injetar. As units não residem no repositório; elas são geradas sob demanda.
O stack implícito é transparente, porém rígido. Mover um diretório quebra referências de include e dependency, e a coleção de infraestrutura não pode ser versionada de forma atômica. O stack explícito inverte essa lógica: a definição da coleção passa a ser código versionado, e a árvore de diretórios torna-se um artefato gerado e descartável.
A Anatomia do terragrunt.stack.hcl
O arquivo terragrunt.stack.hcl é o coração do modelo explícito. Ele é composto, fundamentalmente, por blocos unit e, opcionalmente, por blocos stack (que abordaremos na seção de composição).
Cada bloco unit declara uma instância de infraestrutura a ser materializada:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# live/prod/terragrunt.stack.hcl
unit "vpc" {
source = "git::git@github.com:sua-org/catalog.git//units/vpc?ref=v2.3.0"
path = "vpc"
values = {
cidr_block = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b"]
}
}
unit "database" {
source = "git::git@github.com:sua-org/catalog.git//units/database?ref=v2.3.0"
path = "database"
values = {
engine_version = "16.3"
multi_az = true
}
}
Três atributos definem cada unit:
source: a origem da unit. O uso de uma referência versionada (?ref=v2.3.0) é o que confere à coleção a capacidade de ser atualizada de forma explícita e reversível.path: o caminho relativo onde a unit será gerada dentro do diretório.terragrunt-stack.values: o mapa de valores injetado na unit no momento da geração.
A ausência de qualquer terragrunt.hcl no diretório de produção é intencional. O que existe é a descrição da coleção; a coleção em si é gerada.
O Fluxo de Valores: values e terragrunt.values.hcl
O atributo values é o mecanismo que resolve o problema da duplicação. Ele permite instanciar a mesma source com parâmetros distintos por ambiente, sem replicar arquivos.
Durante a geração, o Terragrunt materializa o conteúdo do bloco values em um arquivo terragrunt.values.hcl, depositado no diretório da unit. A definição da unit, por sua vez, consome esses dados através do objeto values:
1
2
3
4
5
6
7
8
9
10
# catalog/units/database/terragrunt.hcl
terraform {
source = "git::git@github.com:sua-org/modules.git//rds?ref=v4.1.0"
}
inputs = {
engine_version = values.engine_version
multi_az = values.multi_az
}
O contrato é claro e explícito. A unit no catálogo declara uma interface de valores que espera receber, e o terragrunt.stack.hcl de cada ambiente preenche essa interface. Você define a unit uma única vez e a reutiliza em dev, staging e prod, variando apenas o conteúdo do bloco values.
Essa separação entre a definição da unit e a sua parametrização é o que torna o modelo verdadeiramente DRY (Don’t Repeat Yourself: o princípio de não repetir a mesma definição em múltiplos lugares).
Composição em Escala: Stacks Aninhados e Catálogos
O modelo explícito não se limita a units. Um terragrunt.stack.hcl pode referenciar outros stacks por meio do bloco stack, habilitando a composição hierárquica.
1
2
3
4
5
6
7
8
9
10
# live/prod/terragrunt.stack.hcl
stack "networking" {
source = "git::git@github.com:sua-org/catalog.git//stacks/networking?ref=v2.3.0"
path = "networking"
values = {
environment = "prod"
}
}
Esse mecanismo viabiliza o conceito de catálogo de infraestrutura: um repositório versionado que expõe stacks reutilizáveis (uma malha de rede completa, uma plataforma de dados, um ambiente Kubernetes), consumidos por diferentes equipes e ambientes.
A vantagem estratégica é a governança. Uma atualização no stack de rede do catálogo, promovida para a versão v2.4.0, propaga-se para todos os ambientes de forma controlada, com a simples alteração de uma referência. A coleção de infraestrutura passa a ter um ciclo de release próprio, equiparável ao de qualquer outro artefato de software.
Geração Sob Demanda: O Ciclo de Vida do .terragrunt-stack
Definida a coleção, ela precisa ser materializada. O Terragrunt expõe um conjunto de subcomandos dedicados ao ciclo de vida do stack explícito:
1
2
3
4
5
6
7
8
# Materializa todas as units e stacks no diretório .terragrunt-stack
terragrunt stack generate
# Executa um comando sobre a stack gerada, em ordem de dependência
terragrunt stack run plan
# Consolida os outputs de todas as units pertencentes à stack
terragrunt stack output
O diretório .terragrunt-stack resultante é um produto da geração, e não uma fonte da verdade. Em um fluxo maduro, no qual o pipeline de CI/CD executa terragrunt stack generate por conta própria, esse diretório deve ser excluído do versionamento:
# .gitignore
.terragrunt-stack/
Você inspeciona o conteúdo gerado localmente antes de qualquer aplicação, garantindo que a materialização produza exatamente as units esperadas. A natureza determinística dessa geração é o que torna o modelo confiável.
A Decisão: Quando Adotar Cada Modelo
A escolha entre implícito e explícito não é uma questão de preferência estética, e sim de escala e governança. O critério a seguir orienta a decisão:
- Permaneça com stacks implícitos quando o número de units é reduzido, cada unit é genuinamente única e a transparência total da árvore de diretórios é mais valiosa do que a redução de duplicação.
- Adote stacks explícitos quando há múltiplos ambientes que compartilham os mesmos padrões, quando a duplicação de arquivos
terragrunt.hcljá se tornou um custo de manutenção, ou quando a infraestrutura precisa ser versionada e distribuída como um catálogo.
A migração entre os dois modelos é incremental. Units soltas e stacks explícitos coexistem no mesmo repositório, o que permite uma adoção gradual, ambiente por ambiente.
O Impacto na Engenharia de Plataforma
A introdução dos stacks explícitos representa mais do que uma otimização de organização de arquivos. Ela altera o paradigma de propriedade e distribuição da infraestrutura.
Ao converter coleções de units em artefatos versionados, a Engenharia de Plataforma ganha a capacidade de tratar infraestrutura como um produto interno. Times de aplicação consomem stacks de um catálogo curado, sem precisar compreender os detalhes de implementação de cada módulo. A equipe de plataforma, por sua vez, evolui esses stacks de forma centralizada, com versionamento semântico e ciclos de release controlados.
Essa é a transição de uma operação baseada em convenção de diretórios para uma operação baseada em contratos versionados. É a diferença entre manter infraestrutura e distribuí-la como capacidade.
Conclusão
O Terragrunt Stacks não são uma renomeação. O modelo implícito reflete a estrutura de arquivos; o modelo explícito a substitui por uma declaração versionada, parametrizável e componível. A primeira abordagem é adequada para repositórios pequenos e específicos; a segunda é o caminho para a escala, a reutilização e a governança.