Terraform Guide

Terragrunt diagram: the run order graph and the architecture are two different pictures

By the engineer who builds Datadef, from client work on data platforms · Reviewed August 21, 2026

Terragrunt users asking for a diagram usually get pointed at dag graph, look at the output, and realize it answered a different question. The DAG is about order: which unit applies before which. The picture people wanted is about substance: what all of this builds. Both are legitimate, and only one of them is recoverable from terragrunt.hcl.

6 min readFor platform teams running Terragrunt over a modules directory

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

142/20003 credits left
Try:

No account needed · Editable canvas, not a picture

The DAG answers order, not architecture

Terragrunt ships dag graph, documented as an alias for list --format=dot --dependencies --external, which prints the dependency graph between units in DOT for Graphviz. Piped into dot you get a picture of the run queue: which unit has to finish before the next one starts. That is genuinely the thing you want when a run-all is taking forty minutes and you need to know what serialized it.

What that graph cannot show is contents. A unit is a box with a directory name on it. The vpc unit and the eks unit are two labelled rectangles whether the first declares four resources or forty, and nothing in the DAG says what a subnet is or which storage account feeds which workspace. Reviewers, new joiners, and auditors all ask the contents question.

Where the architecture actually lives in a Terragrunt repo

The typical unit directory holds a terragrunt.hcl with a terraform block pointing source at a module, an inputs block, and often a dependency block or two. There are no resource blocks there. The resources live in the module the source points at: usually a directory in the same repository under modules/, sometimes a registry or git address.

So the question of what does this build is answered by the module directories, not by the wrapper. That is convenient, because module directories are ordinary Terraform: resource blocks, variables, outputs, references. Parsing them recovers the same structure any other Terraform repo gives up.

What Datadef parses, and what it leaves alone

The sync fetches and parses files ending in .tf and .tfvars. terragrunt.hcl is not parsed, so dependency blocks, generate blocks, and inputs declared in the wrapper are not part of the model, and the diagram will not carry the run order. Stating that up front saves an evaluation.

What you do get, for a repo that keeps its modules under modules/ or similar, is the architecture of those modules: each module as a zone with its real resources inside, provider icons on the ones that carry the platform, repeated minor types rolled into one node with the count and member names, cross-module references resolved through outputs into labelled arrows. A module draws at most eight nodes before its remaining singles fold into one Supporting resources node with a count, which is what keeps a large modules directory legible.

The consequence for a live-only repository is worth knowing before you connect one. A repository containing nothing but a live/ tree of terragrunt.hcl files has zero .tf files, so the Terraform pipeline never engages, and the general file selector classifies by extension where .tf counts as infrastructure code and .hcl is not classified at all. What comes back is a diagram built from whatever else is in the repo, usually a README. Connect the repository holding the modules instead.

live/
  prod/
    vpc/terragrunt.hcl        <- wrapper, not parsed
    eks/terragrunt.hcl        <- wrapper, not parsed
modules/
  vpc/*.tf                  <- parsed: resources, variables, outputs
  eks/*.tf                  <- parsed: resources, variables, outputs

drawn: one zone per module, resources inside, references as arrows

Keeping it current without a run-all

Connect the repository read-only from GitHub, GitLab, or Azure DevOps, pick a branch or tag, and the diagram plus an architecture doc regenerate on a daily sync and on demand. Nothing runs terragrunt, nothing runs terraform init, no state is read, no cloud credentials are involved, which also means the analysis works on the repository during a freeze.

A commit that only touches a wrapper or a comment moves the sha but not the parsed structure, and a sync with an unchanged structure fingerprint skips regeneration, so the diagram history records module changes rather than noise. Put the current picture in the repo README with one live embed line, and read how to read a module structure for the directory rules the parse relies on.

FAQ

Does Datadef read terragrunt.hcl files?

No. The sync fetches and parses files ending in .tf and .tfvars only, so terragrunt.hcl wrappers, their dependency blocks, their generate blocks, and the inputs they set are outside the model. The architecture comes from the Terraform modules those units point at, which are ordinary .tf directories and parse like any other Terraform code.

What does terragrunt dag graph actually show?

The dependency graph between Terragrunt units, printed in DOT format for Graphviz. The Terragrunt documentation describes it as an alias for list --format=dot --dependencies --external. It answers run order, which unit must apply before the next, and it labels each unit with its directory name. It says nothing about the resources inside a unit.

Our modules live in the same repository under modules/. Does that work?

Yes, that is the common case and the good one. Those module directories are ordinary Terraform, so each becomes a zone holding its real resources, with cross-module references resolved through outputs into labelled arrows. A module keeps up to eight drawn nodes before its remaining single resources fold into one Supporting resources node carrying the count.

Do dependency block relationships appear as arrows?

No. Dependencies declared in terragrunt.hcl are not parsed. Arrows come from references inside the .tf files, for example a resource attribute reading module.vpc.vpc_id, which resolves through that module output to the declared resource on the other end. That is a different and more granular relationship than a unit-level dependency, and it is labelled with the attribute that carried it.

Does anything need to run, like terragrunt run-all or terraform init?

No. The analysis is a parse of the repository source. There is no terragrunt invocation, no terraform init, no plan, no state access, and no cloud credentials, so read-only access to the repository is the whole requirement. That also means it works during a change freeze, when nothing is allowed to be applied.