On this page
Terraform flatten() converts a nested list into a single flat list. It is useful when a for expression creates a list of lists, and you need one collection for further processing, especially when preparing data for for_each.
This guide explains how Terraform flatten works, when to use it, and how to use it with nested data and for_each.
What Is Terraform Flatten?
Terraform flatten() is a built-in function that takes a list and replaces nested lists with their elements. If you are working with other Terraform functions alongside flatten(), see our guide to [Terraform functions] for more examples.
The syntax is:
flatten(list)For example:
flatten([
["web", "api"],
["database", "cache"]
])
The result is:
[
"web",
"api",
"database",
"cache"
]The function changes the structure of the list without changing the individual values. Directly nested lists are flattened recursively.
How Does the Terraform Flatten Function Work?
Suppose a variable contains groups of servers:
locals {
server_groups = [
["web-01", "web-02"],
["api-01", "api-02"]
]
}The value is a list containing two other lists.
You can flatten it with:
locals {
servers = flatten(local.server_groups)
}
The resulting value is:
[
"web-01",
"web-02",
"api-01",
"api-02"
]Terraform also handles multiple levels of direct list nesting:
flatten([
[
["web", "api"],
["database"]
],
["cache"]
])
Result:
[
"web",
"api",
"database",
"cache"
]
Empty lists contribute no elements:
flatten([
["web", "api"],
[],
["database"]
])
Result:
[
"web",
"api",
"database"
]HashiCorp documents this recursive behavior for directly nested lists.
How Do You Use Flatten With a for Expression?
A common reason to use flatten() is that a for expression can produce nested lists. The for expression is another important part of this pattern. If you’re still getting familiar with Terraform’s configuration syntax, our guide to [Terraform HCL] explains variables, expressions, locals, and other HCL concepts.
Consider this variable:
variable "environments" {
default = {
development = ["web", "api"]
production = ["web", "api", "worker"]
}
}You could create an object for every service like this:
locals {
services = [
for environment, services in var.environments : [
for service in services : {
environment = environment
name = service
}
]
]
}This produces a list of lists.
The outer for creates one list for each environment, while the inner for creates the service objects inside each list.
To turn that into one list, wrap the expression with flatten():
locals {
services = flatten([
for environment, services in var.environments : [
for service in services : {
environment = environment
name = service
}
]
])
}The result is a flat list:
[
{
environment = "development"
name = "web"
},
{
environment = "development"
name = "api"
},
{
environment = "production"
name = "web"
},
{
environment = "production"
name = "api"
},
{
environment = "production"
name = "worker"
}
]A for expression transforms each item in a collection, while flatten() removes the extra list nesting created by the nested expression.
How Do You Use Terraform Flatten With for_each?
One of the most useful flatten Terraform patterns is preparing nested data for for_each.
Terraform’s for_each expects a map or a set of strings. When your input is a hierarchical structure, you may need to transform it before Terraform can create individual resource instances.
For example, imagine several networks, each containing multiple subnets:
variable "networks" {
type = map(object({
cidr_block = string
subnets = map(object({
cidr_block = string
}))
}))
}A simplified value could look like this:
networks = {
private = {
cidr_block = "10.1.0.0/16"
subnets = {
database = {
cidr_block = "10.1.1.0/24"
}
backend = {
cidr_block = "10.1.2.0/24"
}
}
}
public = {
cidr_block = "10.2.0.0/16"
subnets = {
frontend = {
cidr_block = "10.2.1.0/24"
}
loadbalancer = {
cidr_block = "10.2.2.0/24"
}
}
}
}The subnet information is nested inside each network. You can flatten it into one list:
locals {
network_subnets = flatten([
for network_key, network in var.networks : [
for subnet_key, subnet in network.subnets : {
network_key = network_key
subnet_key = subnet_key
cidr_block = subnet.cidr_block
}
]
])
}Now each subnet is a separate object in one list.
You can then create a map with unique keys:
locals {
subnet_map = {
for subnet in local.network_subnets :
"${subnet.network_key}.${subnet.subnet_key}" => subnet
}
}The resulting keys are:
private.database
private.backend
public.frontend
public.loadbalancerThat map can be used with for_each:
resource "example_subnet" "this" {
for_each = local.subnet_map
cidr_block = each.value.cidr_block
}
The important part is the transformation:
Nested data
↓
for expressions
↓
List of lists
↓
flatten()
↓
Flat list of objects
↓
for expression
↓
Map with unique keys
↓
for_eachThis is the same general pattern shown in HashiCorp’s documentation for flattening nested network and subnet data before using it with for_each.
Does Flatten Work With Maps and Objects?
Not by searching through them for every nested list.
flatten() works on lists and only flattens lists that are directly nested inside the input list. A list stored inside an object or map is considered indirectly nested and is not automatically flattened.
For example:
flatten([
{
name = "web"
services = ["nginx", "php"]
}
])
does not produce:
[
"nginx",
"php"
]The object remains an element of the outer list.
This distinction matters when working with complex Terraform variables. If the list you want to flatten is inside an object attribute, you need to access that attribute explicitly.
Can You Flatten a List of Objects?
Yes.
flatten() removes the surrounding list nesting but does not merge or modify the objects.
For example:
locals {
groups = [
[
{
name = "web"
type = "server"
}
],
[
{
name = "database"
type = "service"
}
]
]
resources = flatten(local.groups)
}
The result is:
[
{
name = "web"
type = "server"
},
{
name = "database"
type = "service"
}
]The attributes inside each object stay unchanged.
This makes flatten() useful when nested for expressions produce structured objects that you later want to process as one collection.
What Is the Difference Between flatten and concat in Terraform?
flatten() and concat() can both result in one list, but they solve different problems.
Use concat() when you already have separate lists and want to join them:
concat(
["web", "api"],
["database", "cache"]
)
Result:
[
"web",
"api",
"database",
"cache"
]Use flatten() when your input itself contains nested lists:
flatten([
["web", "api"],
["database", "cache"]
])
Result:
[
"web",
"api",
"database",
"cache"
]The difference becomes clearer with deeper nesting:
flatten([
["web"],
[["api", "database"]]
])
flatten() removes the additional list level:
[
"web",
"api",
"database"
]If your goal is simply to join two known lists, concat() communicates that intention more clearly. If you need to remove nested list structure, use flatten().
How Can You Test Flatten in Terraform?
You can test an expression without creating or changing infrastructure by using Terraform Console.
From your Terraform project directory, run:
terraform consoleThis opens an interactive Terraform expression console.
Now test a simple expression:
flatten([
["web", "api"],
["database"]
])
The result should be:
[
"web",
"api",
"database",
]
You can also test nested lists:
flatten([
[["web"], ["api"]],
["database"]
])
The result should be:
[
"web",
"api",
"database",
]This approach is useful when you are unsure what a complex expression returns. You can test the expression before placing it inside a resource or module.
What Are Common Terraform Flatten Mistakes?
Expecting flatten to Remove Duplicates
flatten() does not remove duplicate values.
flatten([
["web", "api"],
["web"]
])still returns two instances of “web”.
If you need unique values, use distinct() or convert the collection to a suitable set where appropriate.
Expecting flatten to Convert a List Into a Map
flatten() returns a flat list. It does not create keys.
If you need a map for for_each, use a for expression after flattening:
{
for item in local.items :
item.name => item
}Make sure the resulting keys are unique.
Flattening Data Too Early
Nested structures often contain useful relationships.
For example, keeping a subnet associated with its network may matter later. Flattening the data is useful when you need one collection, but it should not be used simply because the data is nested.
Passing a List Directly to for_each
A flattened list is not automatically a valid for_each value in every situation. The for_each meta-argument expects a map or a set of strings.
If you have a list of objects, convert it into a map with stable, unique keys:
{
for item in local.items :
"${item.group}.${item.name}" => item
}When Should You Use Terraform Flatten?
Use flatten() when the structure of your data is the problem.
It is a good fit when:
- A nested for expression creates a list of lists.
- Several groups need to become one collection.
- Network and subnet data needs to be processed together.
- You need to prepare hierarchical data for for_each.
- A dynamic block needs a flat collection to iterate over.
You do not need it for every nested Terraform value. If the existing structure already works with the expression or resource you are using, keeping it nested can make the configuration easier to understand.
Terraform Flatten Best Practices
Keep the function focused on data transformation.
Use it after creating nested data. If a for expression produces a list of lists, flatten() is often the cleanest way to produce one list.
Keep meaningful relationships. If the parent-child relationship matters, include that information in each flattened object.
For example:
{
network = network_key
subnet = subnet_key
}Create stable keys for for_each. A combination such as:
"${item.network}.${item.subnet}"can make each instance identifiable.
Test complex expressions first. Terraform Console lets you inspect the result before using the expression in a resource.
Do not use flatten just to shorten code. The goal is to produce the collection required by the next operation while keeping the configuration understandable.
Frequently Asked Questions
What does flatten do in Terraform?
Terraform flatten() removes directly nested list structures and returns one flat list. It can recursively process directly nested lists.
Is Terraform flatten recursive?
Yes. It recursively flattens lists that are directly nested inside other lists. Lists hidden inside maps or objects are not flattened automatically.
Can flatten be used with for_each?
Yes. It is commonly used to turn nested structures into a flat list before converting that list into a map suitable for for_each.
Does flatten remove duplicate values?
No. flatten() changes the nesting of a list but does not remove duplicates. Use distinct() when duplicate removal is required.
Can flatten convert a list into a map?
No. It returns a flat list. Use a for expression to transform the flattened list into a map when you need unique keys for for_each.
Final Takeaway
Terraform flatten() is useful when nested list data needs to become one collection. Its most practical use is with nested for expressions, where it can turn a list of lists into a flat list of values or objects.
When working with for_each, the usual pattern is to flatten the nested data first and then convert the resulting list into a map with unique keys. This keeps the original input structure useful while giving Terraform the collection shape it needs to create individual instances.