
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
Terraform depends_on is the explicit mechanism for declaring resource ordering when implicit attribute references cannot capture a real-world dependency. Most engineers overuse it, creating brittle graphs that fail during updates or destroy operations because they misunderstand how Terraform resolves state. Understanding Terraform depends_on: When and Why requires distinguishing between data dependencies (which Terraform handles automatically) and side-effect dependencies (which require manual intervention). This guide covers the precise scenarios where explicit dependencies are mandatory versus harmful.
How does Terraform depends_on differ from implicit references?
Terraform builds a directed acyclic graph (DAG) of all resources before executing any API calls. When you reference an attribute like aws_db_instance.main.endpoint inside another resource, Terraform infers two things simultaneously: the target must be created first, and its output value is required for configuration. This is an implicit dependency. It is resilient because if you later remove the reference, the ordering constraint vanishes with it.
Explicit depends_on creates an edge in the DAG without transferring data. You are telling Terraform "create this after that" purely for temporal reasons. The danger arises when engineers use depends_on as a substitute for proper attribute references. If Resource B actually needs Resource A's ID but you only declare depends_on, Terraform will create A first, then B, but B receives no value. During updates, if A changes in a way that doesn't trigger recreation but alters runtime behavior, B won't know to update. This decoupling of configuration from ordering is the primary source of drift in mature infrastructure as code with Terraform projects.
In practice, implicit references also allow greater parallelism. Terraform can evaluate independent branches of the graph concurrently. An unnecessary depends_on serializes operations that could otherwise run in parallel, increasing apply times significantly in large state files. Always prefer referencing an actual attribute, even if you have to use a dummy output or a null_data_source to make the dependency visible to the graph.
When should you use Terraform depends_on for side effects?
The legitimate use case for explicit dependencies involves resources where the API returns "created" before the service is actually usable. Cloud providers are eventually consistent. A database instance may report ACTIVE status while still initializing internal schemas, rejecting connections for 30–90 seconds afterward. A Kubernetes namespace may exist in etcd before the admission controller is ready to accept pods. These are side-effect dependencies: the state says "done," but reality says "wait."
Handling eventual consistency in cloud APIs
Consider deploying a Helm chart into an EKS cluster immediately after creating the cluster. The aws_eks_cluster resource completes when the control plane is provisioned, but the OIDC provider and API server endpoint may take additional time to stabilize. Without explicit ordering, your Helm release fails with authentication errors.
resource "aws_eks_cluster" "main" {
name = var.cluster_name
role_arn = aws_iam_role.eks.arn
vpc_config {
subnet_ids = var.subnet_ids
}
}
# Wait for cluster endpoint to be fully operational
resource "time_sleep" "wait_for_eks" {
depends_on = [aws_eks_cluster.main]
create_duration = "60s"
}
resource "helm_release" "app" {
name = "my-app"
repository = "https://charts.example.com"
chart = "my-app"
namespace = "default"
# Explicit dependency on the sleep, not directly on the cluster
depends_on = [time_sleep.wait_for_eks]
} Note the pattern here: we depend on a time_sleep resource, not directly on the EKS cluster. This isolates the workaround. If AWS fixes their API responsiveness, you delete the sleep block and the Helm release naturally reverts to depending only on what it needs. Direct depends_on on the cluster would permanently couple these resources even after the underlying issue is resolved.
Managing external system integrations
Another valid scenario involves resources outside Terraform's management. If you provision an S3 bucket and then need to configure a third-party CDN that reads from that bucket via a separate API call wrapped in a null_resource provisioner, Terraform has no visibility into the CDN's readiness. The null_resource must explicitly depend on the bucket, and subsequent resources consuming the CDN must depend on the null_resource. This chain makes the invisible visible. For teams managing complex hybrid environments, understanding these boundaries is critical, much like distinguishing between Terraform vs Ansible responsibilities.
Why do modules require special depends_on handling?
Modules introduce a boundary that complicates dependency resolution. Inside a module, you can reference resources normally. But when calling a module, you cannot pass individual resource attributes as dependencies unless the module exposes them as outputs. This leads to a common anti-pattern: adding depends_on at the module call site because the module doesn't export what you need.
The correct approach is to design modules with explicit outputs that represent completion signals. If a networking module creates VPCs, subnets, NAT gateways, and route tables, expose a vpc_id or a dedicated network_ready output. Consuming modules reference this output, creating an implicit dependency on the entire network stack. This is superior to depends_on = [module.networking] because it documents what is being waited for, not just that something is being waited for.
There is one exception: when a module performs side effects that aren't captured by any output. For example, a module that creates an IAM role and attaches policies might complete before policy propagation finishes across AWS regions. In this case, the module should internally use time_sleep and expose a role_arn output that implicitly depends on the sleep. Callers then get correct ordering without knowing about the workaround. This encapsulation is key to maintainable Terraform modules for reusable infrastructure.
What are the risks of misusing explicit dependencies?
Overusing depends_on creates three categories of problems that compound over time. First, destroy-order failures. Terraform destroys resources in reverse dependency order. If you've created artificial chains, destruction may attempt to delete a resource while something still logically depends on it, even though Terraform's graph says otherwise. This is especially painful with databases and storage buckets that refuse deletion while connections exist.
Second, reduced parallelism. Each explicit edge serializes operations. In a state file with 500 resources, unnecessary dependencies can double or triple apply times. Teams often blame Terraform performance when the real issue is self-inflicted graph constraints. Profile your applies with terraform plan -parallelism=10 and examine the graph visualization (terraform graph | dot -Tpng > graph.png) to identify bottlenecks.
Third, maintenance burden. Explicit dependencies are opaque. Six months later, no one remembers why depends_on = [aws_lambda_function.processor] exists. Was it a timing issue? A data dependency someone forgot to wire? A leftover from debugging? Comments help, but comments rot. Implicit references are self-documenting: the attribute name explains the relationship. When refactoring, implicit dependencies move with the code; explicit ones become dangling references that cause confusing errors.
| Scenario | Use Implicit Reference | Use Explicit depends_on | Risk Level |
|---|---|---|---|
| Resource needs another's ID/ARN/endpoint | ✅ Yes | ❌ No | Low |
| API returns ready before service accepts requests | ❌ No | ✅ Yes (via time_sleep) | Medium |
| Module completion signal needed | ✅ Via output | ⚠️ Only if no output exists | Medium |
| External system configured via provisioner | ❌ No | ✅ Yes | High |
| "Just to be safe" ordering | ❌ No | ❌ No | Critical |
How do you debug dependency issues in Terraform plans?
When applies fail due to ordering, resist the urge to add depends_on immediately. First, generate the dependency graph: terraform graph -type=plan | dot -Tsvg > plan.svg. Visual inspection reveals whether the missing edge is truly absent or whether the failure stems from something else entirely, like insufficient IAM permissions masquerading as a timing issue.
Use terraform console to test attribute availability. If aws_db_instance.main.endpoint returns a value in the console but fails during apply, you're dealing with eventual consistency. If it returns null or errors, you have a genuine missing dependency. Check provider documentation for known async behaviors; many AWS and Azure resources have documented stabilization periods that should inform your time_sleep durations.
For module boundaries, run terraform plan -out=tfplan && terraform show -json tfplan | jq '.resource_changes[] | select(.module_address != null)' to inspect which module resources are changing and in what order. This JSON output is more reliable than visual graph parsing for large states. Remember that depends_on inside modules affects only resources within that module; cross-module dependencies must flow through inputs and outputs. Treat every explicit dependency as technical debt requiring a comment explaining why implicit references failed. Future maintainers, including yourself during incident response at 2 AM, will thank you.
Applying Terraform depends_on Safely in Production
Treat Terraform depends_on: When and Why as a question with a default answer of "don't." Reserve explicit dependencies for verified side-effect scenarios, isolate them behind time_sleep or dedicated null resources, and always document the rationale. Prefer implicit attribute references for data flow, design modules with completion-signaling outputs, and validate ordering through graph visualization before committing workarounds. Your infrastructure will be faster to apply, safer to destroy, and easier to reason about during outages. If your team struggles with dependency management or needs an audit of existing Terraform codebases, reach out for infrastructure consulting to establish patterns that scale.