Terraform

How to Use Terraform Flatten Function [Examples]

How to Use Terraform Flatten Function [Examples]

In this short article, we will look at the flatten function in Terraform, showing some examples of its use with nested lists and structures.

We will cover:

  1. What is the flatten function in Terraform?
  2. How to use the flatten function?
  3. Terraform flatten examples

What is the flatten function in Terraform?

flatten is a built-in function provided by Terraform. It takes a list and replaces any elements that are lists with a flattened sequence of the list contents. flatten only expects one argument, which should be passed in as a list (elements separated by commas within square brackets [] ). In Terraform, lists are ordered collections of values.

A Terraform function is a built-in operation that you can use to manipulate, transform, or query data within your Terraform configuration. Functions allow you to perform various tasks, such as transforming data, filtering collections, performing calculations, and more. They are an essential part of the HashiCorp Configuration Language (HCL) that Terraform uses.

Functions in Terraform are designed to be used within expressions. Expressions are used to define values, calculations, and transformations in your Terraform configuration files. Functions take input arguments (such as strings, lists, maps, etc.), perform a specific operation, and return a result that can be used within your configuration.

How to use Terraform flatten function?

To test the flatten function, you can use the Terraform console. Simply type terraform console and enter the expression you want to test.

In the example below, I have two lists of values:

flatten([["luke", "yoda", "obiwan"], ["darth", "palpantine"]])

The flatten function returns the flattened sequence of the lists contents:

terraform flatten output

You can also include empty lists ( [] ).

flatten([["luke", "yoda"], [], ["darth"]])
terraform flatten empty list

To define a list in your Terraform configuration files, you can define a variable like the example below.

If you want to define a list of a specific type (e.g., a list of numbers or strings), you can specify the type in the list type declaration.

variable "list_of_characters" {
  type    = list(list(string))
  default = [["luke", "yoda"], ["darth", "palpantine"], ["c3po", "r2d2"]]
}

To use flatten with this variable, your configuration file would reference it like the example below:

flatten(var.list_of_characters)

And would output the following:

terraform flatten variable list

Terraform flatten examples

Example 1: Flattening nested structures for for_each

Terraform for_each requires a collection that has one element for each repetition.

Sometimes your data structure may not be suitable to use with the for_each function because of this. This is where the flatten function can be used to manipulate the data in the correct way for use with for_each .

Consider we define a variable containing some lists of our network ranges and subnets:

variable "networks" {
  type = map(object({
    cidr_block = string
    subnets    = map(object({ cidr_block = string }))
  }))
  default = {
    "private" = {
      cidr_block = "192.168.1.0/24"
      subnets = {
        "sql1" = {
          cidr_block = "192.168.1.0/25"
        }
        "cosmos1" = {
          cidr_block = "192.168.1.128/25"
        }
      }
    },
    "public" = {
      cidr_block = "192.168.2.0/24"
      subnets = {
        "app1" = {
          cidr_block = "192.168.2.0/28"
        }
        "app2" = {
          cidr_block = "192.168.2.16/28"
        }
      }
    }
  }

In the below example that shows the creation of an AWS VPC, when referencing the variable in our code, we can use the for_each function to iterate through the list and pull out the value for each cidr_block defined in the variable.

resource "aws_vpc" "example" {
  for_each = var.networks

  cidr_block = each.value.cidr_block
}

However, to reference the values for the subnet cidr_blocks, we need to use flatten first to produce a flat list of objects, rather than the list of lists that we have defined in our variable if we wanted to define multiple subnets using a single resource block of code. This creates a neat way to create multiple VNETs and Subnets with minimal code.

In the example below, we use locals to create a flat list and reference it as local.network_subnets. The for_eachfunction is then used with the forexpression to project the list into a map where each key is unique. Network and subnet keys are combined to produce a single unique key per instance.

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
        network_id  = aws_vpc.example[network_key].id
        cidr_block  = subnet.cidr_block
      }
    ]
  ])
}

resource "aws_subnet" "example" {
  for_each = {
    for subnet in local.network_subnets : "${subnet.network_key}.${subnet.subnet_key}" => subnet
  }

  vpc_id            = each.value.network_id
  availability_zone = each.value.subnet_key
  cidr_block        = each.value.cidr_block
}

Example 2: Iterating through a nested list

If any of the nested lists also contain directly-nested lists, these, too are flattened recursively:

flatten([[["luke", "yoda"], []], ["darth"]])
terraform flatten nested list

Example 3: Iterating through a nested list with a map

Indirectly-nested lists, such as those in maps, are not flattened.

In the below example, I have a map (defined within curly braces {} ) that contains two key-value pairs.

flatten([[["luke", "yoda"], []], ["darth"], {key1 = "jedi", key2 = "sith"} ])

Using the flatten function on these flattens the contents of the lists, but not the map contents:

terraform flatten map

Example 4: Using the flatten function with the for expression

For the below examples, we have a variable that defines a nested map:

variable "nested_map" {
  type = map(any)
  default = {
    "group:jedi" = [
      "luke",
      "yoda"
    ],
    "group:sith" = [
      "darth",
      "palpantine"
    ],
    "group:everyone" = [
      "luke",
      "darth",
      "c3po",
      "r2d2"
    ],
  }
}

The first example below shows how we could use the flatten function in a Terraform for expression to retrieve values from the map.

Note that “luke” and “darth” are retrieved twice because they are in two of the lists.

locals {
  flattened_map = flatten([
    for group, characters in var.nested_map : [
      group
    ]
  ])
}

output "result" {
  value = distinct(local.flattened_map)
}
---------------------------------------------------------------------------
Output:
 + result = [
      + "luke",
      + "yoda",
      + "darth",
      + "palpantine",
      + "luke",
      + "darth",
      + "c3po",
      + "r2d2"
    ]

The second example below shows how we could use the flatten function in a loop in combination with the distinct function to retrieve only distinct values from the map.

locals {
  flattened_map = flatten([
    for group, characters in var.nested_map : [
      group
    ]
  ])
}

output "result" {
  value = distinct(local.flattened_map)
}
---------------------------------------------------------------------------
Output:
 + result = [
      + "luke",
      + "yoda",
      + "darth",
      + "palpantine",
      + "c3po",
      + "r2d2"
    ]

We could also use the flatten function with a for expression to retrieve the values of the keys in the map:

locals {
  flattened_map = flatten([
    for group, characters in var.nested_map : [
      group
    ]
  ])
}

output "result" {
  value = local.flattened_map
}
----------------------------------------------------------------------------
Output:
 + result = [
      + "group:jedi",
      + "group:sith",
      + "group:everyone",
    ]

Key points

The flatten function can be used in expressions in Terraform to manipulate the output of a data structure, and can be useful to present your data to the for_each function to iterate through a data structure that naturally wouldn’t be suitable to use with it, as for_each requires a collection that has one element for each repetition.

For easier Terraform management, you can also check out Spacelift – a sophisticated and compliant infrastructure delivery platform. Spacelift can help you with building more complex workflows based on Terraform, and has the flexibility to integrate with any third party tool you want. You can test drive it for free by going here and creating a trial account.

Note: New versions of Terraform will be 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 will expand on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6. OpenTofu retained all the features and functionalities that had made Terraform popular among developers while also introducing improvements and enhancements. OpenTofu works with your existing Terraform state file, so you won’t have any issues when you are migrating to it.

The most flexible management platform for Infrastructure as Code

Spacelift is a sophisticated SaaS product for Infrastructure as Code that helps DevOps develop and deploy new infrastructures or changes quickly and with confidence.

Start free trial