.tfvars files are how you give Terraform environment-specific settings without hardcoding them into your .tf files.
Let’s see it in action. Imagine you have a Terraform configuration to deploy an AWS S3 bucket.
Here’s your main main.tf:
resource "aws_s3_bucket" "data" {
bucket = var.bucket_name
acl = var.bucket_acl
tags = {
Environment = var.environment_tag
ManagedBy = "Terraform"
}
}
variable "bucket_name" {
description = "The name for the S3 bucket."
type = string
}
variable "bucket_acl" {
description = "The ACL for the S3 bucket."
type = string
}
variable "environment_tag" {
description = "The tag value for the environment."
type = string
}
Now, you want to deploy this for both a development and a production environment. Instead of changing main.tf, you create two .tfvars files.
First, development.tfvars:
bucket_name = "my-dev-data-bucket-12345"
bucket_acl = "private"
environment_tag = "dev"
And production.tfvars:
bucket_name = "my-prod-data-bucket-67890"
bucket_acl = "private"
environment_tag = "prod"
To apply the development configuration, you run:
terraform apply -var-file="development.tfvars"
And for production:
terraform apply -var-file="production.tfvars"
Terraform reads the variables from the specified .tfvars file and uses them to populate the var.* references in your .tf files. This keeps your core infrastructure code clean and reusable across different environments.
The problem this solves is managing configuration drift and avoiding sensitive data exposure. Imagine you have hundreds of variables for different regions, instance sizes, or database credentials. Hardcoding these would be a nightmare to maintain and prone to errors. Using .tfvars allows you to separate your infrastructure definition from its configuration.
Internally, when Terraform encounters -var-file="filename.tfvars", it parses that file as HCL (HashiCorp Configuration Language) and loads the key-value pairs into its variable store for that specific terraform apply or terraform plan command. If you have multiple -var-file flags, Terraform loads them in order, with later files overwriting earlier ones if there are duplicate variable names.
The exact levers you control are the file names and their contents. You can have as many .tfvars files as you need. For instance, you might have us-east-1.tfvars, europe-west-2.tfvars, database.tfvars, network.tfvars, etc. Terraform’s variable loading order is quite specific: it loads variables in this order, with later sources overriding earlier ones:
- Variables defined in
.tffiles (e.g.,variable "foo" { default = "bar" }). - Environment variables (e.g.,
TF_VAR_foo=baz). terraform.tfvars(automatically loaded if present in the root directory).terraform.tfvars.json(automatically loaded if present in the root directory).*.auto.tfvarsand*.auto.tfvars.jsonfiles (automatically loaded alphabetically if present in the root directory).--varand--var-fileflags on the command line (loaded in the order they appear).
This hierarchy is crucial. It means that a variable set on the command line with -var="bucket_name=my-special-bucket" will always override a value for bucket_name defined in terraform.tfvars or any other .tfvars file.
A common pattern is to have a base terraform.tfvars file for common settings, and then environment-specific overrides using -var-file. For example, terraform.tfvars might contain region = "us-east-1", and production.tfvars would have region = "us-west-2". When you run terraform apply -var-file="production.tfvars", the region would be set to us-west-2. You can also combine them: terraform apply -var-file="common.tfvars" -var-file="production.tfvars".
If you want to avoid specifying -var-file every time, you can use files named terraform.tfvars (which is loaded automatically) or *.auto.tfvars (which are also loaded automatically in alphabetical order). This is useful for your default or most common environment.
You can also set variables using environment variables prefixed with TF_VAR_. For example, to set bucket_name, you could run export TF_VAR_bucket_name="my-env-bucket" before running terraform apply. This is excellent for CI/CD pipelines where you might inject secrets or configuration values dynamically.
The next thing you’ll likely encounter is managing sensitive variables, like API keys or passwords, and how to do that securely using Terraform’s variable input types and backend configurations.