Resources, Dependencies, and Meta-Arguments¶
Overview¶
Declare managed resources, understand the dependency graph, and use count, for_each, lifecycle, and depends_on correctly.
A resource is something Terraform creates and updates through a provider. Addresses look like local_file.readme. Terraform builds a graph from references between resources; meta-arguments change how many instances exist and how replace/destroy behaves.
This is a core tutorial in Module 6 · Resources of the REBASH Academy Terraform for Cloud & DevOps Engineers series — written for Cloud, DevOps, Platform, and SRE engineers.
Prerequisites¶
Learning Objectives¶
By the end of this tutorial, you will be able to:
- Write a resource block and read its address in state
- Explain implicit vs explicit (
depends_on) dependencies - Choose
for_eachovercountfor named instances - Apply a
lifecyclerule intentionally
Architecture¶
This topic’s control points and relationships are shown below.
Theory¶
What it is¶
resource "local_file" "readme" {
filename = "${path.module}/README.txt"
content = "managed by Terraform\n"
}
Terraform tracks this as local_file.readme in state. When another resource references local_file.readme.filename, Terraform gains an implicit dependency and creates the file first. Use depends_on only when there is a real ordering need that references cannot express (for example, an API side effect with no attribute link).
Meta-arguments (available on most resources):
| Meta-argument | Purpose |
|---|---|
count | Create N copies; instances are resource.name[0] |
for_each | Create one instance per map/set key; resource.name["key"] |
depends_on | Explicit edges in the graph |
lifecycle | create_before_destroy, prevent_destroy, ignore_changes, … |
provider | Select a non-default provider alias |
Why it matters¶
Wrong dependencies cause apply-time races (subnet before VPC, DNS before load balancer). Prefer implicit links via attribute references — they document intent. Prefer for_each with stable keys (environment names, availability zones) so removing one item does not reshuffle indexes the way count often does. Lifecycle rules protect production databases from accidental destroy — and can also hide drift if you overuse ignore_changes.
How it works¶
- Terraform evaluates expressions and builds a directed graph of resources.
- Walk order respects dependencies; independent resources may create in parallel.
count/for_eachexpand one block into multiple instances in state.- On update, providers may update in place or force replace (destroy + create) depending on the attribute.
lifecycletweaks replace/destroy behaviour for that resource.
for_each = toset(["dev", "stage"]) expands to local_file.env["dev"] and local_file.env["stage"] — stable keys beat count indexes when membership changes.
Key concepts and comparisons¶
| Pattern | Prefer when |
|---|---|
| Implicit reference | Attribute of A is needed by B |
depends_on | Ordering without a referenceable attribute |
count | Simple N identical copies; indexes OK |
for_each | Named, stable instance keys |
create_before_destroy | Zero-downtime-style replace |
prevent_destroy | Block accidental destroy plans |
ignore_changes | Skip listed attributes (use sparingly) |
Common pitfalls¶
- Using
countwith a list that reorders — instance indexes shift and force replacements. - Sprinkling
depends_oneverywhere instead of wiring attributes. - Setting
ignore_changes = allto “fix” drift — you stop managing the resource. - Assuming
prevent_destroyblocksterraform destroyof the whole root without care — it blocks plans that would destroy that resource; design env teardown deliberately.
Hands-on Lab¶
Create a workspace for this tutorial.
Focus: Explore implicit dependencies, count, and depends_on
Step 1 – Create chained resources¶
cat > main.tf <<'EOF'
terraform {
required_providers {
local = { source = "hashicorp/local", version = "~> 2.5" }
null = { source = "hashicorp/null", version = "~> 3.2" }
}
}
resource "local_file" "base" {
filename = "${path.module}/base.txt"
content = "base
"
}
resource "local_file" "child" {
count = 2
filename = "${path.module}/child-${count.index}.txt"
content = "depends on ${local_file.base.filename}
"
}
resource "null_resource" "after" {
depends_on = [local_file.child]
triggers = { stamp = timestamp() }
}
EOF
terraform init
Step 2 – Apply and inspect graph order¶
terraform apply -auto-approve
terraform state list
ls -1 child-*.txt base.txt
terraform graph | head -n 30
Final step – Cleanup note¶
terraform destroy -auto-approve
# Workspace kept for notes; remove with: rm -rf "$(pwd)" when finished
Validation¶
- Lab commands run under
~/rebash-terraform/module-06/ - You can explain each Theory section in your own words
- You used modern tooling where it applies to this topic
- You can describe one production failure mode for this topic
Code Walkthrough¶
Production practice for Resources, Dependencies, and Meta-Arguments always combines:
- Inspect before you change (status, plan, logs, dry-run)
- Prefer reversible, documented changes (Git, IaC, drop-ins, version pins)
- Capture evidence (command output, pipeline logs) for handovers
- Prefer current tools and APIs over legacy shortcuts
- Least privilege — escalate credentials only when required
Keep runbooks short enough to follow under pressure. Automate checks; keep humans for judgement.
Security Considerations¶
- Treat credentials and tokens for terraform as privileged — never commit them
- Prefer short-lived auth (OIDC, roles, SSO) over long-lived keys
- Validate blast radius before apply/deploy/delete operations
- Restrict who can approve production changes
- Collect audit logs; limit who can read sensitive traces
Common Mistakes¶
Using count with a list that reorders — instance indexes shift and force replacements.
Validate assumptions against the Theory section and official docs before changing production.
Sprinkling depends_on everywhere instead of wiring attributes.
Lab shortcuts (open security groups, admin roles, skip approvals) must not ship unchanged.
Changing production without a rollback path
Always know how to revert (previous artefact, prior release, state rollback, DNS failback).
Best Practices¶
- Encode Resources, Dependencies, and Meta-Arguments changes as code and review them in pull requests
- Pin versions (images, modules, actions, provider plugins)
- Separate environments with clear promotion gates
- Alert on symptoms with runbooks attached
- Destroy lab resources; tag everything with owner and expiry where possible
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
| Auth / permission denied | Wrong identity, policy, or scope | Check caller identity, roles, and least-privilege policies |
| Timeout / no route | Network, DNS, security group, or endpoint | Trace path, DNS, and allow-lists before retrying |
| Drift / unexpected plan | Manual change or wrong state/workspace | Reconcile desired vs actual; avoid click-ops on managed resources |
| Pipeline/job red | Flaky step, cache, or missing secret | Read failing step logs; bisect recent workflow/config changes |
| Cost spike | Idle load balancer, NAT, oversized compute | Inventory billable resources; stop/delete labs promptly |
Summary¶
Resources, Dependencies, and Meta-Arguments is essential for Cloud and DevOps engineers working with terraform. Practise the lab until the inspection and change path is muscle memory, then continue the track.
Interview Questions¶
- How does Terraform infer dependencies between resources?
- When do you need explicit
depends_on? - What is the difference between
countandfor_each? - What operational risk does
countindex shifting introduce? - What does
lifecycle { prevent_destroy = true }protect against?
Sample answer — question 2
Implicit dependencies come from references. depends_on is for hidden ordering (for example, API readiness) that references cannot express.
Sample answer — question 4
Removing an element from a count list can force replacement of later indexes. for_each with stable keys usually produces safer updates.