Dependencies Between Units
The idea in one sentence
Section titled “The idea in one sentence”A dependency block lets one unit read another unit’s outputs across the state boundary that separates them, and Terragrunt uses those same blocks to work out the order units must be applied in.
Why module composition is not enough anymore
Section titled “Why module composition is not enough anymore”In the Modules module you saw output-to-input composition: one module’s output feeding straight into another module’s input argument, inside a single main.tf, evaluated as one Terraform plan against one state file. That works because both modules are part of the same apply.
Terragrunt units break that assumption on purpose — a vpc unit and an ec2 unit each have their own terragrunt.hcl and their own separate state file, applied independently. module.vpc.outputs.vpc_id, referenced directly the way it was inside a single Terraform config, simply does not exist from the ec2 unit’s point of view; there is no shared configuration file for that reference to live inside. Something has to read across that state boundary explicitly, and that something is a dependency block.
dependency: reading across the state boundary
Section titled “dependency: reading across the state boundary”include "root" { path = find_in_parent_folders("root.hcl")}
terraform {}
dependency "vpc" { config_path = "../vpc"
mock_outputs = { vpc_id = "mock-vpc-id" }
mock_outputs_allowed_terraform_commands = ["plan"]}
inputs = { vpc_id = dependency.vpc.outputs.vpc_id}config_path points at the other unit’s directory — not a Terraform module, a Terragrunt unit. Before running, Terragrunt runs terraform output against that unit’s real state and exposes every output it finds as dependency.vpc.outputs.<name>, available anywhere in this unit’s config, including inside inputs.
mock_outputs: letting plan succeed before the dependency exists
Section titled “mock_outputs: letting plan succeed before the dependency exists”dependency.vpc.outputs.vpc_id only has a real value once the vpc unit has actually been applied at least once. Two situations break that assumption: standing up a brand-new environment for the first time, where nothing has been applied yet, and CI validating a pull request that only touched the ec2 unit’s files, where nobody wants a plan to fail just because some unrelated unit has not been applied in that particular environment.
mock_outputs covers exactly this gap. It supplies placeholder values Terragrunt substitutes in whenever the real outputs are not available, and mock_outputs_allowed_terraform_commands restricts which commands are allowed to fall back to them — ["plan"] above means a plan on ec2 succeeds using vpc_id = "mock-vpc-id" even with no real vpc state yet, while an apply still refuses to run against a fake value and forces the real dependency to be applied first.
How Terragrunt orders units automatically
Section titled “How Terragrunt orders units automatically”Terraform already computes an execution order inside a single state — its own dependency graph between individual resources. A dependency block hands Terragrunt the equivalent information one level higher, between whole units: since ec2 declares a dependency on vpc, Terragrunt knows vpc has to be applied first. Multiply this across a real tree of a dozen units, and Terragrunt builds a full graph from every dependency block it finds, then walks it in the correct order for any command that touches more than one unit at once — covered fully in the next lesson — without anyone maintaining that ordering by hand.
flowchart LR vpcstate["vpc unit state (applied separately)"] --> vpcout["vpc outputs: vpc_id"] vpcout --> dep["ec2 terragrunt.hcl: dependency vpc block"] dep --> ecin["ec2 inputs: vpc_id = dependency.vpc.outputs.vpc_id"] mock["mock_outputs: vpc_id = mock-vpc-id"] -.->|lets plan succeed before vpc is applied| dep