Fix: Terraform Variable Not Set — No Value for Required Variable
Part of: Docker, DevOps & Infrastructure
Quick Answer
How to fix Terraform 'no value for required variable' errors — variable definition files, environment variables, tfvars files, sensitive variables, and variable precedence.
The Error
Terraform prompts for variables interactively or fails because a required variable has no value:
╷
│ Error: No value for required variable
│
│ on variables.tf line 1:
│ 1: variable "database_password" {
│
│ The root module variable "database_password" is not set, and has no default value.
│ Use a -var or -var-file command line argument to provide a value for this variable.
╵Or in CI/CD, Terraform hangs waiting for interactive input:
var.database_password
Enter a value:
(waiting indefinitely — no TTY available)Or a terraform.tfvars file exists but variables are still not being loaded:
╷
│ Error: No value for required variable
│ Variable "aws_region" not set.
╵
# Despite aws_region being in terraform.tfvarsWhy This Happens
Terraform variables without default values are required — Terraform must have a value before planning or applying. The error message is straightforward, but the real debugging challenge is figuring out why a variable you think is set is not being picked up.
The most common scenario is a developer who has a terraform.tfvars file in their project directory but runs terraform plan from a parent directory. Terraform looks for terraform.tfvars relative to the current working directory, not relative to the .tf files. This catches people who organize their infrastructure in subdirectories (infra/terraform/, deploy/tf/) and run commands from the project root.
The second-most-common scenario involves variable precedence. Terraform has six levels of precedence for variable values, and a variable can be set in one place but silently overridden by another. An environment variable TF_VAR_region is overridden by a terraform.tfvars entry, which is overridden by a -var flag. When a value appears wrong, the issue is often that a higher-precedence source is supplying a different value, not that the variable is truly unset.
Specific causes:
terraform.tfvarsis loaded automatically only if it exists in the current working directory with that exact name.*.tfvarsfiles with other names require-var-file=filename.tfvarsto be passed explicitly.TF_VAR_environment variables are read, but the casing must match exactly (case-sensitive).-varflags on the CLI override all other sources.- Module variables are not the same as root module variables — variables must be declared and passed explicitly to each module.
Diagnostic Timeline
Here is the step-by-step process an experienced infrastructure engineer follows when Terraform reports a missing variable.
Minute 0 — Read the error and identify the variable. The error message names the exact variable and the file where it is declared. Note whether it says “not set” (no value provided anywhere) or a different message. Also note whether the error comes from the root module or a child module — child module variable errors look different and have a different fix.
Minute 1 — First instinct: “I already set this variable.” Check where you think the variable is set. If it is in terraform.tfvars, verify the file is in the same directory where you run terraform plan. If it is an environment variable, run env | grep TF_VAR_ and confirm the name matches exactly (case-sensitive). A common mistake: TF_VAR_Database_Password does not match variable "database_password".
Minute 2 — Check the working directory. Run pwd and compare it to the location of your .tf files and terraform.tfvars. If they are in different directories, Terraform will not find terraform.tfvars automatically. Either cd into the correct directory or pass -var-file with the full path.
Minute 3 — Check variable precedence. If the variable is set but the value seems wrong or ignored, the issue may be precedence. Run terraform plan and look at the plan output for the variable’s effective value. The precedence order from lowest to highest is: environment variables, terraform.tfvars, *.auto.tfvars, -var-file flags, -var flags. A -var flag always wins. If you set the variable in terraform.tfvars but also pass an empty string via -var="region=", the empty string takes effect.
Minute 4 — Check for typos in variable names. Terraform variable names are case-sensitive. variable "aws_Region" and TF_VAR_aws_region do not match. Open variables.tf and compare the exact variable name against your tfvars file entries and environment variables character by character.
Minute 5 — Check module variable passing. If the error references a variable inside a module, the root module must explicitly pass the value. TF_VAR_ environment variables only set root module variables. If your modules/database/variables.tf declares variable "password", the root main.tf must include password = var.database_password in the module block. The environment variable TF_VAR_password does not reach child modules directly.
Minute 6 — Run with debug logging. If nothing obvious is wrong, enable Terraform debug output to see exactly which files it loads and which variables it reads:
TF_LOG=DEBUG terraform plan 2>&1 | grep -i "variable\|tfvars\|TF_VAR"This shows every variable source Terraform evaluates, in order.
Fix 1: Create or Fix terraform.tfvars
The most straightforward fix — create a terraform.tfvars file in the same directory as your .tf files:
# terraform.tfvars — automatically loaded by Terraform
database_password = "secure-password-here"
aws_region = "us-east-1"
instance_type = "t3.micro"
project_name = "my-app"Verify the file location:
project/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars ← Must be in THIS directory, not a subdirectoryRunning terraform plan or terraform apply from a different directory means terraform.tfvars in the project directory will not be found.
For multiple environments — use separate .tfvars files and pass them explicitly:
# Development
terraform plan -var-file="environments/dev.tfvars"
# Staging
terraform plan -var-file="environments/staging.tfvars"
# Production
terraform plan -var-file="environments/prod.tfvars"project/
├── main.tf
├── variables.tf
└── environments/
├── dev.tfvars
├── staging.tfvars
└── prod.tfvarsFix 2: Use Environment Variables
For CI/CD pipelines or when you do not want values in files, use TF_VAR_ environment variables:
# Set environment variable — Terraform reads TF_VAR_<variable_name>
export TF_VAR_database_password="secure-password"
export TF_VAR_aws_region="us-east-1"
export TF_VAR_instance_count="3"
# Variable names are CASE SENSITIVE
# Variable in variables.tf: database_password (lowercase)
# Environment variable: TF_VAR_database_password (same case)
terraform plan # Picks up TF_VAR_ variables automaticallyIn GitHub Actions:
# .github/workflows/deploy.yml
jobs:
terraform:
runs-on: ubuntu-latest
env:
TF_VAR_database_password: ${{ secrets.DB_PASSWORD }}
TF_VAR_aws_region: us-east-1
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- name: Terraform Plan
run: terraform plan
working-directory: terraform/Complex types via environment variables — must be HCL/JSON encoded:
# List variable
export TF_VAR_allowed_ips='["10.0.0.1", "10.0.0.2"]'
# Map variable
export TF_VAR_tags='{"Environment":"prod","Team":"backend"}'
# Object variable
export TF_VAR_database_config='{"host":"db.example.com","port":5432,"name":"mydb"}'Fix 3: Pass Variables via -var Flag
For one-off values or script-based workflows:
# Single variable
terraform plan -var="database_password=my-secret-password"
# Multiple variables
terraform plan \
-var="aws_region=us-east-1" \
-var="instance_type=t3.medium" \
-var="database_password=$DB_PASS"
# Combine with var-file
terraform apply \
-var-file="environments/prod.tfvars" \
-var="database_password=$DB_PASS" # Override specific varFix 4: Set Default Values in variables.tf
For non-sensitive variables with reasonable defaults, set them in the variable declaration:
# variables.tf
# Required variable — no default, must be provided
variable "database_password" {
type = string
description = "Master password for the database instance"
sensitive = true # Prevents value from appearing in logs
}
# Optional variable — has a default
variable "aws_region" {
type = string
description = "AWS region for all resources"
default = "us-east-1" # Used if not set elsewhere
}
# Variable with validation
variable "instance_type" {
type = string
description = "EC2 instance type"
default = "t3.micro"
validation {
condition = contains(["t3.micro", "t3.small", "t3.medium", "t3.large"], var.instance_type)
error_message = "instance_type must be one of: t3.micro, t3.small, t3.medium, t3.large"
}
}
# Map variable with default
variable "tags" {
type = map(string)
default = {
ManagedBy = "Terraform"
Team = "platform"
}
}Note: Never set
default = ""(empty string) as a workaround for sensitive variables. An empty string is a valid value that will be used — it will not prompt the user, and it might cause downstream errors (database with no password, etc.). Usesensitive = truewithout a default to force explicit provision.
Fix 5: Understand Variable Precedence
When the same variable is set in multiple places, Terraform uses this precedence (later overrides earlier):
1. Environment variables (TF_VAR_*) ← Lowest precedence
2. terraform.tfvars file (auto-loaded)
3. terraform.tfvars.json file (auto-loaded)
4. *.auto.tfvars files (auto-loaded, alphabetical order)
5. -var-file flags (in order specified)
6. -var flags ← Highest precedence# Example — variable "region" set in multiple places:
# TF_VAR_region=us-west-1 (env var — lowest)
# terraform.tfvars: region = "us-east-1"
# -var="region=eu-west-1" (CLI flag — highest)
# Effective value: eu-west-1 (CLI flag wins)
terraform plan -var="region=eu-west-1"
# Effective value: us-east-1 (tfvars wins over env var)
terraform plan
# Show final variable values
terraform plan -var-file=prod.tfvars # Look at plan output for variable valuesUse *.auto.tfvars for per-workspace defaults:
# These files are auto-loaded in alphabetical order
project.auto.tfvars # Always loaded
dev.auto.tfvars # Always loaded (named without environment guard)
# Better: use workspace-specific logic in variables.tfFix 6: Handle Sensitive Variables Safely
Sensitive variables (passwords, API keys, certificates) should not be stored in .tfvars files that could be committed to git:
# variables.tf — declare as sensitive
variable "database_password" {
type = string
sensitive = true # Terraform redacts this from logs and output
}
variable "api_key" {
type = string
sensitive = true
}Option 1 — .tfvars file in .gitignore:
# .gitignore
*.tfvars
!terraform.tfvars.example # Keep the example file committed
# terraform.tfvars.example — committed to repo as documentation
database_password = "REPLACE_WITH_ACTUAL_PASSWORD"
api_key = "REPLACE_WITH_API_KEY"Option 2 — Terraform Cloud / HCP Terraform workspace variables:
# Set sensitive variables in Terraform Cloud UI (marked as sensitive — never shown)
# Automatically available when running Terraform from the workspaceOption 3 — HashiCorp Vault:
# Read secrets from Vault at plan/apply time
data "vault_generic_secret" "db" {
path = "secret/myapp/database"
}
resource "aws_db_instance" "main" {
password = data.vault_generic_secret.db.data["password"]
}Option 4 — AWS Secrets Manager / SSM Parameter Store:
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "myapp/production/database-password"
}
resource "aws_db_instance" "main" {
password = data.aws_secretsmanager_secret_version.db_password.secret_string
}Fix 7: Fix Variable Passing to Modules
Variables defined in the root module are not automatically available inside child modules — they must be explicitly passed:
# Root module — variables.tf
variable "database_password" {
type = string
}
variable "aws_region" {
type = string
default = "us-east-1"
}
# Root module — main.tf — must pass variables to child modules
module "database" {
source = "./modules/database"
# Explicit variable passing — required
password = var.database_password # Must be passed in
region = var.aws_region
project = var.project_name
}
# modules/database/variables.tf — module's own variable declarations
variable "password" {
type = string
sensitive = true
}
variable "region" {
type = string
}
variable "project" {
type = string
}Module variables do not accept TF_VAR_ directly — TF_VAR_database_password sets the root module variable. The root module must then pass it to the module:
module "database" {
source = "./modules/database"
password = var.database_password # Chain: TF_VAR → root var → module input
}Still Not Working?
Check current working directory — Terraform looks for terraform.tfvars in the directory where you run the command, not the directory of the .tf files:
# WRONG — running from parent directory
cd /project
terraform plan # Looks for /project/terraform.tfvars, not /project/terraform/terraform.tfvars
# CORRECT — run from the directory containing .tf files
cd /project/terraform
terraform plan # Finds /project/terraform/terraform.tfvarsCheck for typos in variable names — a TF_VAR_database_Password (capital P) will not match variable "database_password" (lowercase):
# List all TF_VAR_ environment variables
env | grep ^TF_VAR_
# Compare against variable names in variables.tfterraform.tfvars vs terraform.tfvars.json — Terraform auto-loads both. The JSON format must use valid JSON (no comments):
{
"database_password": "my-password",
"aws_region": "us-east-1",
"instance_count": 3
}Check Terraform workspace isolation. If you use Terraform workspaces, each workspace has its own state but shares the same variable definitions. A common pattern is using terraform.workspace in variable defaults or locals, but the variables themselves still need to be set. If you rely on *.auto.tfvars and the values are workspace-specific, use conditional logic in locals instead:
locals {
environment = terraform.workspace
instance_type = {
dev = "t3.micro"
staging = "t3.small"
prod = "t3.large"
}[terraform.workspace]
}Verify the .tfvars file is valid HCL. A syntax error in the tfvars file (missing quotes, wrong brackets for a list) can cause Terraform to skip the file entirely without a clear error about which variable was not loaded. Run terraform fmt on the tfvars file to catch formatting issues, or use terraform validate after init:
terraform fmt terraform.tfvars
terraform init
terraform validateCheck for -input=false in CI/CD scripts. If your CI pipeline passes -input=false to Terraform (which it should, to prevent hanging), Terraform will immediately fail on any missing variable instead of prompting. This is the correct behavior, but it surfaces variable issues that you might not notice locally where Terraform prompts you interactively.
For related Terraform issues, see Fix: Terraform State Lock, Fix: Terraform Plan Error — Invalid Reference, Fix: Terraform Failed to Install Provider, and Fix: Terraform Import Error.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Terraform Import Error — Resource Not Importable or State Conflict
How to fix Terraform import errors — terraform import syntax, import blocks (Terraform 1.5+), state conflicts, provider-specific import IDs, and importing existing infrastructure.
Fix: Terraform Error Acquiring State Lock — State Lock Conflict
How to fix Terraform state lock errors — understanding lock mechanisms, safely force-unlocking stuck locks, preventing lock conflicts in CI/CD, and using remote backends correctly.
Fix: Helm Not Working — Release Already Exists, Stuck Upgrade, and Values Not Applied
How to fix Helm 3 errors — release already exists, another operation is in progress, --set values not applied, nil pointer template errors, kubeVersion mismatch, hook failures, and ConfigMap changes not restarting pods.
Fix: nginx Upstream Load Balancing Not Working — All Traffic Hitting One Server
How to fix nginx load balancing issues — upstream block configuration, health checks, least_conn vs round-robin, sticky sessions, upstream timeouts, and SSL termination.