Terraform Types: Use of Boolean, Map,List, Data types

Malcolm Matalka avatar

Malcolm Matalka

Cover for Terraform Types: Use of Boolean, Map,List, Data types

Terraform Variable Types

In Terraform, you must specify the type of data that needs to be assigned to a variable, passed to and from modules, or output. Terraform types and Terraform variables are used to build clean and robust Terraform code as they validate the user-provided values for the input variables, output variables, and resources.

In this blog, we will be looking at various Terraform variable types:

  • string
  • number
  • bool
  • list
  • set
  • map
  • object
  • tuple

We will aim to understand their variable definitions and usage when writing Terraform code. We will also use the Terraform console to test and validate Terraform types.

By the end of this blog, you will be able to convert map to list or Terraform list variables to Terraform map variables, perform string manipulation using the format() function, perform Terraform list manipulations using concat(), compact(), coalescelist(), tolist() functions and use map manipulation with the tomap() function.

We’ll cover the following

  • Defining the variables required to create an AWS resource with suitable Terraform types.
  • Converting the defined variables from one Terraform type to another and using the function.
  • Using the Terraform console to check the converted or transformed value.
  • Running the Terraform plan for resource creation.

Categories

Terraform types can be divided into two major categories.

1. Primitive types

There are 3 primitive Terraform types. We will take a look at them one by one:

string

This Terraform type can represent text and is enclosed in double quotes, typically the collection of characters. For example, we can define a Terraform variable block instance_type with the string value.

variable "instance_type" {_
type = string // string type value
default = "t2.micro"
}

number

This Terraform type represents both integer and floating point numbers. For example, we can define a Terraform variable block number_of_instances with the numeric value.

variable "number_of_instances" {
type = number // number type value
default = 2
}

bool

This Terraform type is used to pass a boolean value, either “true” or “false.” For example, we can define a Terraform variable block ebs_optimized with the boolean value.

variable "ebs_optimized" {
type = bool // boolean type value
default = true
}

2. Complex types

Complex types contain other values or a combination of values. They are collections of primitive data types, allowing them to create and handle complicated and complex data. They are further classified into two types:

Collection Types

Collection types represent the group of values.

list

A list is an ordered sequence of elements that belong to the same type. Each element is identified using the indices. For example, we define a Terraform variable block instance_tenancy with variable values of the Terraform list of strings.

variable "instance_tenancy" {
type = list(string) // list type that accepts strings as values
default = ["default", "dedicated", "host"]
}

set

A set type is an unordered collection of unique elements that belong to the same type. However, you cannot access variable values individually since a set is unordered. For example, we define a Terraform variable block security_group_ids with a set of strings.

variable "security_group_ids" {
type = set(string) // set type that accepts strings as values
default = ["sg-02ce123456e7893c7", "sg-02ce123456e7894c7", "sg-02ce123456e7893c7"]
}

You can convert a list to a set using toset() function as shown below and remove the duplicates from the list

security_group_ids = toset(["sg-02ce123456e7893c7", "sg-02ce123456e7894c7", "sg-02ce123456e7893c7"])

map

A map type is a collection of key-value pairs where all the values are of a specified type.

For example, we define a Terraform variable block metadata_options with variable values of the Terraform map of strings.

variable "metadata_options" {
type = map(string) // map type that accepts string keys and values
default = {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = "2"
}
}

Structural Types

These are used to create a composition of multiple values into one single value.

object

An object type that allows you to group different variable types of attributes with different names into a single structure. One could use them in scenarios where a fixed set of attributes and their specific type, allows flexibility in using both simple and complex types.

For example, we define a Terraform variable root_block_device with variable values of the object Terraform type.

variable "root_block_device" {
type = object({
volume_type = string,
volume_size = number,
delete_on_termination = bool
})
// object type that is expecting a string, a number and a bool value_
default = {
"volume_type" = "gp2"
"volume_size" = 20
"delete_on_termination" = true
}
}

tuple

A tuple represents a collection of ordered elements where these elements can have a different data type. You could use tuples to ensure that a specific structure is followed in storing the elements data, a fixed number of elements, and their types in the particular position of the tuple.

For example, we define a Terraform variable launchDate with tuple Terraform type variable values.

variable "launchDate" {
type = tuple([number, string]) // tuple type that accepts a list of two values, a number and a string
default = [4, "September"]
}

How can we use Terraform Types to create an AWS resource?

We would create a file var.tf using the variables defined above with the specified Terraform types and use them to create an aws_instance resource in a main.tf file.

The Terraform types used in creating an aws_instance resource are:

  • string - a variable instance_type with the string value, which will provide the configuration for what type of aws_instance resource.
  • number - a variable number_of_instances with the numeric value, which will provide the number of aws_instances to be created
  • boolean - a variable ebs_optimized with the bool value, which will provide the configuration for aws_instance resource if the ebs volume will be optimized or not.
  • list of strings - a variable instance_tenancy with the list of string, which will provide the configuration for aws_instance resource, which tenancy to use.
  • set of strings - a variable security_group_ids with the set of string, which will provide the configuration for aws_instance resource which security groups_ _ to use.
  • map of strings - a variable metadata_options with the map of string, which will provide the metadata for aws_instance resource.
  • object - a variable root_block_device with the object Terraform type, which expects a string, numeric, and boolean value, for aws_instance resource.
  • tuple - a variable launchDate with the tuple Terraform type, which accepts a list of two values, a number and a string, which will provide tags for the aws_instance resource.
resource "aws_instance" "ec2_instance" {
count = var.number_of_instances
ami = "ami-0c55b159cbfafe1f0"
instance_type = var.instance_type
ebs_optimized = true
tenancy = var.instance_tenancy[0]
root_block_device {
volume_type = var.root_block_device["volume_type"]
volume_size = var.root_block_device["volume_size"]
delete_on_termination = var.root_block_device["delete_on_termination"]
}
metadata_options {
http_endpoint = var.metadata_options.http_endpoint
http_tokens = var.metadata_options.http_tokens
http_put_response_hop_limit = var.metadata_options.http_put_response_hop_limit
}
vpc_security_group_ids = var.security_group_ids
tags = {
LaunchDay = var.launchDate[0]
LaunchMonth = var.launchDate[1]
}
}

Run the terraform plan command in the terminal in the same directory as main.tf and var.tf.

Terraform plan aws instance

Terraform console

This is a command-line interface that you can use to inspect and evaluate expressions and their output values based on the configurations and current infrastructure code, enabling you to easily debug the configuration.

First, execute the terraform console command in your terminal. Now, you can use the type() function to get the Terraform type of the data provided.

For example, if you pass the value as true in the type() function as type(true), it would give you a bool type for boolean values.

Terraform Console boolean

Getting hands-on with lists, maps, and strings

The example below shows how you can use various types to build out an EKS cluster.

Convert a Terraform map of string to Terraform lists of string

Let’s create a Terraform variable name cluster_tags for the tags in the variable (var.tf) file, a Terraform map of strings. We will use this for our EKS cluster configuration.

variable "cluster_tags" {
type = map(string) // map of string type value
default = {
env = "dev"
cluster-type = "dedicated"
creation_date = ""
}
}

Functions

  • compact() - Takes a list of strings and removes any null or empty string values.
  • concat()- Takes two or more lists and returns one combined list.
  • keys() - Returns a list of all the keys in the map.
  • values() - Returns a list of all the values corresponding to the keys in the map.

First, we will convert the Terraform map of strings to Terraform lists of strings of keys and values of the Terraform map.

As we can see above, the cluster_tags Terraform map variable has a key named creation_date__ which has an empty value. We would remove this empty value for our EKS configuration using the _comact() function in main.tf.

locals {_
remove_empty_from_values_of_map = compact(values(var.cluster_tags))
values_of_map = concat(local.remove_empty_from_values_of_map, [2024])
keys_of_map = keys(var.cluster_tags)
}

We will use the Terraform console in the same directory where the main.tf and var.tf files are present to see the transformed lists and values() function.

For example, values(var.cluster_tags), which gives us the values in a map of strings as a list of strings, and to refer to the local values we are using, for example, local.keys_of_map, which gives us keys from the map as the list. An important note is that the elements in the lists local.remove_empty_from_values_of_map and local.keys_of_map are in lexicographical order.

Terraform list lexicographical order

Let’s create a local variables list to pass subnet IDs in var.tf named subnet_ids and extra_subnet_ids for the EKS cluster.

variable "subnet_ids" {
type = list(string) // list of string type value
default = ["subnet-01234567890abcdef", "subnet-01234567891abcdef", ""]
}
variable "extra_subnet_ids" {
type = list(string) // list of string type value
default = [""]
}

Functions

  • coalescelist() - This function takes any number of list arguments and returns the first one that isn’t empty.

In the variable extra_subnet_ids, there’s an empty Terraform list of string values. We will just pass values of the default values out of the subnet_ids (non-empty Terraform list) and extra_subnet_ids (empty Terraform list) and use coalescelist() function in main.tf.

locals {
remove_empty_lists = coalescelist(var.subnet_ids, var.extra_subnet_ids)
}

We have 2 different subnet ids above. When passing them in EKS, we will remove the empty subnet from var.subnet_ids using compact() function and pass one single list in subnet_ids:

locals {
subnet_ids = compact(local.remove_empty_lists)
}

We will use the Terraform console in the same directory to see the transformed lists, local.subnet_ids, and local.remove_empty_lists.

Terraform transformed lists

To access the EKS cluster, we need to create a role and define environment variables and attach policies. Let’s look at the policies we would attach to the variable attach_policies of type Terraform map of strings in var.tf.

variable "attach_policies" {
type = map(string) // map of string type value
default = {
AmazonEKSClusterPolicy = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
AmazonEKSVPCResourceController = "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController",
AmazonEKSServicePolicy = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy"
}
}

We would attach some additional policies for our cluster role in the Terraform variable additional_policies of type Terraform map of strings in var.tf.

variable "additional_policies" {
type = map(string) // map of string type value
default = {
AmazonEKSCNIPolicy = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
}
}
  • merge() - this function takes several maps or objects and returns one map or object.

Since there are two different policy maps, we would now merge them into one Terraform map using the merge() function to attach these policies to the cluster role in main.tf.

locals {
policies_to_be_attached = merge(var.attach_policies, local.additional_policies)
}

We will use the Terraform console to verify the value type of local.policies_to_be_attached by using the type function.

Terraform type function

Now, we will attach the role to these policies by iterating over the policies_to_be_attached _object of strings and creating policy attachments for each value in the Terraform map using _for_each.

resource "aws_iam_role_policy_attachment" "managed_policy" {
for_each = {for k, v in local.policies_to_be_attached : k => v}
policy_arn = each.value
role = "arn:aws:iam::123456789012:user/infra"
}

We can see the policies attached to the role when we run the terraform plan.

Terraform IAM role policies

Convert the object to a map

We can convert the Terraform object of strings to a Terraform map of strings using the tomap() function in main.tf.

Function

  • tomap() - converts a Terraform object to a Terraform map.
locals {
map_of_policies = tomap(local.policies_to_be_attached)
}

We will use the Terraform console to see the default value of local.map_of_policies.

Terraform map of policies

Convert a Terraform list of string values to a Terraform map of string values

Let’s convert the lists of strings to a map of strings to create the timeout configuration for our EKS cluster in main.tf.

locals {
timeouts_key = ["create", "update", "delete"]
timeouts_value = ["30m", "30m", "30m"]
}

We have defined two lists, one for the map’s key and the other for its value. Let’s create the map. We will iterate over the list timeouts_key using the key and capture its index.

By iterating over the list of keys, the timeouts_key value on that index is mapped with the value in the timeouts_value in main.tf. These will be used to create EKS timeouts later.

locals {
eks_timeouts = tomap({
for index, key in local.timeouts_key :
key => local.timeouts_value[index]
})
}

We will use the Terraform console to see the value of eks_timeouts.

Terraform eks timeouts

When enabled, a CIDR range must be passed to allow public access to the Amazon EKS public API server endpoint. Let’s create a variable in the vars.tf.

variable "control_plane_allowed_cidrs" {
type = string // string type value
default = "0.0.0.0/0"
}

Convert the string argument into a list

Since the EKS cluster only accepts list value, we will convert the string argument into a list of strings using the Terraform tolist() function in main.tf. We will use public_access_cidrs to configure the EKS cluster.

Functions

  • tolist() - this function converts the arguments into a list
locals {
public_access_cidrs = tolist([var.control_plane_allowed_cidrs])
}

We will use the Terraform console to see the value of public_access_cidrs.

Terraform public access cidrs

We would create the variables we want to use to name our cluster in var.tf.

variable "eks_cluster_name" {
type = string // string type value
default = "eks-cluster"
}
variable "eks_cluster_env" {
type = string // string type value
default = "non-prod"
}
  • format() - this function produces a custom string formatting the values provided per the string specification.

Using the format() function in the main.tf file, we would produce a new string using the name and env as a suffix for the cluster name.

locals {
cluster_name_with_suffix = format("%s-%s", var.eks_cluster_name, var.eks_cluster_env)
}

We will use the Terraform console to see the local value of cluster_name_with_suffix.

Terraform cluster name with suffix

We have looked at various Terraform types, functions, and conversions and have used them to create Terraform code for an EKS cluster.

create aws eks cluster resource
resource "aws_eks_cluster" "cluster" {
name = local.cluster_name_with_suffix
role_arn = "arn:aws:iam::123456789012:user/infra"
vpc_config {
endpoint_private_access = true
endpoint_public_access = true
public_access_cidrs = local.public_access_cidrs
subnet_ids = local.subnet_ids
}
tags = {
cluster_type = local.values_of_map[0]
env = local.values_of_map[1]
day = local.values_of_map[2]
}
timeouts {
create = local.eks_timeouts["create"]
update = local.eks_timeouts["update"]
delete = local.eks_timeouts["delete"]
}
}

When you run the terraform plan command in the terminal, you will see that the aws_eks_cluster would be created.

Terraform plan aws_eks_cluster

Other conversions

Converting a list of objects to a map of objects

Let’s create a list of objects in vars.tf, which we would convert to a map of objects.

variable “list_of_objects” {
type = list(object({
name = string
type = string
}))
default = [
{
name = “Orange”
type = “Fruit”
},
{
name = “Potato”
type = “Vegetable”
}
]
}

In main.tf we would iterate over the list_of_objects, using the for expression. We will name the converted variable value in this expression using the locals my_map and produce a map of objects by keeping the name as the unique value of input Terraform variables in the object.

locals {
my_map = tomap({ for obj in var.list_of_objects : obj.name => obj })
}

We will use the Terraform console to see the value of local.my_map.

Terraform console map

Convert a list to a map of an object

Let’s create a list of strings that we will convert to a map of an object in vars.tf.

variable "autoscaling" {
type = list(string)
default = ["desired_size", "min_size", "max_size"]
}

We will iterate over this list to create an object of an object and use the tomap() function to convert it into a map of an object in main.tf.

locals {
autoscaling_object = tomap({
for autoscale in var.autoscaling:
autoscale => {size = "2"}
})
}

We will use the Terraform console to check the Terraform type of local.autoscaling_object and the value of the Terraform map variable.

Terraform consle map

Convert Terraform list to string

Let’s create a Terraform list in vars.tf.

variable "instance_tenancy" {
type = list(string) // list type that accepts string values
default = ["default", "dedicated", "host"]
}

Functions

  • join() - takes a separator as the first value and a list as the second value. Using the separator, the function converts the Terraform list to string values.

To convert the Terraform list to string values, we will use the join() function in main.tf.

locals {
string_of_instance_tenancy = join(", ", var.instance_tenancy)
}

We will use the Terraform console to verify the value of the converted Terraform list to a string variable.

Terraform consle string

Key differences between list, map, tuple, set, and object

TypeStructureValue TypesUniqueness / Order
ListOrderedSame typeIndexed, maintains order
MapUnorderedSame type but keys are always stringsUnique keys, no inherent order
TupleOrderedDifferent typesIndexed, maintains order
SetUnorderedSame typeUnique values, no inherent order
ObjectNamed attributesDifferent typesUnique attributes, order not applicable

Frequently Asked Questions

1. How do I convert a list of objects to a map in Terraform?

A list of objects can be converted into a map using a for expression to iterate over the list and create a map, keeping the unique value in the object as the key value.

2. What is the Terraform tolist() function in Terraform?

The Terraform tolist() function converts the arguments into a list.

3. How do you make a list in Terraform?

A list can be made by passing the arguments in a Terraform tolist() function.

4. What is a map in Terraform?

A map is a complex Terraform type that is a collection of key-value pairs where all the values are of a specified type for which the keys are of string type.

5. What is a list in Terraform?

A list is a complex Terraform type, an ordered sequence of elements belonging to the same type. Its components are identified using indices.

Conclusion

In conclusion, understanding Terraform types is essential for building robust and maintainable infrastructure configurations. This blog explored Terraform types such as string, number, bool, list, set, map, object, and tuple and handling sensitive values. It highlighted their definitions and practical usage in Terraform configuration files for variable definitions files, module interactions, and resource creation.

Additionally, we discussed utilizing the Terraform console to test and validate Terraform types. By mastering Terraform types and manipulation techniques, readers will be equipped to create cleaner, more efficient Terraform code, ensuring smoother infrastructure deployments.

Infrastructure as Code. Optimized.

Ready to get started?

Experience powerful infrastructure orchestration, seamlessly integrated with GitHub.