How to Use the Terraform `depends_on` Meta-Argument with Modules
The depends_on meta-argument lets you explicitly declare ordering dependencies between module calls, ensuring a dependent module is fully evaluated only after all referenced resources, data sources, or other modules have been successfully created or refreshed.
The terraform depends_on meta-argument with modules provides explicit control over execution order when implicit dependencies are insufficient. According to the hashicorp/terraform source code, this meta-argument is parsed during configuration loading, validated for whole-object references, and enforced during graph construction to guarantee specific module sequencing.
How Module Dependencies Work Internally
Terraform implements module-level dependencies through a coordinated pipeline across several internal packages. Understanding these implementation details clarifies why certain constraints exist when declaring dependencies.
Configuration Parsing in module_call.go
When Terraform loads your configuration, internal/configs/module_call.go scans each module block for a top-level depends_on attribute. The parser stores these references as static dependencies associated with the module call itself, protecting them from overrides by child modules.
Reference Validation in depends_on.go
The file internal/configs/depends_on.go enforces a critical constraint: all references must point to whole objects, not specific attributes. This means you can reference module.network but not module.network.vpc_id. The validator rejects any address that drills into a resource or module output, ensuring dependencies remain at the object level.
Graph Construction in graph_builder_plan.go
During the planning phase, internal/terraform/graph_builder_plan.go translates stored dependencies into directed edges within the evaluation graph. These edges force the dependent module to wait until all referenced objects complete their create or refresh operations, strictly controlling the order of operations during both terraform plan and terraform apply.
Syntax Rules and Constraints
Using depends_on with modules requires strict adherence to reference syntax and architectural limitations.
Whole-Object References Only
You must reference the complete object address using module.<NAME> syntax. Terraform rejects dotted paths into module internals, such as module.foo.aws_instance.bar, because the dependency system tracks object lifecycle states, not attribute values.
Legacy Module Incompatibility
Modules that define their own provider configurations within the module block are incompatible with depends_on. The validation logic in internal/configs/provider_validation.go detects this pattern and emits an error, as legacy provider inheritance conflicts with explicit dependency ordering.
No Impact on Variables or Outputs
The meta-argument influences evaluation timing but does not affect data flow. Input variables passed to a module resolve during the configuration phase, and outputs become available immediately after the module completes, regardless of depends_on declarations elsewhere.
Practical Implementation Examples
Basic Module-to-Module Dependency
Declare a simple dependency when one module requires infrastructure from another to exist first:
module "network" {
source = "./modules/network"
}
module "compute" {
source = "./modules/compute"
# Ensure compute waits for network VPC creation
depends_on = [module.network]
}
In this configuration, Terraform guarantees that all resources inside the network module reach a successful state before creating any resources within the compute module.
Multiple Parallel Dependencies
Supply a list to depend on several modules simultaneously:
module "database" {
source = "./modules/rds"
}
module "cache" {
source = "./modules/elasticache"
}
module "api" {
source = "./modules/api_gateway"
depends_on = [
module.database,
module.cache,
]
}
The api module evaluates only after both the database and cache modules complete their provisioning cycles.
Mixed Resource and Module Dependencies
Combine resource and module references in a single dependency list:
resource "aws_security_group" "bastion" {
# configuration...
}
module "network" {
source = "./modules/vpc"
}
module "dmz" {
source = "./modules/public_subnet"
depends_on = [
aws_security_group.bastion,
module.network,
]
}
Each element in the list must be a complete addressable object—either a managed resource, data source, or module call.
Summary
- Explicit ordering: The
depends_onmeta-argument in Terraform modules guarantees that dependent modules evaluate only after referenced objects complete successfully. - Whole-object restriction: References must target entire modules (
module.name) or resources, never specific attributes or outputs. - Implementation path: Parsed in
internal/configs/module_call.go, validated ininternal/configs/depends_on.go, and enforced ininternal/terraform/graph_builder_plan.go. - Legacy exclusion: Modules with inline provider configurations cannot use
depends_ondue to architectural conflicts detected ininternal/configs/provider_validation.go. - Timing only: The argument controls execution order, not variable resolution or output availability.
Frequently Asked Questions
Can I reference a specific resource inside a module using depends_on?
No. Terraform requires whole-object references for dependencies. You cannot write depends_on = [module.vpc.aws_subnet.private] because the validation logic in internal/configs/depends_on.go accepts only complete object addresses like module.vpc. This design ensures dependencies track lifecycle states rather than individual attribute values.
Does depends_on affect when module outputs become available?
No. Module outputs populate immediately after the module's resources finish creating, regardless of depends_on relationships. The meta-argument controls the planning and apply order between modules but does not influence the timing of output resolution or input variable processing within the dependency graph.
Why does Terraform return an error when I use depends_on with certain modules?
Terraform rejects depends_on for modules that contain legacy provider configurations defined within the module block. The validator in internal/configs/provider_validation.go flags these modules as incompatible because explicit dependencies conflict with legacy provider inheritance patterns. Refactor such modules to remove internal provider blocks or use provider passing instead.
How is explicit depends_on different from implicit dependencies?
Implicit dependencies form automatically when a module uses output values from another module as input variables. Explicit depends_on creates ordering constraints without data exchange, useful when a module requires another to exist first but does not consume its outputs. According to the graph builder in internal/terraform/graph_builder_plan.go, explicit edges from depends_on take precedence in ordering calculations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s https://instagit.com/install.md