Resource Dependencies: Implicit, Explicit and the Dependency Graph
Scenario
You need to create an S3 bucket and an SQS queue, where the queue's name includes the bucket name. Terraform must create the bucket first because the queue depends on its name. You also need to add an external dependency where there is no attribute reference — for example, ensuring a notification configuration only runs after the queue is ready.
This lab teaches you how Terraform automatically detects dependencies from attribute references (implicit) and how to force ordering with depends_on (explicit) when there is no data flow between resources.
- Create an S3 bucket.
- Create an SQS queue whose name references the bucket name — this creates an implicit dependency.
- Create a second SQS queue with depends_on pointing to the first queue — this creates an explicit dependency.
- Run terraform graph to visualize the dependency graph.
- Observe the creation order in terraform apply output.
Additional Context
Terraform builds a directed acyclic graph (DAG) of all resources. It uses this graph to determine the optimal creation order — resources with no dependencies are created in parallel; resources that depend on others are created sequentially.
Implicit dependencies happen automatically when you reference another resource's attribute (e.g., aws_s3_bucket.app.id). Terraform sees the reference and knows to create the bucket first.
Explicit dependencies use depends_on for cases where there's a runtime dependency but no attribute reference — for example, an IAM policy must exist before a Lambda can use it, but nothing in the Lambda config directly references the policy ARN.
main.tf
# main.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
}
}
provider "aws" { region = "us-east-1" }
# ─── Resource 1: S3 bucket (no dependencies — created first) ───
resource "aws_s3_bucket" "data" {
bucket = "dependencies-demo-data"
tags = { ManagedBy = "terraform" }
}
# ─── Resource 2: SQS queue — IMPLICIT dependency on the bucket ───
# The name references aws_s3_bucket.data.id → Terraform knows to create the bucket first.
resource "aws_sqs_queue" "notifications" {
name = "${aws_s3_bucket.data.id}-notifications"
tags = { ManagedBy = "terraform" }
}
# ─── Resource 3: SQS dead-letter queue — EXPLICIT dependency ───
# There's no attribute reference here, but we need this queue
# to exist only AFTER the notifications queue is ready.
resource "aws_sqs_queue" "dead_letter" {
name = "notifications-dlq"
tags = { ManagedBy = "terraform" }
# depends_on forces Terraform to create notifications queue first,
# even though there's no attribute reference linking them.
depends_on = [aws_sqs_queue.notifications]
}outputs.tf
output "bucket_name" { value = aws_s3_bucket.data.id }
output "queue_name" { value = aws_sqs_queue.notifications.name }
output "dlq_name" { value = aws_sqs_queue.dead_letter.name }Workflow Commands
terraform init && terraform apply
# Visualize the dependency graph (outputs DOT format)
terraform graph
# Render to an image (requires Graphviz installed: brew install graphviz)
terraform graph | dot -Tpng -o graph.png
# Or view as text — look for the edges (arrows) showing dependencies
terraform graph | grep "->"
terraform destroy# terraform apply output shows: aws_s3_bucket.data: Creating... # Step 1 — no dependencies aws_s3_bucket.data: Creation complete aws_sqs_queue.notifications: Creating... # Step 2 — waits for bucket (implicit) aws_sqs_queue.notifications: Creation complete aws_sqs_queue.dead_letter: Creating... # Step 3 — waits for notifications (explicit) aws_sqs_queue.dead_letter: Creation complete Apply complete! Resources: 3 added, 0 changed, 0 destroyed. # terraform graph | grep "->" shows: # aws_sqs_queue.notifications -> aws_s3_bucket.data # aws_sqs_queue.dead_letter -> aws_sqs_queue.notifications

