Maps are a fundamental data type in Terraform’s HashiCorp Configuration Language (HCL), which is used to define infrastructure as code (IaC) in Terraform. They are useful when you want to associate a key with a specific value, especially when working with dynamic or repeatable resources.
In this article, we will examine map variables, explaining what they are, why you would use them, and the differences between other key data structure types in Terraform.
We will also provide some practical examples of how to use a map, how to use a map with local values, and how to use Terraform functions to manipulate your map output.
What we will cover:
A Terraform map variable is a key-value data structure that groups related values under a single variable, allowing structured access to configuration data.
Maps are commonly used in Terraform to store configuration data, define variables, and pass information between different parts of your code.
When defining your map variable, you can also set the type annotation. The following types can be used to define your map:
map(string)
: The values in the map are of type “string.”map(number)
: The values in the map are of type “number” (integer or floating-point).map(bool)
: The values in the map are of type “bool” (true or false).map(list)
: The values in the map are lists (arrays) containing elements of the same type.map(set)
: The values in the map are sets containing unique elements of the same type.map(object({ ... }))
: The values in the map are objects (complex data structures) that must conform to a specific structure defined by the object’s attributes.
What is the difference between map and object?
While maps and objects have similar syntax (both use curly braces {}
and key-value pairs), their intended purposes and where they are typically used in Terraform are distinct. A map in Terraform is a simple key-value structure where all values must be of the same type. An object
, by contrast, defines a schema with named attributes that can each have different types.
Objects are mainly used to represent and manage resource attributes in Terraform providers, where the provider uses the object to understand the properties of a resource being managed.
As an example, the code below creates an AWS EC2 instance. The tags
attribute of the resource would be considered an ‘object’.
resource "aws_instance" "example_instance" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "Example Instance"
Environment = "Production"
}
}
What is the difference between map(string) and map(object)?
The difference between map(string)
and map(object)
refer to different types of maps with distinct purposes.
map(string)
defines a map where all values must be strings, while map(object({...}))
allows values to be structured objects with multiple typed attributes. In this context, “object” means a complex data structure that can have multiple attributes represented by key-value pairs.
The “object” type is typically used when you want to define structured data with multiple attributes for each key in the map.
type = map(string)
Let’s check out some examples of each!
A map in Terraform is defined using curly braces {}
and consists of zero or more key-value pairs. Each key in the map must be unique, and the values can be of any data type supported by Terraform, such as strings, numbers, lists, or even nested maps.
Example 1 – Map of string
The example below shows a simple map of type string in Terraform:
variable "force_map" {
type = map(string)
default = {
luke = "jedi"
yoda = "jedi"
darth = "sith"
}
}
In this example, we define a variable named force_map
with the type map(string)
, indicating that it is a map with string values.
The map contains three key-value pairs (the keys are luke, yoda and darth, and the values are specified for each as jedi, jedi and sith). Values are simple strings associated with each key.
You can use the keys to access the values in the map. For example, to access the value corresponding to ‘yoda’ in the force_map
variable, you would use ${var.force_map["yoda"]}
. This would return jedi
!
Example 2 – Map of object
The example below shows a map of type ‘object’. Values are complex objects with multiple attributes represented by key-value pairs.
variable "example_map" {
type = map(object({
name = string
enemies_destroyed = number
badguy = bool
}))
default = {
key1 = {
name = "luke"
enemies_destroyed = 4252
badguy = false
}
key2 = {
name = "yoda"
enemies_destroyed = 900
badguy = false
}
key3 = {
name = "darth"
enemies_destroyed= 20000056894
badguy = true
}
}
}
Each key in the example above (key1, key2 and key3) is associated with a complex object having attributes “name,” “enemies_destroyed,” and “badguy”, each value having a different data types (string, number and bool).
Example 3 – Map of lists
The next example shows a map of lists, which contain values of type string:
variable "lightsabre_color_map" {
type = map(list(string))
default = {
luke = ["green", "blue"]
yoda = ["green"]
darth = ["red"]
}
}
This could also be achieved with a map using the ‘set’ type:
variable "lightsabre_color_map" {
type = map(set(string))
default = {
luke = ["green", "blue"]
yoda = ["green"]
darth = ["red"]
}
}
You can use a map with Terraform local values to store data that you want to use within your configuration without repeating the computation. Using local values helps to keep your code clean, readable, and more maintainable.
In the below example, we generate a name for an AWS S3 bucket resource using the number of enemies each character has destroyed.
# The list of characters and their corresponding enemies destroyed
variable "enemies_map" {
type = map(number)
default = {
luke = 4252
yoda = 900
darth = 20000056894
}
}
locals {
# Computed names for each bucket
bucket_names = {
for name, destroyed in var.enemies_map :
name=> "${name}-bucket"
}
}
resource "aws_s3_bucket" "buckets" {
for_each = local.bucket_names
bucket = each.value
acl = "private"
}
You can convert a list to a map using the for expression and the toset() function.
The Terraform toset()
function converts a list into a set, which can then be used to create a map with unique keys and associated values. This conversion is necessary in situations where a map in Terraform requires unique keys and a list has duplicate elements.
In the example below, we have two lists defined in our locals: characters
and enemies_destroyed
, which we want to convert to a map.
locals {
characters = ["luke", "yoda", "darth"]
enemies_destroyed = [4252, 900, 20000056894]
map = {
for index, character in toset(local.characters) : # Convert character list to a set
character => local.enemies_destroyed[index]
}
}
The for
expression is used to loop over the set to create the map, where we use characters
as the key and enemies_destroyed
as the value.
This results in the following map:
{
"luke" = 4252
"yoda" = 900
"darth" = 20000056894
}
To avoid runtime errors when converting lists to maps in Terraform, follow the best practices below:
- Use
for
expressions with clear key-value mappings: When converting a list to a map, ensure that each element in the list contains enough structure to derive a unique key. - Ensure key uniqueness: Keys in a map must be unique. When using a property from list elements as the map key (e.g.,
user.id
), validate or sanitize input if necessary. - Handle complex structures with object unpacking: If the list contains objects, use property unpacking to map meaningfully
- Use conditionals cautiously in
for
expressions:
Include filters only when relevant, to avoid introducing sparse maps or unexpectednull
values. - Prefer clarity over compactness: Avoid deeply nested or overly concise expressions that hinder maintainability.
To convert your map or nested list back into a flat list, use the Terraform flatten()
function.
When applied to a map, the flatten function flattens the map into a list of key-value pairs represented as a list of lists. Each sublist contains two elements: the key and the corresponding value from the original map.
In the example below, we use the flatten
function again with the for
expression to iterate over the key-value pairs in the enemies_map
.
locals {
enemies_map= {
luke = 4252
yoda = 900
darth = 20000056894
}
flattened_enemies_map = flatten([
for key, value in local.enemies_map:
[key, value]
])
}
The resulting flat list looks like this:
[
"luke", 4252,
"yoda", 900,
"darth", 20000056894,
]
We can also use the Terraform tomap
function to convert other data types to maps. It can be used to convert a single object or a single-element list into a map, where each attribute of the object becomes a key-value pair in the resulting map.
In the example below, we have our enemies_list
defined as type list. We can use the tomap
function to convert this to a list where the name is the key and enemies_destroyed is the value.
locals {
enemies_list = [
{ name = "luke", enemies_destroyed = 4252 },
{ name = "yoda", enemies_destroyed = 900},
{ name = "darth", enemies_destroyed = 20000056894},
]
map = tomap({
for character in local.enemies_list:
character.name => character.enemies_destroyed
})
}
Note that because a map requires all of the elements to be of the same type, mixed-type elements will be converted to the most general type. For example, if we had a list containing strings and boolean values, using tomap
would result in the values all being strings:
tomap({"character" = "luke", "badguy" = false})
# Output:
{
"a" = "luke"
"b" = "false"
}
The map()
function in older Terraform versions created a map from a sequence of key-value pairs, for example, map("env", "prod", "region", "us-west-1")
. It was a variadic function, which made it convenient for defining small inline maps quickly.
However, starting with Terraform 0.12, the map()
function was removed. This change was due to the introduction of native map syntax using curly braces {}
, such as:
{
env = "prod"
region = "us-west-1"
}
The native map syntax is more readable, supports complex expressions, and aligns better with HCL2’s consistent data types, making the map()
function redundant.
Terraform is really powerful, but to achieve an end-to-end secure Gitops approach, you need to use a product that can run your Terraform workflows. Spacelift takes managing Terraform to the next level by giving you access to a powerful CI/CD workflow and unlocking features such as:
- Policies (based on Open Policy Agent) – You can control how many approvals you need for runs, what kind of resources you can create, and what kind of parameters these resources can have, and you can also control the behavior when a pull request is open or merged.
- Multi-IaC workflows – Combine Terraform with Kubernetes, Ansible, and other infrastructure-as-code (IaC) tools such as OpenTofu, Pulumi, and CloudFormation, create dependencies among them, and share outputs
- Build self-service infrastructure – You can use Blueprints to build self-service infrastructure; simply complete a form to provision infrastructure based on Terraform and other supported tools.
- Integrations with any third-party tools – You can integrate with your favorite third-party tools and even build policies for them. For example, see how to integrate security tools in your workflows using Custom Inputs.

As it pursues its mission to transform grocery delivery logistics technology, Picnic Technologies wants to free its infrastructure team to do impactful work. Spacelift helps them to create the infrastructure they need, without the pain of manual Terraform processes. Now developers can work efficiently on more enjoyable work.
Spacelift enables you to create private workers inside your infrastructure, which helps you execute Spacelift-related workflows on your end. Read the documentation for more information on configuring private workers.
You can check it out for free by creating a trial account or booking a demo with one of our engineers.
In Terraform, a map is a data structure used to represent a collection of key-value pairs and is commonly used to store configuration data, define variables, and pass information between different parts of your infrastructure code.
A map in Terraform is always defined using curly braces {}
and consists of zero or more key-value pairs. Each key in the map must be unique, and the values can be of any data type supported by Terraform, such as strings, numbers, lists, or even nested maps.
You can convert data to or from maps, using Terraform’s built-in functions as required.
Note: New versions of Terraform are placed under the BUSL license, but everything created before version 1.5.x stays open-source. OpenTofu is an open-source version of Terraform that expands on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6.
Manage Terraform better with Spacelift
Build more complex workflows based on Terraform using policy as code, programmatic configuration, context sharing, drift detection, resource visualization and many more.