Skip to main content

Variables

·1848 words·9 mins
Terraform - This article is part of a series.
Part 6: This Article

Variables in Terraform represent containers for values coming from the outside world. The outside world in this case is usually the command line, or a deployment pipeline. Variables could also be part of a module (which we will cover later) and values for the variables could be provided from another module.

A schematic view of what variables are is shown in the figure below.

UserTheOutsiVVVVVdaaaaaellllluuuuuWeeeeeorl-d----------VaVrairaibalTbeelreraformVCaorniVfaaibrgliuearbalteionVariable

In this lesson I begin to go through part 8 of the Certified Terraform Associate exam curriculum. This part of the curriculum is outlined below:

PartContent
8Read, generate, and modify configuration
(a)Demonstrate use of variables and outputs
(b)Describe secure secret injection best practice
(c)Understand the use of collection and structural types
(d)Create and differentiate resource and data configuration
(e)Use resource addressing and resource parameters to connect resources together
(f)Use HCL and Terraform functions to write configuration
(g)Describe built-in dependency management (order of execution based)

To be specific: I will cover parts of 8 (a) in this lesson, i.e. Demonstrate use of variables. We also briefly touch on the subjects in 8 (b) and (c).

Declaring a variable
#

In Terraform a variable is declared using a variable block1. The general format of a variable block is this:

variable "label" {
  type        = <type>
  default     = <default value>
  description = <description>
  sensitive   = boolean
  nullable    = boolean

  validation { ... }
}

The variable block has one label. The label is the symbolic name of the variable, it must be unique within a given module2. We can use the symbolic name of the variable to refer to it from elsewhere in our Terraform configuration.

The arguments in the variable block are explained next:

  • type restricts the possible values for this variable to a given type. The basic types available are string, number, and bool. It is also possible to specify more advanced types such as list(string), set(number), etc. If no type is specified then any type will be accepted.
  • default is a provided default value for this variable if no other value is provided at the time that Terraform runs this configuration.
  • description is a friendly string explaining what this variable is used for.
  • sensitive is a boolean (true or false, default is false) that specifies if Terraform should consider this value to be sensitive, like a password. If true, Terraform will mask this value in any output it produces.
  • nullable is a boolean (true or false, default is true) that specifies if this variable is allowed to be null.

Apart from these arguments you can also specify validation blocks nested inside of variable blocks. A validation block is used to validate the provided value. A simple example of a validation block that checks that the length of a string variable is less than 10 characters looks like this:

variable "my_string" {
  type = string

  validation {
    condition     = length(var.my_string) < 10
    error_message = "my_string should be less than 10 characters long"
  }
}

Let us see a few more examples of how we can declare a variable. In the next example I declare a variable of type number that should be an even number:

variable "even_number" {
  type        = number
  description = "Provide an even number"
  default     = 2
  nullable    = false

  validation {
    condition     = var.even_number % 2 == 0
    error_message = "even_number should be an even number!"
  }
}

The next example shows a variable that should be a list of strings and the number of elements should be at least three:

variable "names" {
  type        = list(string)
  description = "Provide a list of three or more names"
  default     = ["agnetha", "anni-frid", "björn", "benny"]
  nullable    = false

  validation {
    condition     = length(var.names) >= 3
    error_message = "You should provide at least three names"
  }
}

Using a variable in Terraform
#

Once we have declared a variable we can refer to it by var.<NAME>, where <NAME> is replaced by the symbolic name of the variable (the label on the variable block). An example of where I define a simple string variable and use it in a resource looks like this:

variable "name" {
  type = string
}

resource "local_file" "my_file" {
  filename = var.name
  // ...the rest of the resource omitted
}

We can also use variables in string interpolation. String interpolation means we include one or more variables inside of a string. We do this using ${} inside of a string, like so:

variable "name" {
  type = string
}

resource "local_file" "my_file" {
  filename = "prefix-${var.name}-postfix"
  // ...the rest of the resource omitted
}

Variables may be used almost anywhere. There are some exceptions:

  • you can’t use a variable in the label of a Terraform block
  • you can’t use variables inside of other variable definitions
  • you can’t use variables inside of backend blocks3

These are the exceptions I could think of right now, but there might be more. If you try to use a variable where it is not supported you will get an error message stating this.

Providing a value for a variable
#

We have declared our variables and we have used them in our Terraform configuration. How do we provide values to them? There are a few different ways.

Default values
#

We already saw that in a variable block we can specify the default argument with a value that should be the default value if no other value is provided. It makes sense to put a good default value here that is sufficient most of the time, then you only need to provide a different value to the variable if you have a specific need to do so.

Imagine I create a Kubernetes cluster using Terraform and I want the number of worker nodes in my cluster (the number of virtual machines) to be 10, except in rare cases where I want something else. In this case it would make sense to define a variable like this:

variable "number_of_worker_nodes" {
  type    = number
  default = 10
}

Command line
#

In the previous lesson I went through the core Terraform workflow, and we saw the terraform plan and terraform apply commands. We can provide values for our variables in these commands. Imagine that we have the following variable declared in our Terraform configuration:

variable "my_string" {
  type = string
}

variable "my_number" {
  type = number
}

variable "my_bool" {
  type = bool
}

I can run terraform plan and provide values to my variables using the -var flag:

$ terraform plan -var "my_string=hello" -var "my_number=42" -var "my_bool=true"

Likewise I can provide them to terraform apply:

$ terraform plan -var "my_string=hello" -var "my_number=42" -var "my_bool=true"

Variable definition files
#

If you have many variables it is tedious to set them using -var flags for terraform plan and terraform apply. Instead you can use variable definition files. This is a file with a file-ending of .tfvars or .tfvars.json (in my experience the JSON-kind is rarely used, but it is good to know that it is possible to use it).

A basic variable definition file looks like this:

variable_1 = "value 1"
variable_2 = 10000
variable_3 = [
  "list_item_1",
  "list_item_2"
]

It is basically a list of variable assignments.

To use a variable definition file you can explicitly provide it to terraform plan and terraform apply:

$ terraform plan -var-file="variables.tfvars"
$ terraform apply -var-file="variables.tfvars"

You could include several variable definition files by using the -var-file flag more than once.

You can let Terraform automatically include your variable definition files by naming your file exactly terraform.tfvars or any name ending with .auto.tfvars, e.g. dev.auto.tfvars.

Environment variables
#

In automation scenarios I have found it to be beneficial to be able to set values for variables using environment variables.

If I have the following variable declared in Terraform:

variable "my_variable" {
  type = string
}

I can define an environment variable to match this variable. The name of the environment variable must start with TF_VAR_ and be followed by the name of the variable, like this:

$ export TF_VAR_my_variable=my_value

The following terraform plan and terraform apply commands will automatically use this environment variable.

Variable definition precedence
#

With so many ways of providing values for variables keep this precedence order in mind:

  1. Environment variables are read first (i.e. all TF_VAR_* environment variables)
  2. The file named terraform.tfvars is read next, if it exists (followed by terraform.tfvars.json if it exists)
  3. All files named *.auto.tfvars sorted by names (*.auto.tfvars.json are also included)
  4. All explicitly provided variable values with -var or variable definition files with -var-file, in the order that they are provided

This means that if I have a variable named my_variable I could define a value for it as an environment variable TF_VAR_my_variable, I could include it in terraform.tfvars, I could include it in myvars.auto.tfvars, and I could provide it with -var "my_variable=my_value". What would happen then? In this case the -var "my_variable=my_value" would take precedence because it is the last method to be evaluated.

Summary
#

We now know all we need to know about variables to be able to handle variable-related questions in the certification exam! This lesson covered everything I have ever needed to know about variables when using Terraform in production scenarios as well. A quick summary of what we did:

  • We saw how to declare a variable in our Terraform configuration and went through all the arguments we can specify
  • We went through how we refer to a variable from other parts of our Terraform configuration with var.<NAME>, e.g. var.my_variable
  • We saw how to provide values for variables using the Terraform CLI
    • terraform plan -var "variable_1=value" -var "variable_2=value"
    • terraform apply -var "variable_1=value" -var "variable_2=value"
  • We saw how we can easily provide values for several variables at once using variable definition files, i.e. files with names ending in .tfvars
    • terraform plan -var-file "dev.tfvars"
    • terraform apply -var-file "dev.tfvars"
  • We saw how we can provide values for variables using environment variables like TF_VAR_my_variable
  • We learned about the precedence order of variable definitions:
    1. environment variables
    2. terraform.tfvars
    3. *.auto.tfvars
    4. -var and -var-file flags

  1. Remember that in HCL there are two main concepts: blocks and arguments. To refresh your memory of these concepts see the lesson on providers where we first encountered these concepts. ↩︎

  2. Again, we cover modules in the future but for now think of a module as a given scope where names of things must be unique. ↩︎

  3. We will cover backends in a future lesson. ↩︎

Mattias Fjellström
Author
Mattias Fjellström
Cloud architect · Author · HashiCorp Ambassador
Terraform - This article is part of a series.
Part 6: This Article