Chapter 4: Variables, Outputs, and Expressions
Topic 22

Input Variables

Inputs

Input variables are a configuration's parameters — the values you change between environments or runs without editing the code. A variable block declares one; var.name reads it. Variables are how a single configuration serves dev and prod, how a module exposes a clean interface, and how environment specifics stay out of the committed code.

The thing that trips people up is not the syntax — it is where a value actually comes from. Terraform reads a variable from several sources with a strict precedence, and a surprising number of "why is it using the wrong region?" sessions end with someone discovering a -var flag or a stray .auto.tfvars file was winning.

Declaring a Variable

A variable block has three fields worth setting on every declaration: type constrains what callers may pass, default makes the variable optional by supplying a fallback, and description documents the interface. The block below declares an instance type that defaults to t3.micro and a required environment name with no default.

variables.tf — two declarations
variable "instance_type" {
  type        = string
  default     = "t3.micro"
  description = "EC2 instance type for the app servers"
}

variable "environment" {
  type        = string
  description = "Deployment environment: dev, staging, or prod"
}

Reading a Variable

You reference a variable anywhere a value is allowed with var.name. There is no special interpolation needed for a bare reference — var.instance_type evaluates to the string. The resource below reads both variables, building a name from the environment and selecting the instance type from the input.

main.tf — referencing var.name
resource "aws_instance" "app" {
  ami           = "ami-0abc123"
  instance_type = var.instance_type
  tags = {
    Name        = "app-${var.environment}"
    Environment = var.environment
  }
}

Where Values Come From

Terraform collects a variable's value from several sources and the most specific wins. In ascending order of precedence: the default in the block, then terraform.tfvars, then any *.auto.tfvars files (alphabetical), then -var-file and -var flags on the command line. The TF_VAR_ environment variables sit below the tfvars files. Knowing this order is what lets you predict which value actually applies.

Variable precedence — later sources override earlier ones
default in block
TF_VAR_ env
tfvars files
-var-file / -var
setting values three ways
# prod.tfvars
instance_type = "m5.large"
environment   = "prod"

# an environment variable feeds var.environment
export TF_VAR_environment=staging

# a -var flag overrides the tfvars file
terraform apply -var-file=prod.tfvars -var="instance_type=m5.xlarge"

Required Variables and Automation

A variable with no default is required. In an interactive session Terraform prompts for it; in automation there is no human to answer, so an unset required variable either hangs waiting for input or fails the run. That is why CI pipelines always supply every required variable through a -var-file or TF_VAR_ environment variable, never relying on the prompt.

Per-Environment Variable Files

The same code drives dev and prod by swapping the variable file: terraform apply -var-file=dev.tfvars versus -var-file=prod.tfvars. The configuration stays byte-for-byte identical across environments, and the only thing that differs is the inputs — which is exactly the property that makes a config reusable rather than copy-pasted per environment.

Sensitive Variables

Setting sensitive = true on a variable keeps its value out of plan and apply output, so a password passed in does not get echoed into a terminal or a CI log. It does not encrypt anything and it does not keep the value out of state — if the value flows into a resource attribute, it lands in the state file in plaintext regardless. sensitive is about display, not storage.

Common Mistakes
  • Relying on the interactive prompt for a required variable in CI, where no human answers and the run hangs until it times out or fails.
  • Misreading precedence and being surprised a -var flag overrides the .tfvars file — the more specific command-line source always wins.
  • Committing a terraform.tfvars full of one environment's specifics, coupling code that should be reusable to a single environment.
  • Putting a secret in a variable default in code, so it sits in version control in plaintext regardless of sensitive = true.
  • Leaving a stray *.auto.tfvars in the directory, which Terraform loads automatically and silently overrides what you thought you set.
Best Practices
  • Give every variable a description and an explicit type — the description is the interface documentation a caller reads.
  • Drive environments with -var-file=prod.tfvars, keeping the configuration identical across every environment.
  • Pass secrets through TF_VAR_ environment variables or a secrets manager, never as a committed default.
  • Set a default only for values that are genuinely environment-independent, so a wrong environment can't inherit a stale fallback.
  • Mark any secret variable sensitive = true to keep it out of plan output and CI logs, while still securing state separately.
Comparable tools CloudFormation parameters with NoEcho for secret inputs Pulumi config with first-class secret support Ansible variables and --extra-vars on the command line

Knowledge Check

Both a -var flag and a terraform.tfvars file set instance_type. Which value applies?

  • The -var flag, because the command line is the most specific source and wins
  • The terraform.tfvars value, because a file is more authoritative than a flag
  • Neither — Terraform errors on the conflict and refuses to plan
  • Whichever was defined first in alphabetical order of the source name

What makes a variable required rather than optional?

  • Declaring it with no default, so Terraform must obtain a value from somewhere
  • Adding the required = true argument to the variable block declaration
  • Marking the variable with sensitive = true in its block
  • Referencing the variable from at least one resource somewhere in the configuration

What does sensitive = true on a variable actually do?

  • Suppresses the value in plan and apply output; it does not encrypt it or keep it out of state
  • Encrypts the value at rest in the state file using the backend's key
  • Prevents the value from ever being written into the state file at all
  • Forces the value to be supplied through an environment variable only, never through a tfvars file

Why pass a database password via TF_VAR_db_password instead of a variable default?

  • A default in code lands in version control in plaintext; an environment variable keeps the secret out of the committed config
  • Environment variables holding secrets are encrypted by Terraform automatically before they are read in and used during a plan
  • A variable default cannot hold a string value longer than 64 characters
  • Only values supplied via environment variables can be marked sensitive

You got correct