Ante Miličević
January 4, 2024

How to Manage Terraform Variables

Today, we'll teach you how to manage Terraform variables by ordering a Domino's pizza.

With the help of Terraform, a potent infrastructure as code (IaC) tool, you can define and manage your cloud infrastructure. Using variables to parameterize and increase the flexibility and reusability of your setups is one of Terraform's primary advantages.

Were you aware that Terraform can also be used to order Domino's Pizza? In this article, we'll explore Terraform variables and have some fun with the Domino's Pizza Terraform. We'll help you learn how to apply them in your infrastructure deployments efficiently.

Requirements: A GitHub account is necessary (all hands-on sessions will use GitHub's Codespaces, so you won't need to install anything on your PC). You can find the repo here.

What are Terraform Variables?

Terraform variables are used to modify modules and resources without changing their source code. They make it possible for you to specify and manage reusable values in Terraform configurations, making it easier to respond to changes and maintain consistency across several environments.

Importance of Terraform Variables

The use of Terraform variables is important for managing big infrastructure settings since it avoids hardcoding values and makes updating and maintaining your configurations easier. Variables also allow for code reuse and the creation of modular, scalable, and maintainable infrastructure.

Types of Terraform Variables

The versatility of Terraform types make them very adaptable.

Each kind has a distinct function:

  • String: This is the most basic type of Terraform variables. It's used for text and is rather simple. For instance, one might opt for a string as the designation for a server or label.
  • Number: This type, as its name implies, serves to represent numerical values. Numbers are handy for specifying the number of VMs or creating a size parameter.
  • Bool: Boolean is short for true or false. This is used for toggle controls, such as toggling on/off a feature in your settings.
  • List: Consider this as an array. Lists have the capability of holding a vast amount of uniform values within their structure. Consequently, should one possess a roster of either IP addresses or usernames, lists are what you will reach for.
  • Map: A collection of key-value pairs is what comprises this map. Maps are ideal for associating values with unique keys, such as for building up environment-specific variables.
  • Object: Objects, which are slightly more sophisticated, allow you to build a structure with so many fields of different kinds. It's similar to designing a new data type tailored to your specific requirements.

Default Values

Setting default settings can make your Terraform scripts error-free and user-friendly. So, why use default values?

  • They make your configurations easier to comprehend. Setting a common value as the default might save time and avoid repeating inputs.
  • How do you set them? Simply define the default within the variable declaration. If a user does not give a value, Terraform will silently utilize the default.
  • What about optional variables? Some variables become optional when default values are used. If users wish to customize the presets, they have the capacity to specify alternative configurations to replace the usual standard options.
  • Input prompting? If no default is set and a value is wanted, Terraform will prompt you for it. This ensures that your setup won't run with missing pieces.

In the following example, you can see how to define defaults for an instance type, a logging flag, and a list of server ports:

<pre class="codeWrap"><code>variable "instance_type" {
 type    = string
 default = "t2.micro"  # A common default for AWS EC2 instances
}

variable "enable_logging" {
 type    = bool
 default = true  # Defaulting to having logging enabled
}

variable "server_ports" {
 type    = list(number)
 default = [80, 443]  # Defaulting to standard HTTP and HTTPS ports
}
</code></pre>

Defining Variables and Assigning Values

The variable block in Terraform configuration files is used to define variables. Within this block, one finds out a variable's designation, its type, what value will be assumed if none is given, and other important qualities.You tell Terraform what inputs to expect during deployment by creating variables. While variable blocks can be defined in any Terraform configuration file, the standard practice is to define values in a separate variable.tf file within your Terraform project.

Variable Blocks: Syntax

A variable block's syntax includes the variable keyword, the variable name, and a collection of attributes contained in curly braces. Variable types, default values, descriptions, and validation rules are examples of attributes.

<pre class="codeWrap"><code>variable "variable_name" {
 type        = "variable_type"
 default     = "default_value"
 description = "variable_description"
 validation {
   condition = var.variable_name > 0
   error_message = "Variable must be greater than zero."
 }
}
</code></pre>

Variable Descriptions

You can add more context and information about your variable's purpose by adding a description. The description is optional and can be included within the variable block. For instance:

<pre class="codeWrap"><code>variable "order" {
 type        = bool
 description = "Whether to order a pizza or not"
 default     = false
}
</code></pre>

Assigning Variable Values

To configure the values for your variables in Terraform, you use a file called terraform.tfvars. Terraform picks up these settings automatically when you perform commands like apply or plan. Put this file in the Terraform project's main folder. You list your variables and their values therein, as shown below:

<pre class="codeWrap"><code>variable_name = "variable_value"</code></pre>

Practical example: 

<pre class="codeWrap"><code>first_name   = "John"
last_name    = "Smith"
</code></pre>

When Terraform executes its configuration, it will utilize the explicitly defined values of "John" for the first_name variable and "Smith" for the last_name variable, as illustrated in this example.

Furthermore, the terraform.tfvars file should be added to your.gitignore file so that it is not checked into git. The rationale for this is that sensitive values are often defined in this file. An example terraform.tfvars.sample file could show the format and content of the terraform.tfvars file without including any private information for security purposes.This could act as a model for users who would use your programming.

Terraform Variables in the Configuration

To use variables in Terraform setups, use the syntax var.variable_name. As an illustration, if one has a variable named street, it can be accessed in the setup portion through var.street, demonstrated as follows:

<pre class="codeWrap"><code>data "dominos_address" "addr" {
  street      = var.street
}
</code></pre>

Using Built-in-Functions

Terraform's diverse array of intrinsic functions allows for the flexible manipulation and changing of variable contents in nuanced ways. These functions empower users to perform a wide variety of tasks through string, mathematical and logical manipulations while also providing additional capabilities. Lookup, merge, element, length, keys, and values are some commom functions.

Conditional Expressions

Terraform conditional expressions let you pick between two values based on a condition. A conditional expression's syntax is:

<pre class="codeWrap"><code>condition ? true_value : false_value</code></pre>

For example:

<pre class="codeWrap"><code>variable "order" {
 type        = bool
 description = "Whether to order a pizza or not"
 default     = false
}

locals {
 should_order = var.order ? 1 : 0
}

resource "dominos_order" "order" {
 count      = local.should_order
...
}
</code></pre>

The Terraform code sample above demonstrates a variable named order of type bool. It gives the order whether or not to order a pizza. The variable's description outlines its intended function and a default of false will be assumed if an explicit value fails to be produced.The code also shows a local variable named should_order, which is holding onto the value of the order variable. If the order variable is true, the should_order variable is set to '1'. Otherwise, failing to spot a value would result in it defaulting to zero. This local variable is used to conditionally build the dominos_order resource.

Working with Lists and Maps

Terraform variables support lists and maps, which are very handy when dealing with many values or key-value pairs. Lists permit you to store and iterate over many elements, whereas maps permit you to correlate values with particular keys.

Take a look at this example:

<pre class="codeWrap"><code>Example of a List
variable "menu_item" {
 type        = list(string)
 description = "A list of the items to order from the menu"
 default     = ["philly", "medium", "thin"]
}

#Example of a Map
variable "credit_card" {
 type        = map(string)
 description = "Credit Card Info"
 sensitive   = true
 default     = {
   number      = 123456789101112
   cvv         = 1314
   date        = "15/16"
   postal_code = "18192"
 }
}
</code></pre>

You would then use the var.variable syntax to refer to the list and map variables.

Terraform Environments Variables

Terraform environment variables are used to shift many elements of Terraform's functionality, such as logging, input handling, and backend settings. They can also be used to set values for input variables by using the TF_VAR_ prefix followed by the variable name.

How to Set Terraform Environment Variables Values

To change the value of a Terraform environment variable, you can export command on Unix/Linux or set command on Windows. For instance, in Linux, you would type:

<pre class="codeWrap"><code>export TF_VAR_first_name="Sam"</code></pre>

Input Variables

Input variables are provided whever you are running Terraform commands. They allow users to enter values interactively or through command-line arguments.

Local Variables

Local variables are variables that are calculated dependent on other variables or expressions. Local variables can help with difficult calculations or simplify configuration files by removing redundancy. These are examples of how to use local variables:

<pre class="codeWrap"><code>locals {
 customer_info = {
   first_name    = var.first_name
   last_name     = var.last_name
   email_address = "${var.first_name}.${var.last_name}@company.com"
   phone_number  = var.phone_number
 }
}

provider "dominos" {
 first_name    = local.customer_info.first_name
 last_name     = local.customer_info.last_name
 email_address = local.customer_info.email_address
 phone_number  = local.customer_info.phone_number
 credit_card   = var.credit_card
}
</code></pre>

In this example, a local variable called customer_info is shown. This variable is of a type map and includes many key-value pairs as string values.

The customer_info map contains the following key-value pairs:

  • The input variable's value has been assigned to set the value. The values assigned to the variables represent one's first name.
  • Last names are equal to what has been supplied as the input information. His last name was referenced.
  • By combining the input variables containing a person's first and last name separated by a dot, followed by @company.com, this process derives an email address in the customary format for that organization.
  • phone_number: The value is set to the value of the var.phone_number.

Output Variables

Output variables are used to export values from your Terraform setup for usage in other configurations or show in CLI output. They are normally defined in outputs.tf file and are defined with the output block. An output variable for our pizza order is shown below:

<pre class="codeWrap"><code>output "OrderOutput" {
 value = data.dominos_menu_item.item.matches[*]
 description = "My order"
}
</code></pre>

Variable Precedence

When selecting the value of a variable, Terraform follows a strict precedence. If the same is assigned numerous values, Terraform will utilize the value with the highest precedence, overriding any other values.

The precedence order is listed here, from highest to lowest priority:

  • Variables of the environment TF_VAR_variable_name
  • The terraform.tfvars file
  • The terraform.tfvars.json file
  • Any.auto.tfvars or.auto.tfvars.json files, handled in the lexical order of their filenames.
  • Any command line arguments specifying -var or -var-file options will be processed sequentially in the order provided.
  • Variable defaults

Variable Validation

You can apply validation criteria to your variables by utilizing the validation block within the variable definition. This enables you to impose restrictions on the values that can be set to a certain variable name.

<pre class="codeWrap"><code>variable "menu_item" {
 type        = list(string)
 description = "A list of the items to order from the menu"
 validation {
   condition     = contains(var.menu_item, "thin")
   error_message = "You must order a 'thin' pizza crust since it's our team's favorite"
 }
}
</code></pre>

The preceding sample code contains a validation block that enforces a specified condition on the menu_item variable value.

The following components are included in the validation block:

  • Condition: For a variable's value to be deemed valid, this element stipulates the prerequisite that necessitates fulfillment. The criteria are defined in this case as contains(var.menu_item, "thin"). It looks for the string "thin" in the menu item list. The validation is successful if this criterion is met. Should the validation criteria go unfulfilled, this element will demonstrate an error.
  • The error message in this example is "You must order a 'thin' pizza crust because it's our team's favorite." The error message is shown if the "thin" element is not included in the menu_item list variable

<pre class="codeWrap"><code>menu_item    = ["philly", "medium"]</code></pre>

Image of error ordering a thin pizza

Security: Sensitive Variables

Terraform lets you mark variables as sensitive, which prevents their values from appearing in the CLI output. Add the sensitive property to the variable declaration to make it sensitive. An innovative approach to utilizing our credit cards could be explored in the following example.

<pre class="codeWrap"><code># variable definition contained in the variables.tf file
variable "credit_card" {
 type        = map(string)
 description = "Credit Card Info"
 sensitive   = true
}
# variable assignment contained in the terraform.tfvars file
credit_card = {
 number      = 123456789101112
 cvv         = 1314
 date        = "15/16"
 postal_code = "18192"
}
</code></pre>

Normally, you would not check the terraform.tfvars file into git because it includes sensitive information, as previously stated.

If you try to output the credit card variable using this output block:

<pre class="codeWrap"><code>output "credit_card" {
 value = var.credit_card
 description = "Credit Card Info"
}
</code></pre>

You will get an error message, since it cannot be done.

Image of error message in git

Updating the output variable is necessary for it to work and it turns out to be like this:

<pre class="codeWrap"><code>output "credit_card" {
 value = var.credit_card
 description = "Credit Card Info"
 sensitive = true
}
</code></pre>

When you run terraform apply now, you are unlikely to get an error, and the output variable credit_card is going to show as sensitive,

Image of apply complete credit card shown as sensitive

State File Shows Sensitive Data

Although the credit card output is not shown, sensitive data is still stored in the Terraform state file and must be carefully safeguarded. You can use an S3 bucket as a remote backend that is secured at rest, or you can use a full-fledged Terraform Automation & Collaboration Software (TACoS) solution like env0.

The following credit card information is contained in the state file:

Image of credit card shown as sensitive

Full Example

Let's look at the complete example of ordering a Domino's Pizza. The files necessary to initiate this are contained in the following section. To order a pizza, use the Terraform commands below. However, be cautious when entering your actual credit card information because the system will purchase a pizza for you!

The following commands should be executed in the proper sequence:

<pre class="codeWrap"><code>terraform init
terraform apply
</code></pre>

The variable order is by default set to false. This prevents the resource dominos_order from placing a pizza order. Set this variable to true if you intend to order a pizza with Terraform.

The main.tf File

<pre class="codeWrap"><code> terraform {
 required_providers {
   dominos = {
     source  = "MNThomson/dominos"
     version = "0.2.1"
  }
 }
}

provider "dominos" {
 first_name    = local.customer_info.first_name
 last_name     = local.customer_info.last_name
 email_address = local.customer_info.email_address
 phone_number  = local.customer_info.phone_number
 credit_card   = var.credit_card
}

data "dominos_address" "addr" {
 street      = var.street
 city        = var.city
 region      = var.region
 postal_code = var.postal_code
}

data "dominos_store" "store" {
 address_url_object = data.dominos_address.addr.url_object
}

data "dominos_menu_item" "item" {
 store_id     = data.dominos_store.store.store_id  query_string = var.menu_item
}

resource "dominos_order" "order" {
 count      = local.should_order
 api_object = data.dominos_address.addr.api_object
 store_id   = data.dominos_store.store.store_id
 item_codes = data.dominos_menu_item.item.matches[*].code
}
</code></pre>

The variables.tf file

<pre class="codeWrap"><code>variable "first_name" {
 type        = string
 description = "Customer's first name"
}

variable "last_name" {
 type        = string
 description = "Customer's last name"
}

variable "phone_number" {
 type        = string
 description = "Customer's phone number"
}

variable "street" {
 type        = string
 description = "Street address of the Store"
}

variable "city" {
 type        = string
 description = "City where the store is located"
}

variable "region" {
 type        = string
 description = "State or Province where the store is located"
}

variable "postal_code" {
 type        = string
 description = "Postal code of the Store"
}

variable "order" {
 type        = bool
 description = "Whether to order a pizza or not"  default     = false
}

variable "menu_item" {
 type        = list(string)
 description = "A list of the items to order from the menu"
 validation {
   condition     = contains(var.menu_item, "thin")
   error_message = "You must order a 'thin' pizza crust since it's our team's favorite"
 }
}

variable "credit_card" {
 type        = map(string)
 description = "Credit Card Info"  sensitive   = true
}

locals {
 customer_info = {
   first_name    = var.first_name
   last_name     = var.last_name
   email_address = "${var.first_name}.${var.last_name}@company.com"
   phone_number  = var.phone_number
 }
 should_order = var.order ? 1 : 0
}
</code></pre>

The terraform.tfvars File

<pre class="codeWrap"><code>credit_card = {
 number      = 123456789101112
 cvv         = 1314
 date        = "15/16"
 postal_code = "18192"
}
</code></pre>

The more_variables.auto.tfvars File

<pre class="codeWrap"><code>first_name   = "Sam"
last_name    = "Gabrail"
phone_number = "15555555555"
street       = "123 Main St"
city         = "Anytown"
region       = "WA"
postal_code  = "02122"
menu_item    = ["philly", "medium", "thin"]
</code></pre>

The outputs.tf File

<pre class="codeWrap"><code>output "OrderOutput" {
 value = data.dominos_menu_item.item.matches[*]
 description = "My order"
}

output "credit_card" {
 value = var.credit_card
 description = "Credit Card Info"
 sensitive = true
}
</code></pre>

Our Best Tips

  • To improve code readability, use a consistent variable name convention and detailed variable descriptions.
  • Variable declarations ought to be saved in a separate variable.tf file.
  • For optional variables, establish reasonable default values.
  • To simplify your structures and carry out complicated procedures, consider using local variables and built-in functions.
  • Validation rules can be used to set boundaries on variable values.
  • In order to prevent unforeseen exposure, identify sensitive variables as such.
  • Think about using a TACoS (Terraform Automation and Collaboration Software) like env0 to securely store and manage your sensitive parameters in an ordered manner.

Advanced Terraform Variable Scenarios

Terraform accepts complex data types as variables, such as objects and tuples. These can be used in your settings to store and manipulate more complicated data structures.

Key Takeaways

  • Variables in Terraform are important for handling big infrastructure configurations and ensuring stability across regions.
  • Command-line flags, variables related to the environment, and.tfvars files are all ways to link variables.
  • In order to have more flexible and powerful applications, use built-in functions, locals, conditional expressions, and advanced data types.
  • To offer a reliable and secure infrastructure, use recommendations for naming, organizing, and securing your variables.

Facing Challenges in Cloud, DevOps, or Security?
Let’s tackle them together!

get free consultation sessions

In case you prefer e-mail first:

Thank you! Your message has been received!
We will contact you shortly.
Oops! Something went wrong while submitting the form.
By clicking “Accept”, you agree to the storing of cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts. View our Privacy Policy for more information. If you wish to disable storing cookies, click here.