Introduction
Terraform builds a dependency graph to determine the correct order for creating, updating, and destroying resources. It infers these dependencies automatically when one resource's argument references another resource.
The depends_on meta-argument allows you to create explicit dependencies between resources. However, you should only use depends_on as a last resort when Terraform cannot infer a dependency automatically. This typically occurs when a resource relies on another resource's behavior but does not access any of that resource's data in its arguments. Overusing depends_on, especially with modules, can create complex and difficult-to-manage configurations.
This article provides best practices for managing dependencies between Terraform modules, emphasizing direct references over explicit depends_on arguments.
Key Terminology
-
Terraform Module: Any directory that contains one or more Terraform configuration files (e.g.,
main.tf). -
Top-level Configuration: The root module or directory where you execute
terraform initandterraform apply. - Variables: Inputs for a module. To use a value from one module in another, you must define it as an output in the source module and pass it as an input variable to the destination module.
-
depends_on: A meta-argument available on all resource and module blocks that accepts a list of references to other resources or modules.
Recommendation
It is a best practice to pass output values from one module as input variables to another. This allows Terraform to infer the dependency correctly without needing an explicit depends_on argument.
Example File Structure
The following examples use this file structure.
.
├── alpha
│ └── main_a.tf
├── main.tf
└── zeta
└── main_z.tfRecommended Approach: Direct Variable Reference
You should directly refer to an output variable from the source module (alpha) and pass it as an input variable to the destination module (zeta). This method creates a clear and implicit dependency that Terraform can track automatically.
main.tf
module "alpha" {
source = "./alpha"
}
module "zeta" {
source = "./zeta"
some_input_var = module.alpha.alpha_instance_public_ip
}alpha/main_a.tf
resource "aws_instance" "dev" {
ami = "ami-abc123"
instance_type = "t2.micro"
associate_public_ip_address = true
tags = {
Name = "alpha-dev"
}
}
output "alpha_instance_public_ip" {
value = aws_instance.dev.public_ip
}zeta/main_z.tf
variable "some_input_var" {
type = string
default = "1.1.1.1"
}
resource "some_other_resource" "r" {
some_argument = var.some_input_var
}Not Recommended: Indirect Reference with depends_on
Avoid using depends_on to link modules. This approach creates a dependency on the entire module, not just the specific resource or value you need. It also often requires using data sources to read back information that could have been passed directly as an output, adding unnecessary complexity.
main.tf
# This module is the same as main_a.tf above
module "alpha" {
source = "./alpha"
}
# This module reads data from a data source that depends on module "alpha",
# so it uses depends_on. This is not recommended.
module "zeta" {
source = "./zeta"
depends_on = [module.alpha]
}alpha/main_a.tf
# ...
resource "aws_instance" "dev" {
ami = "ami-abc123"
instance_type = "t2.micro"
associate_public_ip_address = true
tags = {
Name = "alpha-dev"
}
}
output "alpha_instance_public_ip" {
value = aws_instance.dev.public_ip
}
# ...zeta/main_z.tf
# This module depends on module "alpha" for data.
data "aws_eip" "alpha-dev-ip" {
tags = {
Name = "alpha-dev"
}
}
resource "some_other_resource" "other_r" {
some_argument = data.aws_eip.alpha-dev-ip.public_ip # Example attribute
}
# ...This indirect pattern makes the dependency graph less precise and harder to understand. When you make module zeta depend on module alpha, all resources in zeta now depend on all resources in alpha, which can slow down planning and apply operations.
Additional Information
For more details on when an explicit dependency might be necessary, refer to the official Terraform depends_on documentation.