Terraform / Language, providers, and managed identity / Lesson 5
Resource addresses and the dependency graph
Express real value relationships and distinguish configuration identity from runtime scheduling.
On this page
Lesson goals
- Explain resource addresses and implicit dependencies from references.
- Recognize cycles and the limits of explicit depends_on.
An address identifies a managed configuration instance
A resource block has a type and local name, forming an address such as terraform_data.catalog. Module paths and instance keys extend that address when repetition or child modules are involved. The address is not the provider’s remote object id. Changing it without an explicit refactoring strategy can make Terraform interpret old and new declarations as different managed identities. Choose stable names and keys from durable domain concepts rather than presentation order or values that change on every run.
References carry both values and dependency information
A reference to another resource’s attribute lets Terraform infer a dependency needed to evaluate or act safely. The consumer should use that reference rather than copy a value that merely looks equal. In the following complete fixture, consumer.input uses catalog.id. The id of a new instance is not known from source text, so the consumer value depends on the managed instance. This relationship is more meaningful than placing the catalog block earlier in the file. Nothing is created by displaying the configuration.
terraform {
required_version = ">= 1.16.0, < 1.17.0"
}
resource "terraform_data" "catalog" {
input = "atlas-lab"
}
resource "terraform_data" "consumer" {
input = { catalog_id = terraform_data.catalog.id }
}
output "consumer_input" {
value = terraform_data.consumer.input
}
The consumer refers to the managed catalog identity. A first creation can require unknown-value propagation before the id exists; this is not a string placeholder to guess. Terraform’s actual graph also includes provider and operation details beyond these two resource names.
Use explicit dependencies only for hidden relationships
depends_on can describe a necessary relationship not represented by an ordinary value reference, but it does not make an invalid architecture valid. Broad dependencies can force conservative ordering and cause additional values to remain unknown during planning. Prefer the narrowest relationship that expresses the actual requirement. Waiting for resource creation also may not mean that an application behind it is ready. Provider completion, service readiness, and business availability are separate contracts that must not be collapsed into one dependency declaration.
Inspect a small declared graph offline
The Python example orders a fixed acyclic dependency map. Each key waits for its listed prerequisites; ready nodes are sorted for a deterministic teaching display. This does not emulate Terraform’s full execution graph, provider scheduling, destruction ordering, or parallelism. It shows the basic invariant that a node cannot be selected before its declared prerequisites are removed. A lack of ready nodes indicates a cycle or inconsistent model, which should be investigated rather than hidden by arbitrary sleeps or manual resource creation.
pending = {'catalog': set(), 'consumer': {'catalog'}, 'output': {'consumer'}}
order = []
while pending:
ready = sorted(name for name, dependencies in pending.items() if not dependencies)
assert ready, 'cycle in synthetic graph'
order.extend(ready)
for name in ready:
del pending[name]
for dependencies in pending.values():
dependencies.difference_update(ready)
print(' -> '.join(order))
catalog -> consumer -> output
The declared order is catalog, consumer, then output. The loop processes only three in-memory nodes and contacts no provider. An output node here is a reasoning aid rather than a complete representation of Terraform graph internals.
A cycle often reveals an interface problem
If two resources or modules each require the other’s completed result to determine their own identity or arguments, the model may have no valid initial evaluation order. Adding another depends_on edge cannot remove that cycle. Identify which values truly need to cross the boundary, separate stable input identity from created output identity, and consider a more appropriate ownership split. Keep the design change reviewable and preserve existing state bindings through any address refactor. A graph fix should not be improvised as a state deletion.
Common errors and fixes
Reference to undeclared resource.
- Why it happens
- The referenced type/name or module scope does not contain the declared object.
- How to fix it
- Correct the address or module interface after checking the intended scope.
- Prevent it next time
- Review references as typed interfaces rather than copied text labels.
Cycle in dependencies.
- Why it happens
- Two or more values require each other before evaluation can begin.
- How to fix it
- Redesign the dependency boundary and remove the actual circular requirement.
- Prevent it next time
- Test the graph and separate stable keys from generated identities.
depends_on is added to every object.
- Why it happens
- Explicit ordering was used instead of understanding real data relationships.
- How to fix it
- Retain necessary references and narrowly justified hidden dependencies only.
- Prevent it next time
- Review unknown-value and scheduling effects of broad edges.
Check your understanding
1. Would moving the consumer block above catalog remove its dependency?
Show worked solution
No. The reference still connects the consumer to catalog.id regardless of file position. Terraform derives relationships from configuration semantics rather than textual resource order. The same source values and addresses should preserve that relationship after formatting or file organization changes.
2. Why does adding depends_on not repair a circular reference?
Show worked solution
It adds an ordering requirement rather than removing the existing circular requirement. Determine which value or ownership boundary is wrong and redesign it. Arbitrary ordering, manual creation, or state removal can obscure the architecture problem while introducing additional unmanaged or inconsistent objects.
Key takeaways
- Resource addresses are identities; references define relationships.
- A dependency is not proof of application readiness or a cure for cycles.