Home > Net >  Nested loops and looping over maps in Terraform
Nested loops and looping over maps in Terraform

Time:02-25

I am writing a module that needs to create "aws_iam_group_policy_attachment" resources. This resource requires a group name (string) and a policy name (string). I tried using different data structures, but the ones that make most sense look like this:

List of maps:

iam_groups = [
    { name = "testgroup1", policies = [ "Arn1", "Arn2" ] },
    { name = "testgroup2", policies = [ "Arn1", "Arn3", "Arn4" ] }
  ]

Map of maps:

iam_groups = {
  "testgroup1" = { name = "testgroup1", policies = ["Arn1", "Arn2" ] }
  "testgroup2" = { name = "testgroup2", policies = ["Arn1", "Arn3", "Arn4" ] }
}

Because that resource doesn't support supplying a list of policies, my thinking was that I need to create a structure that supports every combination of Group:Policy per group, so I defined a helper local variable with nested for loops:

groups = flatten([ for group in var.iam_groups : [ for policy in group.policies : { (group.name) = policy } ] ])

This gives me the correct combination of values:

    GROUPS  = [
        {
            testgroup1 = "Arn1"
        },
        {
            testgroup1 = "Arn2"
        },
        {
            testgroup2 = "Arn1"
        },
        {
            testgroup2 = "Arn3"
        },
        {
            testgroup2 = "Arn4"
        },
    ]

Now, my question is how to use this in the "aws_iam_group_policy_attachment" resource?

Also, is there some other, better way to structure my data to achieve this?

I tried using:

resource "aws_iam_group_policy_attachment" "this" {
  for_each = local.groups

  group = each.key
  policy_arn = each.value
}

But this gives an error that "for_each" requires a map or list of strings where I have a "tuple" with multiple elements.

What I want to avoid is maintaining a variable that has to manually and statically map all groups to each individual policy with a lot of repetition.

CodePudding user response:

It would be better to use map for the for each, not list, as list depends on the order of the items:


locals {

  groups = merge([ for group in var.iam_groups : {
                    for policy in group.policies :  
                          "${group.name}-${policy}" => { 
                                policy = policy 
                                group_name =  group.name
                                }
                    } ]...) # please do NOT remove the dots                       
}

then

resource "aws_iam_group_policy_attachment" "this" {
  for_each = local.groups

  group = each.value.group_name
  policy_arn = each.value.policy
}
  • Related