What Is Depends On in Terraform?
depends_on is a meta-argument you add inside a resource or module block to tell Terraform "create this only after that other thing is fully created" — even when nothing in the configuration's actual arguments points to that other thing.
Here is the part most tutorials skip: Terraform almost never needs you to specify order manually. If resource_b uses resource_a.id anywhere inside its arguments, Terraform automatically knows resource_a must exist first and builds that into its dependency graph behind the scenes. depends_on exists only for the rare cases where a dependency is real but invisible — nothing in the code references the other resource directly, yet the order genuinely matters.
At Hotstar, an engineer once created an EC2 instance with an IAM instance profile attached, right after creating the IAM role and policy in the same apply. The apply succeeded — Terraform created the role, then the instance. But the instance failed to read from S3 for the first 90 seconds, because AWS IAM permissions take a few seconds to propagate globally after creation, even though Terraform considers the role "done." Terraform had finished — AWS had not. depends_on cannot fix propagation delay by itself, but the same lesson — that "Terraform finished" and "AWS is ready" are not always the same moment — is exactly the class of invisible-dependency problem depends_on is meant for.
Implicit Dependency — What Terraform Figures Out on Its Own
Whenever you reference one resource's attribute inside another resource's arguments, Terraform builds the dependency automatically. No depends_on needed:
resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16"} resource "aws_subnet" "private" { vpc_id = aws_vpc.main.id # <-- this reference IS the dependency cidr_block = "10.0.1.0/24"}# Terraform automatically creates aws_vpc.main BEFORE aws_subnet.private# because the subnet's vpc_id reads an attribute from the VPC.Explicit Dependency — When You Need depends_on
Sometimes two resources have a real ordering requirement but no argument actually references the other resource. This is when you reach for depends_on:
resource "aws_iam_role_policy" "lambda_s3_access" { name = "lambda-s3-access" role = aws_iam_role.lambda.id policy = data.aws_iam_policy_document.s3_read.json} resource "aws_lambda_function" "processor" { function_name = "order-processor" role = aws_iam_role.lambda.arn # implicit dependency on the ROLE exists already # but the lambda also needs the POLICY attached before it can run successfully, # and nothing in this block actually references the policy resource directly depends_on = [aws_iam_role_policy.lambda_s3_access] # explicit dependency added here}Without the depends_on line, Terraform might create the role and the Lambda function in parallel — and the Lambda could attempt its first invocation before the policy is attached, even though the role itself already exists.
Depends On with Modules
depends_on works on entire module blocks too — useful when a module's internal resources have no direct reference to another module's resources, but still need to wait for them:
module "vpc" { source = "./modules/vpc"} module "monitoring" { source = "./modules/monitoring" # the monitoring module's resources don't reference the VPC module's outputs directly, # but CloudWatch alarms inside it are useless until the VPC's flow logs exist depends_on = [module.vpc]}The Performance Cost of Depends On
INFORMATIONTip from a senior engineer: `depends_on` is not free. Terraform normally creates independent resources in parallel — speeding up applies significantly on large configs. Every `depends_on` you add forces those resources into a strict sequential order, even resources that have nothing to do with each other. Adding it "just to be safe" on ten resources can turn a two-minute apply into a fifteen-minute apply.
Common Over-Use of Depends On
COMMON MISTAKE / WARNINGCommon mistake: reaching for `depends_on` to fix a plan error before checking whether a normal reference would solve it instead. Nine times out of ten, the real fix is referencing an attribute — like `aws_iam_role.lambda.arn` — which gives Terraform both the correct value AND the correct ordering for free. Only use `depends_on` when there is truly no attribute to reference.
# WRONG — using depends_on when a reference would do the job betterresource "aws_instance" "app" { depends_on = [aws_security_group.app] # works, but tells Terraform nothing about WHY} # RIGHT — referencing the security group's ID does the same ordering job# AND actually attaches the security group, which depends_on alone does not doresource "aws_instance" "app" { vpc_security_group_ids = [aws_security_group.app.id] # implicit dependency + correct config}Quick Reference
| Type | How Terraform Learns It | Example |
|---|---|---|
| Implicit dependency | Automatically, from an attribute reference | vpc_id = aws_vpc.main.id |
| Explicit dependency | You declare it manually | depends_on = [aws_iam_role_policy.x] |
| Error / Symptom | Root Cause | Fix |
|---|---|---|
| Resource fails intermittently right after creation | IAM/network propagation delay, not a true ordering bug | Add depends_on, but also consider a short retry/wait in app code — depends_on does not add a delay, only an order |
| Apply takes far longer than expected | Excessive depends_on forcing sequential creation |
Remove depends_on where a real attribute reference can replace it |
Cycle: resource depends on itself |
Two resources depends_on each other, directly or indirectly |
Break the cycle — one of the two dependencies is usually unnecessary |
Resources created in unexpected order despite depends_on |
depends_on was added to the wrong block (e.g., the dependent instead of the dependency) |
Double-check which resource needs to wait for which |