Skip to content

Fix: Terraform Variable Not Set — No Value for Required Variable

FixDevs · (Updated: )

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.tfvars

Why 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.tfvars is loaded automatically only if it exists in the current working directory with that exact name.
  • *.tfvars files with other names require -var-file=filename.tfvars to be passed explicitly.
  • TF_VAR_ environment variables are read, but the casing must match exactly (case-sensitive).
  • -var flags 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 subdirectory

Running 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.tfvars

Fix 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 automatically

In 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 var

Fix 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.). Use sensitive = true without 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 values

Use *.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.tf

Fix 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 workspace

Option 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_ directlyTF_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.tfvars

Check 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.tf

terraform.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 validate

Check 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.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles