Supplying Values and Precedence
Scenario: A production deployment quietly targets the wrong region because a stale TF_VAR_region environment variable, left over from an earlier debugging session, silently overrides the region checked into the committed defaults.
A variable's actual value at plan time can come from several different places at once, and Terraform has a fixed rule for which one "wins" when more than one source supplies a value: a variable's own default (lowest precedence), then any TF_VAR_<name> environment variable, then automatically-loaded .tfvars/.tfvars.json files (including terraform.tfvars and any *.auto.tfvars), then explicit -var-file arguments passed on the command line in the order given, then explicit -var flags on the command line (highest precedence). In practice, exact recall of this order matters most during incident response, when you're staring at unexpected behavior and need to figure out fast which of five possible sources actually won.
As a working convention: commit non-secret example values and genuinely environment-safe defaults to version control; never commit a .tfvars file containing a real secret. In automation, make the exact set of variable sources being used explicit in the run's own logs or metadata - "this run used environments/prod.tfvars plus these two -var overrides" - rather than leaving it implicit and hoping everyone remembers the precedence order correctly under pressure.
Analogy: Variable precedence is a stack of transparent sheets laid on top of one another, each one able to write over a field the sheet below it wrote. The value you actually see is whatever the topmost sheet that touches that field says - and if you forget a sheet even exists (like a leftover TF_VAR_ environment variable from an old terminal session), the value you see makes no sense until you remember to check for it.
A worked configuration
terraform plan \
-var-file=environments/prod.tfvars \
-var="release_id=2026-08-29"
# Environment form:
# TF_VAR_region=west
Standardize on exactly one variable-injection pattern per pipeline (usually a single -var-file per environment) so nobody has to reconstruct the precedence chain from memory during an incident. Print the resolved, non-sensitive effective values before planning so a reviewer can sanity-check them, and avoid ever setting personal TF_VAR_ shell variables for anything that touches production - a stray one, set months ago and forgotten, is exactly the kind of thing that silently redirects a deploy to the wrong region. A .auto.tfvars file left behind after a debugging session is especially dangerous because it loads automatically*, with no explicit flag needed, on every subsequent command in that directory.
Common mistakes in production
- Setting a personal
TF_VAR_*environment variable while debugging and forgetting to unset it - it silently outranks the checked-in default on every later command in that shell. - Leaving a
*.auto.tfvarsfile in a directory after using it for a one-off test - it loads automatically on every future run in that directory, with no explicit flag as a reminder.
Goal: Treat the plan as a change contract: understand every create, update, replacement, and destroy before approving it. Reinforce this lesson in the tf-tfvars-precedence Terraform lab. Open/labs/terraformand choose slugtf-tfvars-precedence; the lab runs real Terraform against the offline FakeCloud provider.