Nov 08 2022

Turn a Terraform Map of Maps into a Single Map…

Published by at 10:10 am under DevOps,Terraform

…Or how to avoid the ‘Call to function “merge” failed: arguments must be maps or objects, got “tuple”.’ error message. Maps of maps example.

maps pages papers vintage 1854199


Issue Producing a Single Map


Here’s a typical 2 level data structure that fits users in subgroups, themselves belonging to departments:

locals {
   groups = {
      it = {
         admin   = ["it1","it2"]
         editors = ["it3","it4"]
      },
      accounts = {
         editors = ["account1","account2"]
         viewers = ["account3","account4"]
      }
   }
}


There are good chances you will need to flatten that structure to create some Terraform ressources.
Our target is a map of unique subgroups department_subgroup = [ “user1”, “user2” ]. In our example:

subgroups = {
  "accounts_editors" = [
    "account1",
    "account2",
  ]
  "accounts_viewers" = [
    "account3",
    "account4",
  ]
  "it_admin" = [
    "it1",
    "it2",
  ]
  "it_editors" = [
    "it3",
    "it4",
  ]
}


It is pretty simple to get a list of distinct maps flattening the resulting lists of a double loop:

subgroups = flatten([
   for group,subgroups in local.groups : [
      for subgroup, users in subgroups : {
         "${group}_${subgroup}" = users
      }
   ]
])
# output:
subgroups = [
  {
    "accounts_editors" = [
      "account1",
      "account2",
    ]
  },
  {
    "accounts_viewers" = [
      "account3",
      "account4",
    ]
  },
  {
    "it_admin" = [
      "it1",
      "it2",
    ]
  },
  {
    "it_editors" = [
      "it3",
      "it4",
    ]
  },
]

All we need is merge these maps into a single map but if we do, we end up with:
‘Call to function “merge” failed: arguments must be maps or objects, got “tuple”.’

Call to function "merge" failed: arguments must be maps or objects, got "tuple"
Call to function “merge” failed: arguments must be maps or objects, got “tuple”


2 ways to the rescue: the ugly and the elegant.


The Ugly Way : Another Terraform Loop


The first way you can think of is to process the new map through another for loop. That makes 3 loops and does not make any sense since a simple merge would do the job. Each map has one element only, so we take the first key and first value.

subgroups = { for subgroup in flatten([
   for group,subgroups in local.groups : [
      for subgroup, users in subgroups : {
         "${group}_${subgroup}" = users
      }
   ]
]) : keys(subgroup)[0] => values(subgroup)[0] }


The Elegant Way : Function Expansion


This is much shorter than above solution:

subgroups = merge(flatten([
   for group,subgroups in local.groups : [
      for subgroup, users in subgroups : {
         "${group}_${subgroup}" = users
      }
   ]
])...)

The result is exactly the same but what has changed? Just the 3 dots…
Expansion takes each element out of a list and pass them on to the calling function.


No responses yet

Trackback URI | Comments RSS

Leave a Reply