Skip to main content
DevOps Insightsguides & answers

Terraform · troubleshooting

Error: Variables not allowed

Terraform found var. (or a local or data value) in a place that is read before variables exist: a backend block, a variable's default, or a module's source. For a backend, pass the values to terraform init with -backend-config or a .tfbackend file. For a variable's default, use a local instead; a module's source must be written out in full.

Reproduced on Terraform 1.14.0, . Where the text says “reproduced”, the behaviour was run on that version, not taken from documentation.

First published

New to this? Read what is actually happening first, then come back to the steps.

On this page · 9 sections

Before you start

  • The configuration directory, and the values you meant the variable to supply (bucket, key, region, path).

Run this first

step 1 of 1
terraform init -input=false

Look for in terraform:, in variable, or in module, which tells you which cause it is

Reports each place a variable is used too early, with the file and line.

Work out which cause you have

A few questions to narrow the list. Every answer ends in a command that confirms or rules the cause out — this cannot see your cluster, so nothing here is a certainty until you have checked.

Narrow it down

0 answered · nothing is sent anywhere

Which block does the error point into?

Cause space

3 of 3 still possible

  • A variable or local in the backend blockCommon
  • A variable's default refers to another variableCommon
  • A variable in a module's source or versionOccasional

Nothing ruled out yet. Answer the question above and the branches your answer eliminates will strike through here.

Or diagnose it manually

In this order. The first command usually contains the whole answer.

  1. Step 1

    terraform init -input=false

    Reports each place a variable is used too early, with the file and line.

    Look for in terraform:, in variable, or in module, which tells you which cause it is

Every cause, and how to fix it

Ordered by how often each one turns out to be the answer.

A variable or local in the backend block

Common

Usually an attempt to choose the state bucket, key or path per environment with var.env or a local.

Confirm

terraform init -input=false

The error pointing at a line inside terraform { backend ... }

Fix

  • Remove the arguments that vary from the block and pass them at init: terraform init -backend-config="key=envs/prod.tfstate", or keep one file per environment, such as prod.tfbackend, and run terraform init -backend-config=prod.tfbackend.
  • When you switch between those files in one working directory, add -reconfigure if each file points at its own existing state. If you are moving state that is already in use to a new location, use -migrate-state instead. Either way, check terraform state list before you apply.
  • If the only difference is the environment's name, Terraform workspaces keep separate state under one backend configuration.
terraform init -reconfigure -backend-config=prod.tfbackend

prod.tfbackend holds lines such as key = "envs/prod.tfstate". Reproduced with the local backend's path argument on Terraform 1.14.0.

A variable's default refers to another variable

Common

default must be a literal value. A default worked out from other inputs belongs in a local instead.

Confirm

grep -n 'default *= *var\.' *.tf

A default that uses var.

Fix

  • Give the variable a fixed default, or default = null, and compute the derived value as a local: locals { region = coalesce(var.region, var.default_region) }. The fallback only works with default = null; with no default at all the variable is required.

A variable in a module's source or version

Occasional

terraform init downloads modules before any variable is known, so source and version must be literal strings.

Confirm

grep -n 'source *= *.*var\.' *.tf

A module source built from a variable

Fix

  • Write the source out as a fixed string, such as "./modules/small".
  • To choose between two modules, declare one module block for each, each with its own fixed source, and switch them with count: count = var.size == "small" ? 1 : 0 in one and count = var.size == "large" ? 1 : 0 in the other. Read an output from whichever exists with one(concat(module.small[*].size, module.large[*].size)). Reproduced on Terraform 1.14.0: -var size=large planned only the large module's output.

Understanding it properly

Skip this if you are mid-incident — the working part of the page is above. Worth reading afterwards, because understanding the mechanism is what stops the next one.

In plain terms

Terraform has to know where your state is, and which module code to download, before it evaluates anything else. Variables are only worked out later. So a backend block, a module source and a variable's default must be written out as fixed values, not built from variables; if they refer to one, Terraform stops.

Words used on this page

Backend block
terraform { backend "s3" { ... } }: where state is stored. Read during terraform init, before variables are evaluated.
Partial configuration
Leaving backend arguments out of the block and supplying them to terraform init with -backend-config, as key=value pairs or a file.
.tfbackend file
A file of backend arguments, one name = value per line, passed with -backend-config=FILE.
What is actually happening

Reproduced on Terraform 1.14.0: a backend block with path = "state/${var.env}.tfstate" failed terraform init with Error: Variables not allowed and Variables may not be used here. HashiCorp's documentation is explicit that a backend block cannot refer to named values such as input variables, locals or data source attributes.

The same error came from a variable whose default referred to another variable, and from a module block whose source used a variable; the module case also printed Unsuitable value: value must be known.

Partial configuration is the documented way out. With an empty backend "local" {} block, terraform init -backend-config="path=state/dev.tfstate" initialised and the apply wrote state/dev.tfstate. Moving to a different .tfbackend file then stopped with Error: Backend configuration changed until init was run with -reconfigure.

How to tell this is your problem
WhereWhat you see
terraform initError: Variables not allowed ... on main.tf line N, in terraform: ... Variables may not be used here.
In a variable blockThe same error on a default = var.OTHER line
In a module blockUnsuitable value: value must be known, then Variables not allowed on the source line

How to know it is actually fixed

  • terraform init succeeds, and terraform plan reads the state you expected (terraform state list shows its resources).

Stopping it happening again

  • Keep one .tfbackend file per environment in the repository, without secrets, and make CI pass it explicitly.

Security considerations

  • HashiCorp's backend documentation warns that values passed with -backend-config, or written in the block, are stored in plain text in the .terraform directory and in plan files. Supply credentials through environment variables instead.

Sources

Behaviour described here is drawn from official documentation. Where a figure could not be confirmed on an official page it is attributed in the text rather than stated as canonical.

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it