How to Use Variables in Terraform Code with Examples

In this tutorial, we will explain how to use variables in Terraform code with examples.

Hard-coding values in Terraform’s main configuration file is not a good idea since this prevents the re-usability of Terraform configuration files. This defeats the goal of IaC which is to enhance code re-usability.

Variables are an excellent way of defining values. Generally, Terraform variables are declared independently in a separate file called ‘variables.tf‘ file. This makes it easy to read and modify the values from a single file, which in turn enhances the reusability of code.

Use Variables in Terraform Code

Variables in Terraform can be broadly categorized into two: input and output variables. Input variables define values used to deploy resources on your infrastructure. In contrast, output variables display information about the infrastructure after deployment. This helps display crucial information such as IP addresses, names, IDs, etc.

Input Variables in Terraform

As a developer, you want to ensure that the same code can be used multiple times based on variables provided during runtime. And this is where input variables come into the picture.

Input variables, sometimes referred to as just “Terraform variables” or “variables’ allow you to customize or update certain aspects of the Terraform main configuration file without altering the code itself. This lets you share the configuration file across various Terraform configurations thus allowing code to become reusable and flexible.

Consider the configuration file below that creates a file called /root/dogs.txt in the root home directory containing the string “We love dogs”.

resource "local_file" "dogs" {
  filename = "/root/dogs.txt"
  content  = "Dogs are cute animals"
}

As you can see, the filename and content values have been hard-coded into the configuration file.

To make the configuration reusable, create a `variables.tf` file. As you can see the variable name is specified after the variable keyword while the default parameter takes the variable’s values.

variable "filename" {
  default = "/root/dogs.txt"
}
variable "content" {
  default = "We love dogs"
}

Back to the `main.tf` configuration file, replace the arguments with the variable names prefixed with the `var.` prefix. The var.filename and var.content values reference the variable arguments specified in the `variables.tf` file.

resource "local_file" "dogs" {
  filename = var.filename
  content  = var.content
}

From here, you can run the ‘terraform plan‘ and ‘terraform apply‘ commands to create the resources. If you want to make any changes to the resources, you can simply update the  `variables.tf` file without touching the `main.tf` file.

Below is an example of a Terraform main.tf file that can be used to deploy an EC2 instance.

provider "aws" {
  access_key = var.access_key
  secret_key = var.secret_key
  region = var.region
}

resource "ami_instance" "webserver" {
  ami = var.ami
  instance_type = var.instance_type
}

Use Variables in Terraform

And here is the `variables.tf` file.

variable "access_key" {
  default = "xxxxxxxxxxxxxxxxx"
}
variable "secret_key" {
  default = "xxxxxxxxxxxxxxxxx"
}

variable "region" {
  default = "us-east-2"
}

variable "ami" {
  default = "ami-0fadb6fnjfab793387"
}

variable "instance_type" {
  default = t2.micro
}

Input Variables in Terraform

You can see that the values are not hard-coded on the main configuration file. Instead, the values are specified in the `variables.tf` file from where the `main.tf` file references them.

Output Variables in Terraform

Output variables are used to store the value of an expression and print it out to stdout.

Consider the `main.tf` terraform file below which creates a random pet name. The dog-name output variable is defined right at the end of the configuration file. This will display the name of the pet contained in he created local file.

resource "local_file" "dogs" {
  filename = var.filename
  content  = "My favorite dog id ${random_pet.my-dog.id}"
}

resource "random_pet" "my-dog" {
  prefix = var.prefix
  separator = var.separator
}

output dog-name {
  value  =  random_pet.my-dog.id
  description = "Record the value of dog ID generated by random_dog resource"
}

NOTE:

Terraform provides the ‘random’ provider which supports the generation of random values within Terraform configurations. You can use the ‘random’  provider to generate resources such as a random id, pet name, password and shuffle (random permutation of a list of strings), string or uuid.

In the `man.tf` file, the random_pet resource is used to generate a random pet name, and the value is stored in the output variable called dog-name.

Here is the `variables.tf` file.

variable "filename" {
  default = "/root/dogs.txt"
}

variable "content" {
  default = "We love dogs"
}

variable "prefix" {
  default = "Mr"
}

variable "separator" {
  default = "."
}

After running the `terraform apply` command, the value of the name of the pet will be displayed. In this case, the pet is called “Mr.precise.ewe”

Out Variables in Terraform

Arguments in Variable Declarations

Let’s look at the various arguments that a variable block uses. The variable block accepts the following parameters:

default  – The first parameter that we have already used is the default parameter. The parameter specifies the default value of a variable.

In the Terraform code block below, the parameter specifies the file’s location and content.

variable "filename" {
  default = "/root/dogs.txt"
}
variable "content" {
  default = "We love dogs"
}

type  – This argument enforces the type of variable that can be used or accepted by the variable. The variable types that can be used with this argument include string, number, bool, and any.

  • The string type accepts alphanumeric values – alphabets and numbers.
  • The number type accepts a single integer which can be positive or negative.
  • The bool type accepts True or False values.

When not specified in the variable block, the type parameter is said to be of any by default.

In the code example below, the `type` argument enforces the file name to be a string value.

variable "filename" {
  default = "/root/dogs.txt"
  type = string
}

description  – This is an optional argument that describes the input variable’s documentation. Although optional, it is recommended to use as it provides more information about a variable. Consider the code block below.

variable "filename" {
  default = "/root/dogs.txt"
  type = string
  description = "path of the local file"
}

The description argument gives us more information about the variable.

These are the commonly used variable arguments. Check out more on the Hashicorp Variable arguments section.

Complex Type Constraints

Aside from string, number, and bool, terraform also supports other types such as lists, maps, tuples, and objects. Let’s start with each of these.

Lists : A list is a collection of ordered or numbered elements. Each of the elements can be referenced using indexes.

A list can be presented as shown. The default parameter contains a list of three elements: “Mr”, “Mrs”, and “Miss”. The first element in the list at index 0 is the string “Mr”. The second element at index 1 is “Mrs” and lastly, the last element at index 2 is “Miss”

variable "prefix" {
  type = list
  default = ["Mr", "Mrs", "Miss"]
}

These elements can easily be accessed within the main configuration within square brackets as shown. Here, we are accessing the second element which is “Mrs”.

resource "local_file" "dogs" {
  prefix = var.prefix[1]
}

In addition, we can also combine type constraints. For example, if you want a list of string elements, you can declare it as follows.

variable "prefix" {
  default = ["Mr", "Mrs", "Miss"]
  type = list(string)
}

If you want a  list of number elements, declare it as follows.

variable "students" {
  default = [1, 2, 3]
  type = list(number)
}

Sets :  The difference between sets and lists is that sets take unique values and not duplicate ones.  Below is an example of a set that uses strings.

variable "fruits" {
  default = ["Mango", "Orange", "Melon", "Peach" ]
  type = set(string)
}

The following code will fail due to duplicate values.

variable "fruits" {
  default = ["Mango", "Orange", "Melon", "Mango" ]
  type = set(string)
}

Maps:  A map is a data structure that presents data in the form of key-value pairs. Consider the `variables.tf` file below. The default parameter takes two key-value pairs enclosed in curly braces. “statement1” and “statement12” are keys while “We love dogs” and “Dogs are great pets” are values.

variable "file-content" {
  type = map

  default = {
    "statement1" = "We love dogs"
    "statement2" = "Dogs are great pets"
}

To access a specific value within the map from the main configuration file, we use key-matching where the key matching the value required is specified within square brackets.

In the example below, the key “statement2” specifies the value “Dogs are great pets” which is to be written on the file to be created.

resource "local_file" "dogs" {
  filename = "/root/dogs.txt"
  content = var.file-content["statement2"]
}

Just like lists, you can use type conversion with maps to ensure values are of a specific type. Let’s take a new example. In the code block shown,  the map enforces string values for the value elements.

variable "dogs" {
  type = map(string)
  default = {
    "color" = "brown"
    "name"  = "molly"
}
}

In this example,  we have a map that uses number values.

variable "dog-breeds" {
  type = map(number)
  default = {
    "husky" = "10"
    "corgi"  = "7"
    "poodle" = "3"
}
}

Objects:  You can create complex structures using all the variable types we have covered so far. Here is an example of a variable name of a dog called “pat”. The dog has various attributes such as color, age, food, and cuteness. We use the `object` variable type to specify all those attributes under one umbrella as shown.

variable "pat"
  type = object({
    color = string
    age = number
    food = list(string)
    is_cute = bool
   })

    default = {
      color = "brown"
      age = 5
      food = ["chicken", "turkey", "beef"]
      is_cute = true
}

Tuples:  The difference between a tuple and a list is that a list uses elements of the same variable type such as string or numbers. With tuples, we can have elements of various variable types.

In this example, we have three types of elements defined inside a tuple: string, number, and boolean. The variables passed should be exactly three in number and of the same variable type.

variable "doggo" {
  type =  tuple([string, number, bool])
  default = ["dog", 6, true]
}

In the above example, we gave passed the value of “dog” to the ‘string’ element, 6 to the ‘number’ element and finally true to the ‘bool’ element.

Conclusion

That’s all for this lecture. We have covered variables in Terraform and how they are used in Terraform configuration files. For more information about Terraform variables, check out the Variables documentation.

Also Read : How to Install Terraform on RHEL 9 | Rocky Linux 9 | AlmaLinux 9

Leave a Comment