Skip to content

Input Variables

An input variable is a named, typed parameter that lets the same Terraform configuration produce different Azure infrastructure depending on who calls it and with what values.

A variable block has four parts worth knowing well: type constrains what shape of value is acceptable, default supplies a fallback when nobody sets a value, description documents intent for the next reader, and sensitive redacts the value from most CLI output.

variable "environment" {
type = string
description = "Deployment environment name, e.g. dev, staging, prod"
default = "dev"
}
variable "vm_count" {
type = number
description = "Number of virtual machines to launch"
default = 2
}
variable "enable_monitoring" {
type = bool
description = "Whether to enable Azure Monitor diagnostics on the virtual machines"
default = true
}
variable "allowed_ports" {
type = list(number)
description = "TCP ports allowed inbound on the network security group"
default = [22, 443]
}
variable "resource_tags" {
type = map(string)
description = "Tags applied to every resource in this configuration"
default = {
project = "payments"
team = "platform"
}
}
variable "db_config" {
type = object({
sku_name = string
storage_mb = number
backup_retention_days = number
})
description = "Structured configuration for the PostgreSQL Flexible Server"
default = {
sku_name = "B_Standard_B1ms"
storage_mb = 32768
backup_retention_days = 7
}
}
variable "sql_admin_password" {
type = string
description = "Administrator login password for the Azure SQL server"
sensitive = true
}

object({...}) is worth calling out: it is a typed, fixed-shape structure — every key you list is required (unless you give it a default with optional()), and Terraform rejects a caller-supplied value that has the wrong shape at plan time, well before any Azure API call happens.

The sensitive = true argument on sql_admin_password only affects display. Terraform masks the value in plan/apply output and in the CLI as (sensitive value). It does not encrypt or omit the value from the state file — the state-management module already covered this: state stores every attribute of every managed resource in plain text inside the JSON, including values that came from a sensitive variable. Treat sensitive = true as a display protection for terminals and CI logs, not as a substitute for a properly access-controlled remote backend.

Validating a variable with a validation block

Section titled “Validating a variable with a validation block”

A default or a type constraint only checks the shape of a value, not whether it makes sense. A validation block adds a custom condition and a specific error_message, so a caller gets immediate, actionable feedback at plan time instead of a confusing error from deep inside the azurerm provider.

variable "location" {
type = string
description = "Azure region to deploy resources into, e.g. eastus, westeurope"
validation {
condition = can(regex("^(eastus|eastus2|westeurope|southeastasia)$", var.location))
error_message = "location must be one of: eastus, eastus2, westeurope, southeastasia."
}
}
variable "environment" {
type = string
description = "Deployment environment name"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "environment must be one of: dev, staging, prod."
}
}

Without that first validation block, passing location = "mars" would sail through terraform plan and only fail once Terraform actually calls the Azure Resource Manager API to create a resource group in that region — a much slower feedback loop, and a much less specific error message than “location must be one of: eastus, eastus2, westeurope, southeastasia.” The can() wrapper turns a regex() failure into false instead of an outright error, which is exactly what a condition expression needs: it must always evaluate to true or false, never raise.

Terraform lets you set a value for the same variable from several places at once, and it resolves conflicts with a strict, well-defined precedence. From highest priority (wins) to lowest:

  1. -var and -var-file on the command line, in the order given — the most explicit, most temporary way to set a value.
  2. *.auto.tfvars / *.auto.tfvars.json files, auto-loaded from the working directory in lexical (alphabetical) order of filename.
  3. terraform.tfvars / terraform.tfvars.json, auto-loaded if present.
  4. TF_VAR_<name> environment variables — useful for CI pipelines and for values you do not want sitting in a file at all.
  5. The variable’s own default — used only if nothing above supplied a value.
Terminal window
# Highest precedence: an explicit CLI flag for this one run
terraform apply -var="vm_size=Standard_D8s_v3"
# Or point at a specific tfvars file
terraform apply -var-file="prod.tfvars"
# Environment variable equivalent of setting vm_size
export TF_VAR_vm_size="Standard_D2s_v3"
terraform apply

If vm_size is set in terraform.tfvars, again in prod.auto.tfvars, and again via -var on the command line, the -var value wins — Terraform does not merge scalar values across sources, it picks the highest-priority one that exists. Collection types like maps and objects are the exception worth remembering later: some tooling merges nested values rather than fully overriding, but for the plain sources above, a defined value at a higher-priority source simply replaces a lower-priority one for that variable.

flowchart LR
  a["-var / -var-file (CLI flag)"] --> r["Resolved value of var.vm_size"]
  b["*.auto.tfvars (alphabetical)"] --> r
  c["terraform.tfvars"] --> r
  d["TF_VAR_vm_size (env var)"] --> r
  e["default in the variable block"] --> r
  r --> res["azurerm_linux_virtual_machine.web"]
Multiple value sources resolve to one value per variable, by precedence
What does a validation block on a variable actually prevent?
If a variable has a value in terraform.tfvars, in a TF_VAR_ environment variable, and passed via -var on the command line, which value does Terraform use?
What does sensitive = true on a variable actually protect?
Between *.auto.tfvars files and terraform.tfvars, which one wins if both set the same variable?