Skip to content
Infrastructure as Code iac variables 4 min read

Environment Variables

Terraform reads a handful of environment variables that let you set input values and tune the CLI without touching any .tf or .tfvars files. This is the cleanest way to inject secrets and per-environment settings in CI pipelines, where committing values to disk is undesirable. Environment variables also override interactive prompts, so automated runs never block waiting for input. Everything here works identically in OpenTofu, which honors the same TF_* names.

Setting input variables with TF_VAR_

Any variable declared with a variable block can be supplied through an environment variable named TF_VAR_<name>. The suffix matches the variable name exactly, including underscores, and is case sensitive.

variable "region" {
  type    = string
  default = "us-east-1"
}

variable "instance_count" {
  type = number
}

resource "aws_instance" "web" {
  count         = var.instance_count
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
}
export TF_VAR_region="eu-west-1"
export TF_VAR_instance_count=3
terraform apply

Because the shell passes everything as a string, Terraform parses the value into the variable’s declared type. The example above coerces "3" into a number automatically. For complex types you provide HCL/JSON-style literals as the value.

export TF_VAR_tags='{ team = "platform", env = "prod" }'
export TF_VAR_subnets='["subnet-a1b2", "subnet-c3d4"]'
terraform plan

Variable values in .tfvars and -var flags take precedence over TF_VAR_*. The full order, lowest to highest, is: environment variables, terraform.tfvars, *.auto.tfvars (alphabetical), then -var-file/-var on the command line.

Configuring Terraform behavior

Beyond input values, several environment variables change how the CLI itself runs. These are read at startup and apply to every command in the session.

VariablePurposeExample value
TF_LOGEnable internal logging at a given levelTRACE, DEBUG, INFO, WARN, ERROR
TF_LOG_PATHWrite logs to a file instead of stderr./terraform.log
TF_WORKSPACESelect the workspace without terraform workspace selectstaging
TF_CLI_ARGSAppend arguments to every command-no-color
TF_CLI_ARGS_<command>Append arguments to one commandTF_CLI_ARGS_plan
TF_INPUTDisable interactive prompts when set to falsefalse
TF_DATA_DIROverride the .terraform working directory/tmp/tf
TF_IN_AUTOMATIONSoften CLI output hints when non-emptytrue

Debugging with TF_LOG

When a plan misbehaves, turn on logging to see provider requests and internal decisions. TRACE is the most verbose; DEBUG is usually enough.

export TF_LOG=DEBUG
export TF_LOG_PATH=./terraform-debug.log
terraform apply

Output:

2026-06-14T09:12:04.118Z [DEBUG] provider.terraform-provider-aws: HTTP Request Sent:
  POST https://ec2.eu-west-1.amazonaws.com
2026-06-14T09:12:04.402Z [INFO]  Starting apply for aws_instance.web
2026-06-14T09:12:09.871Z [DEBUG] aws_instance.web: apply complete

Unset TF_LOG afterward, since trace logs are noisy and can contain sensitive request data.

Selecting workspaces and injecting flags

TF_WORKSPACE pins the active workspace for the process, which is handy when a single pipeline runs against several environments. TF_CLI_ARGS_<command> lets you standardize flags without editing the command invocation.

export TF_WORKSPACE=staging
export TF_CLI_ARGS_plan="-compact-warnings -parallelism=20"
terraform plan

When environment variables beat files

Files such as terraform.tfvars are ideal for stable, non-secret configuration that belongs in version control. Environment variables shine in the opposite situations: secrets and ephemeral CI context.

Use casePrefer env varsPrefer .tfvars files
Secrets (passwords, tokens)Yes — never committedNo — risks leaking into git
Per-build CI valuesYes — injected by the runnerNo — would need generating
Shared, reviewable defaultsNoYes — visible in diffs
Large structured configNo — awkward to quoteYes — readable HCL

A typical CI job pulls secrets from a vault and exports them just before the run:

export TF_VAR_db_password="$(vault kv get -field=password secret/prod/db)"
export TF_VAR_api_key="${API_KEY}"   # provided by the CI secret store
export TF_INPUT=false
export TF_IN_AUTOMATION=true
terraform apply -auto-approve

Output:

aws_db_instance.main: Creating...
aws_db_instance.main: Creation complete after 4m12s [id=prod-db]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Mark secret variables with sensitive = true so their values are redacted in plan output. The environment variable still feeds the value in, but Terraform won’t echo it to logs.

Best Practices

  • Prefix only the variable name with TF_VAR_; the rest must match your variable block character for character.
  • Pass secrets through TF_VAR_* from a secrets manager rather than storing them in committed .tfvars files.
  • Set TF_INPUT=false and TF_IN_AUTOMATION=true in CI so runs never hang on a prompt and emit cleaner output.
  • Keep TF_LOG off by default; enable it temporarily for debugging and clear TF_LOG_PATH files afterward, as they may contain sensitive data.
  • Remember that -var and .tfvars override TF_VAR_*, so avoid setting the same variable in two places to prevent surprises.
  • Use TF_CLI_ARGS_<command> to enforce team-wide flags (like -parallelism) instead of relying on everyone typing them.
Last updated June 14, 2026
Was this helpful?