Resources Dependencies and the Dependency Graph
Terraform configuration may contain dozens of resource blocks, but Terraform does not execute those blocks from top to bottom. Instead, it builds a dependency graph and uses that graph to determine what can happen first, what must wait, and what can happen in parallel.
Understanding the graph explains a lot of Terraform behavior that otherwise feels mysterious.
Terraform Does Not Read .tf Files Top-to-Bottom
Suppose you have:
resource "some_provider_server" "web" {
name = "web"
}
resource "some_provider_network" "main" {
name = "main"
}The fact that the server appears first does not mean Terraform tries to create the server first.
Terraform loads the configuration as a whole and analyzes relationships between objects.
Think of the configuration as a set of declarations rather than a script:
.tf files
│
â–¼
Terraform configuration model
│
â–¼
Dependency graph
│
â–¼
Execution planThe filename and position of a resource block do not establish execution order.
How Dependencies Are Created
The most common dependency is created automatically when one resource references another.
Imagine a network and a server:
resource "some_provider_network" "main" {
name = "main"
}
resource "some_provider_server" "web" {
name = "web"
network = some_provider_network.main.id
}The server references the network’s ID.
Terraform can therefore infer:
Network
↓
ServerThe server depends on the network because Terraform cannot evaluate the server’s network argument until the network’s ID is available.
This is an implicit dependency.
Implicit Dependencies Are Usually Best
Whenever possible, express the relationship through an actual reference.
For example:
network = some_provider_network.main.idis better than trying to force Terraform to guess or manually order unrelated-looking resources.
The reference tells Terraform both:
- which value the server needs, and
- that the server depends on the network.
That makes the configuration communicate its actual relationship.
A More Realistic Dependency Chain
Infrastructure often forms a chain:
flowchart TD
A[Network] --> B[Security]
B --> C[Compute]
C --> D[Application]
For example:
- A network must exist before a compute resource can attach to it.
- Security rules may need the network identifier.
- An application may need the compute resource’s address.
- Terraform follows these relationships when constructing the graph.
The important point is that Terraform is not merely executing four commands in a predefined order. It is respecting the relationships between objects.
Parallelism: Terraform Can Do More Than One Thing at Once
Now imagine two independent resources:
Network
/ \
â–¼ â–¼
Server A Server BIf Server A and Server B both depend on the network but not on each other, Terraform does not need to wait for Server A before starting Server B.
Conceptually:
Network
/ \
â–¼ â–¼
Server A Server BOnce the network is ready, the independent operations can proceed concurrently.
This is one of the major benefits of using a dependency graph.
Terraform can respect required ordering without unnecessarily serializing everything.
Note
Dependency ordering does not mean “Terraform performs one resource at a time.” It means Terraform respects dependency constraints while allowing independent work to proceed concurrently.
Explicit Dependencies with depends_on
Sometimes there is a real dependency that Terraform cannot infer from an expression.
That is where depends_on is useful.
For example:
resource "some_provider_server" "web" {
name = "web"
depends_on = [
some_provider_security.policy
]
}This tells Terraform:
security.policy
↓
server.webEven if there is no attribute reference connecting the two resources, Terraform knows that the server must wait for the security resource.
Why Would Terraform Need This?
Imagine a provider has two operations:
Security policy
↓
ServerBut the server resource does not require a policy ID as one of its arguments. Terraform cannot infer a dependency from a value reference because no reference exists. If the real infrastructure relationship still matters, an explicit dependency communicates it.
Don’t Use depends_on as a General Ordering Tool
A common mistake is to use depends_on to force an ordering that simply “feels safer.”
For example:
resource "some_provider_server" "one" {
name = "one"
}
resource "some_provider_server" "two" {
name = "two"
depends_on = [
some_provider_server.one
]
}If Server Two has no actual dependency on Server One, this unnecessarily restricts Terraform.
You have turned:
Server One Server Twointo:
Server One
↓
Server TwoTerraform loses the ability to perform those operations independently.
Tip
Prefer implicit dependencies created by resource references. Use depends_on when a real dependency exists but cannot be represented naturally through an expression.
Dependency Chains
Dependencies can form longer chains.
For example:
Network
↓
Security
↓
Compute
↓
ApplicationTerraform understands the transitive relationship.
If Compute depends on Security and Security depends on Network, Terraform knows that Compute ultimately depends on Network.
You do not need to write:
depends_on = [
network,
security
]on every downstream resource.
A direct dependency is normally enough for Terraform to derive the rest of the graph.
Dependency Cycles
A graph must have a direction.
This creates a problem:
A → B
↑ ↓
└───┘Here A depends on B while B depends on A.
Terraform cannot satisfy both requirements because neither resource can be created first.
A configuration that creates such a dependency cycle will fail during planning.
The important lesson is that a dependency cycle is usually a design problem, not something you fix by adding more depends_on.
Look for relationships such as:
resource A needs value from B
resource B needs value from AYou may need to redesign the resources or the way their values are produced.
Diagnosing Dependency Problems
When Terraform behaves unexpectedly, first inspect the relationships rather than assuming the file order is responsible.
Useful questions include:
- Does one resource reference another?
- Is the reference creating an implicit dependency?
- Did I add
depends_onunnecessarily? - Is there a hidden dependency that Terraform cannot see?
- Have I accidentally created a cycle?
Terraform can also render its dependency graph using:
terraform graphThe output is intended for graph visualization tools, so the raw output is not necessarily the easiest thing to read directly.
The important point is that Terraform has an explicit graph representation of the relationships it has inferred.
The Mental Model
Think of Terraform configuration as a graph rather than a script:
flowchart LR
A[Configuration] --> B[Dependency Graph]
B --> C[Plan]
C --> D[Execution]
Resource references create edges in that graph.
depends_on can add an edge when Terraform cannot infer the relationship itself.
Independent nodes can be processed independently, while dependent nodes wait for their prerequisites.
Once you start thinking in terms of the graph, Terraform’s execution behavior becomes much easier to predict.
The next piece of the engine is different: sometimes Terraform should not create anything at all. It simply needs information about something that already exists.
That is the job of data sources.