Dependency Graph: DAG Generation & Implicit vs Explicit Dependencies
Constructs a Directed Acyclic Graph where nodes are resources and vertices are dependencies.
Independent branches of the DAG execute concurrently across parallel worker threads.
Explicitly force sequencing when hidden dependencies exist that Terraform cannot infer.
The Big Idea
How does Terraform know the exact order in which to create twenty cloud resources across three subnets and two regions? It does not read your files from top to bottom. Instead, it compiles your entire configuration into a Directed Acyclic Graph (DAG).
In this graph, every resource is a node, and every dependency is a directed edge pointing from dependent to prerequisite. Terraform traverses the graph in reverse topological order. Any nodes with zero unsatisfied dependencies are executed in parallel using a pool of worker threads (default: 10 parallel operations).
There are two types of dependencies: Implicit dependencies, created naturally whenever one resource references an attribute of another, and Explicit dependencies, defined using the depends_on meta-argument for out-of-band requirements like IAM permissions propagation.
Jargon Buster: Plain-English Translations
- DAG (Directed Acyclic Graph): A mathematical graph structure with directed edges and no closed loops, ensuring execution never enters an infinite deadlock.
- Implicit Dependency: A relationship automatically deduced by Terraform when resource A references an exported attribute of resource B (e.g. 'subnet_id = aws_subnet.app.id').
- Explicit Dependency: A relationship manually enforced with the 'depends_on' meta-argument when no direct attribute reference exists in code.
- Topological Sort: The algorithmic ordering of graph nodes such that for every directed edge from U to V, U comes before V in the execution order.
Implicit vs Explicit Dependency Declaration
resource "aws_iam_role" "cluster_role" {
name = "production-eks-cluster-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "eks.amazonaws.com" }
}]
})
}
resource "aws_iam_role_policy_attachment" "cluster_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = aws_iam_role.cluster_role.name
}
# Explicit dependency: EKS requires IAM policies to be actively attached
# before creation, even though the EKS resource only references role_arn.
resource "aws_eks_cluster" "main" {
name = "production-core-cluster"
role_arn = aws_iam_role.cluster_role.arn # Implicit dependency
vpc_config {
subnet_ids = [aws_subnet.public_a.id, aws_subnet.public_b.id]
}
# Explicit dependency forces cluster to wait for IAM policy attachment
depends_on = [
aws_iam_role_policy_attachment.cluster_policy
]
}
Beginner Traps & Gotchas
1. Overusing depends_on everywhere as a safety net
The Gotcha: Adding depends_on across all resources out of paranoia, collapsing the DAG into a single serial line and destroying parallelism.
The Fix: Only use depends_on when a genuine hidden dependency exists (such as IAM policy propagation or cloud-init completion). Rely on attribute references for normal dependencies.
2. Creating circular dependency loops
The Gotcha: Resource A referencing an output of Resource B while Resource B references an output of Resource A.
The Fix: Terraform will detect the cycle and refuse to plan. Break circular dependencies by decoupling shared attributes into local values or an intermediate data structure.