Skip to content

Terraform Variables

Input Variables Documentation

Terraform variable blocks allow an existing configuration to be tailored using external input values. A variable block starts with the keyword 'variable' followed by the variable name and a block of attributes associated to the variable. These attributes include:

  • type ("string", "bool", "number", "object", "map", "list", "tuple", "sets", "any")
  • default: a default value to use if none is supplied
  • description: a text string explaining the use of the variable
  • validation: a block with code used to validate the value supplied for the variable
  • sensitive: a boolean value to determine if the variable value is displayed by the Terraform CLI

Variables and their default values can be declared anywhere in your configuration. However, it is preferable to create a separate 'variables.tf' so that others will know where to look to customise the configuration.

Variables values can be assigned on the command-line:

terraform plan -var azure_region="UK South"

This becomes impractical if you want to override several default values. Instead you can additionally create a 'variable definitions' file. The variable definitions file should consist of only variable assignments:

azure_region = "West Europe"
list_of_fruit = ["mango", "pineapple", "blueberry", "grape"]
res_tags = {
    location   = "UK South"
    department = "Accounts"
    owner      = "Freda Perry"
}

Terraform will automatically load variable defintion files named either 'terraform.tfvars' or files ending in '.auto.tfvars'. If you prefer to use JSON format for your variable definition files, Terraform will also load any files named 'terraform.tfvars.json' or ending in 'auto.tfvars.json'.

{
  "azure_region" : "West Europe",
  "list_of_fruit" : ["mango", "pineapple", "blueberry", "grape"],
  "res_tags" : {
    "location"   : "UK South",
    "department" : "Accounts",
    "owner"      : "Freda Perry"
  }
}

Variables can also assigned in environment variables by prefixing the variable name with "TF_VAR_":

export TF_VAR_location="UK South"

The corresponding variable "location" will need to be declared in your scripts to be accessible:

variable "location" {
  default = ""
}

output "region" {
  value = var.location
}

String Variables

Values for string variables are assigned with double-quoted strings:

variable "azure_region" {
  type        = string
  default     = "UK West"
  description = "The Azure region to deploy resources"
}

output "az_region" {
  value = var.azure_region
}

Terraform supports string interpolation to allow the generation of strings by combining variable values and string values. String interpolation expressions are declared within a double-quoted string and references to variable values or other resources are enclosed within '${}':

name = "vm-${var.user_name}-${var.res_tags["department"]}"

Terraform also supports 'String Directives' which allow the generation of strings using loops or conditional statements. String directives are declared within double-quoted strings and the control statements are enclosed within '%{}':

output "users_created" {
  value = "%{ for val in var.user_names }${val} %{ endfor }"
}

Note that the string directive will output everything between the '%{}' blocks: so that the space after '${val}' is also part of the output in the above example

String directives also support 'if' statements and HEREDOCS:

enable_public_ip = <<EOT
%{ if var.location == "UK South" }
true
%{ else }
false
%{ endif }
EOT

Bool variables

Boolean values can be stored in variables and used to toggle a feature in your configuration:

variable "enable_ultra_ssd" {
    type    = bool
    default = false
    description = "Should UltraSSD_LRS disks be available"
}

resource "azurerm_virtual_machine" "my_vm" {
    ...
    ultra_ssd_enabled = var.enable_ultra_ssd
    ...
}

List Variables

Lists are assigned as comma-separated lists inside square brackets:

variable "private_subnet_cidr_blocks" {
    description = "Available cidr blocks for private subnets"
    type = list(string)
    default = [
        "172.30.1.0/24",
        "172.30.2.0/24",
        "172.30.3.0/24",
        "172.30.4.0/24",
        "172.30.5.0/24",
        "172.30.6.0/24",
    ]
}

variable "number_of_subnets_required" {
    description = "Number of subnets required"
    type    = number
    default = 2
}

Values can be retrieved from list variables by index or using the slice() function:


output "third_private_subnet_block" {
    value = var.private_subnet_cidr_blocks[2]
}

output "first_three_subnets" {
    value = slice(var.private_subnet_cidr_blocks, 0, 3)
}

output "selected_subnets" {
    value = slice(var.private_subnet_cidr_blocks, 0, var.number_of_subnets_required)
}

Map Variables

Map variables are collection types, and contain key-value pairs. The values should adhere to the declared datatype. Each key should be unique, where the keys and values are of type string:

variable "res_tags" {
  type       = map(string)
  default = {
    location   = "UK West"
    department = "Finance"
    owner      = "Fred Perry"
  }
}

output "this_department" {
  value = var.res_tags["department"]
}

Object Variables

Object variables are collection types, with key-value pairs where the values for each key can be of different types:

variable "user_account" {
  type = object({
    logon    = string
    email    = string
    roles    = list(string)
    projects = map(string)
    enabled  = bool
  })
  default = {
    logon = "fred001"
    email = "fred@example.com"
    roles = ["user", "report writer", "remote access", "acr pull"]
    projects = {
      primary = "solaris"
      second  = "aix"
      third   = "rhel"
    }
    enabled = true
  }
}

output "user_details" {
  value = var.user_account.logon
}

output "main_project" {
  value = var.user_account.projects.primary
}

Set Variables

Sets are collections that can contain only unique values of the same datatype:

# Although the value "two" is included twice in the default assignment
# the value "two" will only occur once in the actual set variable
variable "myset" {
  type    = set(string)
  default = ["one", "two", "three", "four", "two"]
}

output "set_members" {
  value = var.myset
}

Elements of a set are identified only by their value and don't have any index or key to select with, so it is only possible to perform operations across all elements of the set

Tuple Variables

Tuples are similar to lists but each value in the list can be of a different type:

variable "user_info" {
  type    = tuple([string, bool, number])
  default = ["Finance", true, 1]
}

output "tuple_values" {
  value = var.user_info
}

output "user_enabled" {
  value = var.user_info[1]
}

Variable Validation

Validation blocks can be added to variable declaration to enforce any required rules and display error messages if rules are broken. Functions like length() and regexall() can be useful for string validation.


variable "res_tags" {
...

    validation {
        condition = length(var.res_tags.["project"]) < 16 && length(regexall("[^a-zA-Z0-9]", var.res_tags.["project"])) == 0
        error_message = "Project tags must be less than 16 characters and only contain numbers or letters"
    }
...
}

Protecting Sensitive Values

Variables can be assigned a 'sensitive' attribute to flag whether the value should be displayed in the output from Terraform commands. If you create an output that includes the value of a variable flagged as sensitive, then this will cause terraform commands to fail, unless you also flag the output as sensitive.

However, the terraform state file is stored as plain text, and even sensitive values are stored as plain text so that terraform can read them to see if they have been changed. Therefore you should ensure that your terraform state file is kept secure at all times.