
Sam Gabrail
President at TeKanAid
Terraform is a powerful infrastructure as code (IaC) tool that enables you to define and manage your cloud infrastructure in a declarative manner. One of the key features of Terraform is the ability to use variables, which allow you to parameterize your configurations and make them more flexible and reusable.
Did you know that you could also order Domino's Pizza with Terraform? In this blog post, we will have fun with the Domino's Pizza Terraform provider while exploring the world of Terraform variables. We'll understand how to effectively use them in your infrastructure deployments.
Let’s take a look at our setup.
TL;DR: You can find the repo here.
Terraform variables are used to customize modules and resources without altering their source code. They allow you to define and manage reusable values in your Terraform configurations, making it easier to adapt to changes and maintain consistency across different environments.
Using Terraform variables is essential for managing large infrastructure configurations, as it helps avoid hardcoding values and makes it easier to update and maintain your configurations. Variables also enable you to reuse code and create modular, scalable, and maintainable infrastructure.
Terraform supports several variable types such as:
You can also set default values for variables, making them optional. If no value is provided for a variable, Terraform will use the default value if it's available, otherwise will prompt the user to enter a value.
To define variables in Terraform, you use the variable block within your configuration files. This block specifies the name, type, default value, and other attributes of the variable. By defining variables, you inform Terraform about the inputs it expects during deployment. While variable blocks can be defined in any terraform configuration file, the convention is to define values in a separate variables.tf file in your Terraform project.
The syntax for a variable block includes the variable keyword, followed by the variable name, and then a set of attributes enclosed in curly braces. The attributes can include the variable type, default value, description, and validation rules.
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."
}
}
You can add a description to your variable to provide more context and information about its purpose. The description attribute is optional and can be added within the variable block. For example:
variable "order" {
type = bool
description = "Whether to order a pizza or not"
default = false
}
A terraform.tfvars file is used to assign variable values that are automatically loaded by Terraform during the apply or plan commands. This file should be created in the root directory of your Terraform project and should contain key-value pairs for your variables as shown below:
variable_name = "variable_value"
For example:
first_name = "John"
last_name = "Smith"
Furthermore, the convention is to add this terraform.tfvars file in your .gitignore file so that you don't check it into git. The reason for this is that you would typically define sensitive values in this file. You can also create a terraform.tfvars.example file that shows an example of the content of the terraform.tfvars file with no sensitive values. This would serve as an example for users using your code.
To use variables in your Terraform configurations, reference them using the syntax var.variable_name. For example, if you have a variable named street, you can reference it in your configuration with var.street like this:
data "dominos_address" "addr" {
street = var.street
}
Terraform provides a variety of built-in functions that you can use to manipulate and transform variable values. These functions enable you to perform operations such as string manipulation, mathematical calculations, conditional logic, and more. Some common functions include lookup, merge, element, length, keys, and values.
Conditional expressions in Terraform allow you to choose between two values based on a condition. The syntax for a conditional expression is
condition ? true_value : false_value
For example:
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
...
}
The Terraform code example above defines a variable named "order" of type bool. It represents whether to order a pizza or not. The variable has a description stating its purpose, and a default value set to false if no value is explicitly provided.
The code also declares a local variable named "should_order" which is based on the value of the "order" variable. If the "order" variable is true, the "should_order" variable will be set to 1; otherwise, it will be set to 0. This local variable is used to conditionally control the creation of the "dominos_order" resource.
Terraform variables support lists and maps, which are particularly useful when you need to handle multiple values or key-value pairs. Lists allow you to store and iterate over multiple elements, while maps provide a convenient way to associate values with specific keys. Let's take a look at an example:
# 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"
}
}
You would then reference the list and map variables the same way with the var.variable syntax.
Terraform environment variables are used to customize various aspects of Terraform's behavior, such as logging, input handling, and backend configuration. They can also be used to set values for input variables by using the TF_VAR_ prefix followed by the variable name.
To set a Terraform environment variable value, use the export command on Unix/Linux systems or the set command on Windows systems. For example in Linux you would run:
export TF_VAR_first_name="Sam"
Input variables are variables that are provided when running Terraform commands. They allow users to input values interactively or through command-line arguments. These are the terraform variables that we've been discussing thus far.
Terraform also supports local variables, which are variables calculated based on other variables or expressions. Local variables can be useful for complex calculations or for simplifying configuration files by reducing redundancy. Below is an example of using local variables:
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
}
In this example, a local variable called customer_info is declared. This variable is of type map and contains as string values several key-value pairs.
The customer_info map includes the following key-value pairs:
We then call on the local values using the local.variable_name reference inside the provider block. When calling first_name for example using local.customer_info.first_name.
Output variables are used to export values from your Terraform configuration, making them available for use in other configurations or for display in the CLI output. They are defined using the output block and are typically defined in an outputs.tf file. Below is an example of an output variable for our pizza order:
output "OrderOutput" {
value = data.dominos_menu_item.item.matches[*]
description = "My order"
}
Terraform uses a specific order of precedence when determining the value of a variable. If the same variable is assigned multiple values, Terraform will use the value of highest precedence, overriding any other values. Below is the precedence order starting from the highest priority to the lowest.
You can add validation rules to your variables using the validation block within the variable definition. This allows you to enforce constraints on the values that can be assigned to a particular variable name. Check the example below:
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"
}
}
The example code above includes a validation block to enforce a specific condition on the menu_item variable value.
The validation block includes the following elements:
Below is a screenshot of the error message if we don't include the "thin" element in the menu_item list variable:
menu_item = ["philly", "medium"]
Terraform allows you to mark variables as sensitive, which prevents their values from being displayed in the CLI output. To mark a variable as sensitive, add the sensitive attribute to the variable definition. Below is an example of our credit card to be used.
# 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"
}
Typically you would not check the terraform.tfvars file into git since it usually contains sensitive values as mentioned before.
If you try to output the credit card variable, using this output block:
output "credit_card" {
value = var.credit_card
description = "Credit Card Info"
}
you'll get the error message below:
You would need to update the output variable file to look like this:
output "credit_card" {
value = var.credit_card
description = "Credit Card Info"
sensitive = true
}
Now when you run terraform apply, you won't get an error and the output variable credit_card will show as <sensitive>
Although the output of the credit card is not displayed, the sensitive data is still stored in the Terraform state file and therefore needs to be properly secured. You can use a remote backend that is encrypted at rest such as an S3 bucket or make use of a fully-fledged Terraform Automation & Collaboration Software (TACoS) solution such as env0. Below is the credit card data in the state file:
Now let's take a look at the full example of ordering a Domino's Pizza. Below are all the necessary files to get this going. You will need to run the following Terraform commands to order a pizza. However, be careful putting in your actual credit card because the system will order a pizza for you!
terraform init
terraform apply
The variable "order" is set to false by default. This will prevent the resource "dominos_order" from actually ordering a pizza. If you are serious about ordering a pizza with Terraform, set this variable to true.
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
}
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
}
credit_card = {
number = 123456789101112
cvv = 1314
date = "15/16"
postal_code = "18192"
}
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"]
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
}
Terraform supports complex data types, such as objects and tuples, as variables. These can be used to store and manipulate more complex data structures in your configurations.
Use custom workflows to model any process
Visualize all IaC changes pre and post-deployment
Gain code-to-cloud visibility and governance
Improve developer experience and collaboration
Use custom workflows to model any process
Visualize all changes pre and post-deployment
Gain code-to-cloud visibility and governance
Improve developer experience and collaboration
env0 is the best way to deploy, scale, and manage your Terraform and other Infrastructure as Code tools.