Explicit Dependencies and Graph Review
Scenario: A policy attachment genuinely depends on a role finishing its (invisible, asynchronous) propagation inside the provider's backend, but no attribute reference exists anywhere to expose that timing relationship to Terraform.
You learned in Module 1 that Terraform infers most dependencies automatically, by tracing attribute references through your configuration. But occasionally a real dependency exists that no attribute reference can express - typically a purely behavioral or timing relationship, like "object B's creation must wait for object A to be fully propagated inside the provider's backend," where B doesn't actually consume any attribute of A. For exactly this narrow case, the depends_on meta-argument lets you declare a dependency explicitly, on a whole resource or module rather than a specific attribute.
Use it sparingly, though - depends_on widens the dependency graph beyond what real data flow requires, which serializes work that could otherwise run in parallel and can cascade (known after apply) uncertainty across a much larger set of resources than a true reference would. terraform graph will render your dependency graph (including any explicit depends_on edges) as a diagram, which is a genuinely useful way to see accidental over-serialization - but remember the graph is a planning aid; the actual plan output remains the authoritative list of real operations. Preconditions and postconditions (a related, more targeted tool) let you assert an assumption is true - like "this variable must be non-empty" - and fail fast with a clear message if it isn't, without going as far as a whole extra dependency edge.
Analogy: A project schedule normally derives its task order automatically from real deliverables - framing needs lumber delivered first, and the schedule reflects that on its own. depends_on is the sticky note saying "wait for the concrete to cure before framing starts" for a dependency the delivery schedule itself has no way to represent - genuinely useful, but reached for occasionally, not as your everyday scheduling tool.
A worked configuration
resource "fakecloud_policy_attachment" "api" {
role_id = fakecloud_role.api.id
policy = "read-secrets"
depends_on = [fakecloud_role_propagation.api]
}
Overusing depends_on at the module level (rather than a specific resource) is an especially easy way to accidentally make a large fraction of a plan's values unknown and serialize otherwise-independent work across an entire team's shared root module. Before adding one, always look first for a genuine attribute reference you could add instead - it's usually cleaner and self-documenting. When an explicit edge really is necessary, leave a comment explaining exactly why the reference-based graph couldn't express it, so the next engineer doesn't delete it as "unnecessary" six months from now.
Common mistakes in production
- Reaching for
depends_onas a first fix for an ordering problem instead of checking whether a genuine attribute reference already solves it more precisely. - Adding
depends_onat the whole-module level when only one specific resource inside that module actually has the timing dependency - this serializes far more work than necessary.
Goal: Treat the plan as a change contract: understand every create, update, replacement, and destroy before approving it. Reinforce this lesson in the tf-dependency-ordering Terraform lab. Open/labs/terraformand choose slugtf-dependency-ordering; the lab runs real Terraform against the offline FakeCloud provider.