WintelGuy.com

Understanding Terraform Variable Precedence

Contents

Introduction

Terraform, an infrastructure-as-code (IaC) tool, provides a declarative way to define and provision infrastructure while ensuring consistency and scalability. However, handling Terraform deployments across different environments or projects can be challenging without a structured approach. Terraform variables can address these challenges by enabling dynamic configuration management, reducing code duplication, and improving maintainability. In this post we will dive deeper into one of the important topic of Terraform configuration - variable precedence.

How to Define Terraform Variables

Let's quickly review variable definition and assignment methods. Terraform supports different types of variables, including input variables, local values, environment variables, and outputs.

variable blocks

Input variables must be declared using variable blocks which may specify variable type, default value, description, validation rule, and other optional arguments.

The most common way to define input variables is within a single file, called variables.tf. Note, the name variables.tf does not have any special significance or built-in function. Terraform loads all .tf files in the working directory, regardless of their names, and merges them into a single configuration before execution. However, keeping all variable definitions in variables.tf is a widely accepted convention that makes Terraform configurations more readable and maintainable.

Example: Defining and Using Input Variable

# variables.tf variable "project_id" { description = "The GCP project ID" type = string } variable "region" { description = "The region for the resources" type = string default = "us-central1" }

In this example, the value for project_id must be provided at the time of execution, while region has a default value of "us-central1".

Back to Top

.tfvars files

Terraform also supports variable definitions files with extensions .tfvars and .tfvars.json. It automatically loads terraform.tfvars, terraform.tfvars.json, and *.auto.tfvars files found in the working directory.

Example: Defining Variables in terraform.tfvars File

project_id = "my-gcp-project" region = "us-east1"

Example: Defining Variables in terraform.tfvars.json File

{ “project_id”: "my-gcp-project" “region”: "us-east1" }

Back to Top

TF_VAR_ environment variables

Terraform loads the values from environment variables with the names containing TF_VAR_ prefix followed by a name of a declared variable.

Example: Setting TF_VAR_ Environment Variables

# Set & list export TF_VAR_project_id="my-gcp-project" export TF_VAR_region="us-west1" env | grep TF_VAR_ # Use terraform apply # Clear unset TF_VAR_project_id unset TF_VAR_region

Terraform will automatically assign the values of TF_VAR_project_id and TF_VAR_region to project_id and region when running terraform apply.

Back to Top

locals blocks

Local block assigns a name to a value or an expression and helps avoid repeating the same expression multiple times in a module.

Example: Using Local Values

locals { environment = "dev" vpc_name = "${local.environment}-vpc" } resource "google_compute_network" "vpc_network" { name = local.vpc_name auto_create_subnetworks = false }

Here, local.environment is set to "dev", and local.vpc_name dynamically generates the VPC name as "dev-vpc".

Back to Top

output blocks

Outputs allow Terraform to display key information after execution, making it easier to retrieve values dynamically.

Example: Defining Outputs

output "vpc_name" { description = "The name of the created VPC" value = google_compute_network.vpc_network.name }

After running terraform apply, you can retrieve the output with terraform output vpc_name.

Terraform variables help keep configurations modular, making deployments more flexible and manageable. In the next sections we will dive deeper into Terraform input variable precedence and illustrate how different sources of variables interact in a Terraform deployment.

Back to Top

Terraform Variable Precedence and Hierarchy

As we just saw, Terraform provides multiple options for defining input variables. When the same variable is specified in different places, Terraform follows a specific precedence order to determine which value to use. Understanding this hierarchy is critical to avoid unexpected results when deploying infrastructure.

Terraform loads variables from multiple sources in a specific order and applies precedence rules outlined in the table below. Higher-precedence sources override lower-precedence ones when the same variable is defined in multiple places.

Summary Table: Terraform variable precedence order (highest to lowest)

Precedence
Rank
Variable Source Loaded Automatically?
1 (Highest) CLI -var flag
CLI -var-file flag
(applied in the provided order)
❌ No
(must be specified manually)
2 *.auto.tfvars files
*.auto.tfvars.json files
(loaded alphabetically)
✅ Yes
3 terraform.tfvars.json ✅ Yes
4 terraform.tfvars ✅ Yes
5 Environment variables (TF_VAR_) ✅ Yes
6 (Lowest) Default values in .tf files ✅ Yes

Explicit CLI flags (-var or -var-file) have the highest precedence, any value set here will override all .tfvars files, environment variables, or defaults. Terraform loads variable values and files in the order they appear on the command line and the last one wins.

The CLI -var flag is useful for quick testing but is not ideal for managing complex environments. Use the -var-file flag in conjunction with custom .tfvars files (dev.tfvars, prod.tfvars, etc.) when managing multiple environments or projects.

Terraform automatically loads variable values from the following files:

  • *.auto.tfvars files (highest rank among auto-loaded files). Files are processed in alphabetical order and the last loaded value takes precedence. Use naming conventions (01-, 02-) to explicitly control load order
  • terraform.tfvars.json
  • terraform.tfvars (lowest rank among auto-loaded files)

Environment variables with the TF_VAR_ prefix override the default values set in .tf files but are overridden by .tfvars files and CLI flags.

In the next section we will demonstrate how Terraform variables precedence works with the help of a few simple code examples.

Back to Top

Variables Precedence Demonstration

We start our demonstration with a simple main.tf file containing a singe variable with a default value assigned and then successively apply the following variable definition files and assignment methods:

  • TF_VAR_ environment variable
  • terraform.tfvars file
  • terraform.tfvars.json file
  • 01.auto.tfvars file
  • 01.auto.tfvars.json file
  • 02.auto.tfvars file
  • CLI flags with var-file.tfvars file

To follow along with this demo you need a system with Terraform installed. You can find installation steps in this document - Install Terraform

Base configuration - main.tf

Let's create a new project directory - tfvars-demo and add main.tf file with the following content:

# main.tf variable "test_var" { type = string default = "from: main.tf" } output "test_var" { value = var.test_var }

Run terraform apply -auto-approve. Terraform will display the default value of test_var as set in the main.tf file:

Outputs: test_var = "from: main.tf"

TF_VAR_ environment variable

As the next step, run the following commands to set and check TF_VAR_test_var environment variable:

export TF_VAR_test_var="from: TF_VAR" echo $TF_VAR_test_var

Run terraform apply -auto-approve. Terraform will display the value of test_var loaded from the TF_VAR_test_var environment variable (overriding the default value):

Outputs: test_var = "from: TF_VAR"

terraform.tfvars file

In the working directory create terraform.tfvars containing the following lines:

# terraform.tfvars test_var = "from: terraform.tfvars"

Run terraform apply -auto-approve. Terraform will display the value of test_var loaded from terraform.tfvars (overriding the value set by TF_VAR_test_var):

Outputs: test_var = "from: terraform.tfvars"

terraform.tfvars.json file

Create terraform.tfvars.json containing the following lines:

{ "test_var": "form: terraform.tfvars.json" }

Run terraform apply -auto-approve. Terraform will display the value of test_var loaded from terraform.tfvars.json which overrides terraform.tfvars:

Outputs: test_var = "form: terraform.tfvars.json"

01.auto.tfvars file

In the working directory create 01.auto.tfvars containing the following lines:

# 01.auto.tfvars test_var = "from: 01.auto.tfvars"

Run terraform apply -auto-approve. Terraform will display the value of test_var loaded from 01.auto.tfvars which overrides terraform.tfvars.json:

Outputs: test_var = "from: 01.auto.tfvars"

01.auto.tfvars.json file

Create 01.auto.tfvars.json containing the following lines:

{ "test_var": "from: 01.auto.tfvars.json" }

Run terraform apply -auto-approve. Terraform will display the value of test_var loaded from 01.auto.tfvars.json which overrides 01.auto.tfvars:

Outputs: test_var = "from: 01.auto.tfvars.json"

02.auto.tfvars file

Now create 02.auto.tfvars containing the following lines:

# 02.auto.tfvars test_var = "from: 02.auto.tfvars"

Run terraform apply -auto-approve. Terraform will display the value of test_var loaded from 02.auto.tfvars which overrides 01.auto.tfvars.json:

Outputs: test_var = "from: 02.auto.tfvars"

CLI flags and var-file.tfvars file

Create var-file.tfvars containing the following lines:

# var-file.tfvars test_var = "from: var-file.tfvars"

Run terraform apply -auto-approve -var="test_var=from: -var" -var-file=var-file.tfvars.
Terraform will display the value of test_var loaded form var-file.tfvars which was the last loaded file:

Outputs: test_var = "from: var-file.tfvars"

Now run terraform apply -auto-approve -var-file=var-file.tfvars -var="test_var=from: -var" (with the reversed order of CLI flags).
Terraform will display the value of test_var obtained form -var="test_var=from: -var" which was the last parameter on the command line:

Outputs: test_var = "from: -var"

This completes the demonstration of various Terraform variable assignment methods with different precedence ranks.

Back to Top

Conclusion

Terraform offers flexible ways to define variables, including .tfvars files, environment variables, CLI flags, and variable blocks in .tf files. This flexibility allows teams to customize configurations dynamically while supporting different environments and deployment scenarios. However, with multiple precedence layers, managing variables can become complex, potentially leading to unexpected behavior if not structured properly.

To maintain clarity and consistency, it is important to follow a methodical approach when defining and organizing Terraform variables. Here are some best practices:

  • Clearly document selected variable sources and definition methods to prevent confusion in collaborative environments. Keep it simple!
  • Keep variable definitions centralized, for example in variables.tf or terraform.tfvars, to avoid duplication and inconsistencies.
  • Use .tfvars files for environment-specific configurations rather than hardcoding values in .tf files.
  • Minimize reliance on environment variables (TF_VAR_*) to improve transparency.
  • Prefer -var-file over -var CLI flags for better organization and repeatability.

Understanding of Terraform variable precedence and following there recommendations helps avoid unexpected variable values and ensures correct environment configurations!

See Also:
Handling Terraform State in Multi-Environment Deployments
Terraform Value Types Tutorial
Terraform count Explained with Practical Examples
Terraform for_each Tutorial with Practical Examples
Exploring Terraform dynamic Blocks with GCP Examples
Working with External Data in Terraform
Handling Sensitive and Ephemeral Data in Terraform
Terraform Modules FAQ