4 — Dependencies & the Execution Graph¶
Code: 01-basics/3-resource-attribute-reference/ · 01-basics/4-dependencies/ · 02-gcp-terraform/z-core-gcp-resources/network.tf
Architect's Points to Keep in Mind¶
1. HCL is a Directed Acyclic Graph (DAG), Not Top-to-Bottom Code¶
- Terraform concatenates every
.tffile in a directory and constructs a dependency graph from resource references. - File names and block order have zero effect on execution order. Independent resources are provisioned in parallel (default parallelism = 10); dependent resources run sequentially.
- Destroy order is the exact reverse of create order. If
subnetreferencesvpc, Terraform createsvpc→subnetand destroyssubnet→vpc.
2. Implicit Dependencies vs. depends_on (Explicit)¶
- Implicit dependency: Referencing one resource's attribute inside another (e.g.,
network = google_compute_network.vpc.idormember = "serviceAccount:${google_service_account.gsa.email}") is the dependency declaration. There is no flag to enable it.- Note: In
01-basics/,3-resource-attribute-reference/main.tfand4-dependencies/implicit/main.tfare byte-identical on purpose — an attribute reference already creates the graph edge.
- Note: In
- Explicit dependency (
depends_on = [...]): If Resource B already interpolates an attribute of Resource A, addingdepends_on = [A]is 100% redundant (producing the exact sameterraform graph). - When
depends_onis genuinely required (real-world runtime dependencies invisible to HCL):- IAM Propagation: A workload (
helm_release,google_compute_instance, or GKE cluster) needs an IAM binding (google_project_iam_member,google_service_account_iam_member) to exist before it starts, even though it never interpolates an attribute of the IAM binding block. - API Enablement: A resource requires
google_project_service.apito finish enabling an API before calling it. - Side-effect / Bridge Ordering (The
shell_script/helm_releaseRace Condition): Inbackend/infra,helm_release.acme_sampleapp_backenddoes not interpolate any attribute fromgoogle_project_iam_member.backend_sql_access,google_service_account_iam_member.workload_identity, orshell_script.backend_sa_sql_access(which invokes a Cloud Function to grant in-database Postgres roles). Without explicitdepends_onon all three, Terraform deploys the Helm chart in parallel with the IAM/DB grants, and the pod immediately entersCrashLoopBackOffon startup because its Service Account cannot yet authenticate or read its schema.
- IAM Propagation: A workload (
- Caution with
depends_onon Modules or Data Sources: Addingdepends_onto adatablock forces Terraform to defer reading the data source until theapplyphase whenever any depended-on resource has planned changes, turning downstream attributes into(known after apply).
3. Plan-Time Unknowns ((known after apply))¶
- Computed attributes (like
id,self_link, oremail) do not exist until the cloud API creates the resource. Duringplan, Terraform marks them(known after apply). -
The DAG Cardinal Rule:
countandfor_eachkeys/lengths cannot depend on values that are unknown at plan time.- Why? Terraform Core must build the full graph of resource instances before executing
apply. -
If
for_eachdepends on a computed attribute of a not-yet-created resource,planfails with:The "for_each" value depends on resource attributes that cannot be determined until apply. -
Architectural Fix: Always drive
for_eachkeys andcountlengths from static inputs (var.*orlocal.*derived from variables), never from computed resource attributes. (If unavoidable in an emergency,-targetthe upstream resource first, then refactor the keys.)
- Why? Terraform Core must build the full graph of resource instances before executing
Code Reference¶
resource "random_pet" "my_pet" {
prefix = var.prefix
separator = var.separator
length = var.name_length
}
resource "local_file" "pet" {
filename = var.fileName
# Implicit dependency: pet.id is (known after apply) until random_pet is created
content = "Here is the Random Pet ${random_pet.my_pet.id}"
# Redundant here because `content` already references `random_pet.my_pet.id`
depends_on = [
random_pet.my_pet
]
}
# 1. Implicit dependency: VPC -> Subnet (via .id) & SA -> IAM Member (via .email)
resource "google_compute_network" "vpc" {
name = "fde-training-vpc"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "subnet" {
name = "fde-training-subnet"
ip_cidr_range = "10.0.1.0/24"
region = "us-central1"
network = google_compute_network.vpc.id # Implicit edge: VPC created first
}
resource "google_service_account" "gsa" {
account_id = "${local.name_prefix}-web-app"
display_name = "My Test App"
}
resource "google_project_iam_member" "sa_sql_client" {
project = var.project_id
role = "roles/cloudsql.client"
member = "serviceAccount:${google_service_account.gsa.email}" # Implicit edge: SA created first
}
# 2. Legitimate explicit `depends_on`: API enablement & IAM before workload
resource "google_project_service" "secretmanager" {
service = "secretmanager.googleapis.com"
disable_on_destroy = false
}
resource "google_secret_manager_secret" "sso" {
secret_id = "sso-config"
replication { auto {} }
depends_on = [google_project_service.secretmanager] # Hidden runtime edge
}