Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge maps inside list in terraform

I have a list of maps like this -

[
  {
    "outer_key_1" = [
      {
        "ip_cidr" = "172.16.6.0/24"
        "range_name" = "range1"
      },
      {
        "ip_cidr" = "172.16.7.0/24"
        "range_name" = "range2"
      },
      {
        "ip_cidr" = "172.17.6.0/24"
        "range_name" = "range3"
      },
      {
        "ip_cidr" = "172.17.7.0/24"
        "range_name" = "range4"
      },
    ]
  },
  {
    "outer_key_2" = [
      {
        "ip_cidr" = "172.16.5.0/24"
        "range_name" = "range5"
      },
      {
        "ip_cidr" = "172.17.5.0/24"
        "range_name" = "range6"
      },
    ]
  },
]

I want to merge the maps inside the list. This is in an output variable module.module_name.module_op.

Required Output:


{
    "outer_key_1" = [
      {
        "ip_cidr" = "172.16.6.0/24"
        "range_name" = "range1"
      },
      {
        "ip_cidr" = "172.16.7.0/24"
        "range_name" = "range2"
      },
      {
        "ip_cidr" = "172.17.6.0/24"
        "range_name" = "range3"
      },
      {
        "ip_cidr" = "172.17.7.0/24"
        "range_name" = "range4"
      },
    ]

    "outer_key_2" = [
      {
        "ip_cidr" = "172.16.5.0/24"
        "range_name" = "range5"
      },
      {
        "ip_cidr" = "172.17.5.0/24"
        "range_name" = "range6"
      },
    ]
}

I have done this using

locals {
  result = merge(module.module_name.module_op[0], module.module_name.module_op[1])
}

How do I do this in a more iterative way.

I will not always only 2 maps in my list, it can be more.

I tried using the for loop in terraform 12 like this -

output "result" {
  value = [ for tuple in module.module_name.module_op : merge(tuple) ]
}

and this -

output "secondary_subnets" {
  value = { for tuple in module.module_name.module_op : merge(tuple) }
}

The first one gives me my input back and the second one gives me an error saying I need to add a key since I am returning an object.

Is there a way to get this done?

Thanks in advance.

like image 972
leoOrion Avatar asked Aug 07 '19 10:08

leoOrion


People also ask

How to merge maps Terraform?

You can use terraform merge() function to combine multiple maps into a single map. merge() takes an arbitrary number of maps or objects, and returns a single map or object that contains a merged set of elements from all arguments.

How do I merge two lists in terraform?

» concat Function concat takes two or more lists and combines them into a single list.

What is a terraform map?

Maps are a collection of string keys and string values. These can be useful for selecting values based on predefined parameters such as the server configuration by the monthly price.


2 Answers

You can actually pass a list of maps to the merge() function:

The Terraform language has a general feature for turning lists/tuples into multiple arguments, by using the special symbol ... after the last argument expression

So, in your example above, you could do:

locals {
  result = merge(module.module_name.module_op...)
}

This will then merge all of the maps in the list, allowing for dynamically sized lists.

like image 115
DJ Home Avatar answered Nov 23 '22 14:11

DJ Home


Unfortunately, the values() of your maps are lists. Otherwise you could do the following which will work for list(map(<not_list>)):

e.g.

locals {
  my_list_of_maps = [
    {
       key1 = "val1"
       key2 = "val2"
    },
    {
       key3 = "val3"
    },
  ]
}

zipmap(
  flatten(
    [for item in local.my_list_of_maps : keys(item)]
  ),
  flatten(
    [for item in local.my_list_of_maps : values(item)]
  )
)

this will process like so

zipmap(
  flatten([["key1", "key2"], ["key3"]]),
  flatten([["val1", "val2"], ["val3"]])
)

=>

zipmap(
  ["key1", "key2", "key3"],
  ["val1", "val2", "val3"]
)

=>

{
  key1 = "val1"
  key2 = "val2"
  key3 = "val3"
}

i have used a simple list(map(string)) here but the values() of the map can be more complex. however, since flatten() is used (which flattens nested lists recursively), the values() cannot be lists themselves.

in order to get this to work, you could always transform your data to look like this instead:

[
  {
    "outer_key_1" = {
      range1 = "172.16.6.0/24"
      range2 = "172.16.7.0/24"
      range3 = "172.17.6.0/24"
      range4 = "172.17.7.0/24"
    }
  },
  {
    "outer_key_2" = {
      range5 = "172.16.5.0/24"
      range6 = "172.17.5.0/24"
    }
  },
]

the above logic will work for that without a problem.

by the way, zipmap() behaves like a merge(). so if there are duplicate keys, the late arrivals will overwrite the existing keys.

like image 37
Simon Dickhoven Avatar answered Nov 23 '22 14:11

Simon Dickhoven