Study guide
7.1 Input Variables, tfvars, and Precedence
HashiCorp Certified: Terraform Associate 004
Track your progress as you read
Finished sections get a check mark.
1 Introduction & Exam Overview
1.1 Current Exam Facts 1.2 Format, Delivery, Retakes, and Recertification 1.3 004 Changes, Terraform 1.12, and Study Plan
2 Infrastructure as Code with Terraform
2.1 What Infrastructure as Code Is 2.2 Advantages of IaC Patterns 2.3 Multi-Cloud, Hybrid, and Service-Agnostic Workflows
3 Terraform Fundamentals: Providers & State
3.1 Install and Version Terraform Providers 3.2 How Terraform Uses Providers 3.3 Configuration with Multiple Providers 3.4 How Terraform Uses and Manages State
4 Core Terraform Workflow: Init & Validate
4.1 The Terraform Write–Plan–Apply Workflow 4.2 Initialize a Working Directory 4.3 Validate a Configuration
5 Plan, Apply, Destroy, and Formatting
5.1 Generate and Review an Execution Plan 5.2 Apply Changes to Infrastructure 5.3 Destroy Terraform-Managed Infrastructure 5.4 Formatting and Style Adjustments
6 Resources, Data Sources, and References
6.1 Resource Blocks vs Data Blocks 6.2 Resource Attributes and Cross-Resource References 6.3 Resource Addressing and Interpolation Patterns
7 Variables, Outputs, and Complex Types
7.1 Input Variables, tfvars, and Precedence 7.2 Output Values 7.3 Complex Types: list, map, object, tuple, set
8 Dynamic Configuration and Dependencies
8.1 Expressions, count, for_each, and dynamic Blocks 8.2 Built-in Functions 8.3 Dependencies, depends_on, and Lifecycle Rules
9 Conditions, Secrets, and Sensitive Data
9.1 Custom Conditions: validation, precondition, postcondition, check 9.2 Sensitive Data Handling 9.3 Vault, Ephemeral Values, and Write-Only Arguments
10 Terraform Modules
10.1 How Terraform Sources Modules 10.2 Variable Scope within Modules 10.3 Using Modules in Configuration 10.4 Managing Module Versions
11 Terraform State Management
11.1 The Local Backend 11.2 State Locking 11.3 Remote State and the backend Block 11.4 Resource Drift and State Operations
12 Maintain Infrastructure with Terraform
12.1 Import Existing Infrastructure 12.2 Inspect State with the CLI 12.3 Verbose Logging and Diagnostics
13 HCP Terraform
13.1 Create Infrastructure with HCP Terraform 13.2 Collaboration and Governance Features 13.3 Workspaces and Projects 13.4 HCP Terraform Integrations
7.1 Input Variables, tfvars, and Precedence
Key Takeaways Explain This Topic AI
Declare inputs with a variable block and read them as var.name; type, default, description, sensitive, nullable, validation, and ephemeral are the Terraform 1.12 arguments
Official assignment precedence, highest first: -var and -var-file (in the order given) plus HCP Terraform variables, then *.auto.tfvars lexical, terraform.tfvars.json, terraform.tfvars, TF_VAR_ environment variables, then the variable default
Do not commit secrets in .tfvars files; sensitive only redacts CLI output and still writes the value into state unless you mark the variable ephemeral
A backend block cannot refer to input variables, locals, or data sources — backend settings are not ordinary HCL expressions
A variable with no default is required; Terraform prompts for it before plan unless some other source supplies a value
Last updated: August 2026
7.1 Input Variables, tfvars, and Precedence
Quick Answer: Write variable "NAME" and read it as var.NAME . Root values come from -var / -var-file and HCP Terraform (highest), then *.auto.tfvars in lexical order, terraform.tfvars.json , terraform.tfvars , TF_VAR_ environment variables, and finally default . Do not put secrets in committed tfvars. sensitive hides CLI text but still stores the value in state. A backend block cannot use variables.
Objective 4c on Terraform Associate (004) is: use variables and outputs. This section is the input half. The product version on the exam is Terraform 1.12 . HashiCorp's language pages to quote are Use input variables and the variable block reference . Full custom-condition design lives in chapter 9; here you only need the validation block shape.
Why a variable block exists
Hardcoded instance_type = "t2.micro" makes a module a one-off script. A variable block is the module's argument list: consumers pass values at run time without editing the module source. In the root module those values arrive from the CLI, files, environment, or HCP Terraform. In a child module they arrive only as arguments on the module block. Root terraform.tfvars does not automatically leak into a child — the parent must pass instance_type = var.instance_type . variable "instance_type" {
- type = string
- description = "EC2 instance type for the web server"
- default = "t2.micro"
- }
- variable "subnet_id" {
- type = string
- description = "Subnet ID where the web server will be deployed"
- }
- variable "environment" {
- type = string
- description = "Deployment environment name"
- default = "dev"
- validation {
- condition = contains(["dev", "staging", "prod"], var.environment)
- error_message = "Environment must be dev, staging, or prod."
- }
- }
- resource "aws_instance" "web" {
- ami = data.aws_ami.ubuntu.id
- instance_type = var.instance_type
- subnet_id = var.subnet_id
- tags = {
- Environment = var.environment
- Name = "${var.environment}-web-server"
- }
- }
Reference syntax is always var.<NAME> . There is no variable.instance_type and no variables.instance_type . The label after variable must be unique in the module. Reserved names you cannot use: source , version , providers , count , for_each , lifecycle , depends_on , and locals .
subnet_id has no default , so it is required . Terraform prompts for it before it generates a plan unless another assignment source supplies a value. instance_type and environment are optional because they have defaults.
Arguments on the Terraform 1.12 variable block
Argument Required? Default What 004 expects you to know
type No any Constrains the assigned value. Omit it and the variable accepts any type. Prefer an exact constraint.
default No none Makes the input optional. Must be a literal ; it cannot reference other objects. Must convert to type .
description No none Written for the module consumer, not as an author comment.
validation No none Nested block: condition must be true ; error_message is required. Evaluated while Terraform creates a plan. Chapter 9 covers richer conditions.
sensitive No false Redacts the value (and anything derived from it) in plan and apply logs. Still written to state.
nullable No true false forbids null . When nullable = true and a default exists, an explicit null overrides the default. Nested null inside a list or object is still allowed if the collection itself is not null.
ephemeral No false Terraform 1.10+. Available at run time but omitted from state and plan files . Only usable in other ephemeral contexts (ephemeral outputs, ephemeral variables, write-only arguments, ephemeral resources, provider config, provisioners). Any expression that reads an ephemeral variable becomes ephemeral.
sensitive is not encryption. Anyone who can read terraform.tfstate can read the password. For a short-lived token you do not want in state, mark the variable ephemeral and keep it inside ephemeral contexts — that is the 004 distinction chapter 9 expands.
Assignment sources and official precedence
You cannot reassign the same variable twice inside one file . Across sources, HashiCorp's 1.12 order of precedence is (highest wins):
Any -var and -var-file options on the command line, in the order provided , and variables from HCP Terraform .
Any *.auto.tfvars or *.auto.tfvars.json files, in lexical order.
The terraform.tfvars.json file.
The terraform.tfvars file.
Environment variables ( TF_VAR_<name> ).
The default argument of the variable block.
Memorize that list. The exam loves swapping two adjacent rungs.
Terraform automatically loads terraform.tfvars , terraform.tfvars.json , and every *.auto.tfvars / *.auto.tfvars.json in the working directory. Any other filename — prod.tfvars , secret.tfvars — is ignored until you pass -var-file . # prod.tfvars — not auto-loaded
- instance_type = "t3.large"
- environment = "prod"
- subnet_ids = ["subnet-12345", "subnet-67890"]
- enable_monitoring = true terraform apply -var="instance_type=t3.medium" -var="environment=prod"
- terraform apply -var-file="prod.tfvars"
- export TF_VAR_instance_type=t3.medium
- export TF_VAR_environment=staging
- export TF_VAR_complex_config='{"key": "value", "list": ["a", "b"]}'
If both terraform.tfvars and prod.auto.tfvars set environment , the auto file wins. If you then pass -var="environment=lab" , the CLI wins. If an HCP Terraform workspace variable is set, it sits at that same top rung as the CLI.
Complex values on the CLI or in TF_VAR_ need JSON (or careful HCL quoting). HashiCorp recommends a .tfvars file instead of fighting your shell.
- JSON variable files use variable names as root keys: {
- "image_id": "ami-abc123",
- "availability_zone_names": ["us-west-1a", "us-west-1c"]
- }
- Undeclared names
Terraform treats a value for a name that has no variable block differently by source:
TF_VAR_typo — ignored .
typo = "x" in a .tfvars file — warning (catches misspellings).
-var="typo=x" — error .
Secrets, git, and the backend trap
If a .tfvars file holds passwords, tokens, or private keys, add it to .gitignore . Official style guidance: ignore variable definition files that contain sensitive values. Commit variables.tf (declarations) and maybe a terraform.tfvars.example with fake data. Do not commit secret.tfvars .
sensitive = true does not make a committed tfvars file safe. The value is still on disk in git history.
A backend block has a separate, exam-favorite restriction. Official backend language: a backend block cannot refer to named values (input variables, locals, or data source attributes). This is illegal: terraform {
- backend "s3" {
- bucket = var.state_bucket # error — not an ordinary expression
- key = "app/terraform.tfstate"
- region = var.region
- }
- }
Backend configuration is resolved at terraform init , before ordinary evaluation. Use a partial backend plus -backend-config , or the backend's own environment variables for credentials. Do not try to parameterize the backend with var. .
004 traps for input variables
var.name is the only read syntax. local.name is a local. module.x.name is a child output .
default cannot be var.other or data.aws_ami.x.id .
terraform.tfvars loses to *.auto.tfvars , which loses to -var / -var-file / HCP Terraform.
TF_VAR_ is not the top of the list. It sits just above default .
sensitive is display-only. State still has the value unless the variable is ephemeral .
You cannot put var. inside backend .
Child modules do not read root tfvars. The parent passes arguments.