Run for loop over nested local structure

Hi,
I’m trying to run for loop over the local structure like:

locals {

  aws_iam_roles = {

    role1 = {
      role_name = "Role1"
      role_policies   = ["Policy1", "Policy2"]
    }

    role2 = {
      role_name = "Role2"
      role_policies = ["Policy3"]
    }
  }
}

to achieve structure like:

{
Role1 = Policy1
Role1 = Policy2
Role2 = Policy3
}

I will appreciate any suggestions. Best Regards!

Update:
What I’m done is:

locals {
  policy_role_pairs = [
    for r, p in local.aws_iam_roles : {
      for n in p.role_policies : 
        n => p.role_name
    }
  ]
}

But it produces:

policy_role_pairs = [
  {
    "Policy1" = "Role1"
    "Policy2" = "Role2"
  },
  {
    "Policy3" = "Role3"
  },
]

Which is nice but I cannot run for_each on that structure which is my final goal.

Hi @revaner,

I think part of the problem here is that the structure you are looking for isn’t valid. The object or map shown here contains a duplicate key:

{
Role1 = Policy1
Role1 = Policy2
Role2 = Policy3
}

Would something more like this suit your needs?

> flatten([ for p in local.aws_iam_roles :
  [ for n in p.role_policies : { (p.role_name) = n } ]
])

[
  {
    "Role1" = "Policy1"
  },
  {
    "Role1" = "Policy2"
  },
  {
    "Role2" = "Policy3"
  },
]

Hi @jbardin
Many thanks, it’s producing pairs as expected but I still wonder how to make it work with for_each loop on that output?

How to make what work exactly? If you are trying to use the key-value pairs in a resource for_each expression, you would have the same problem that made the first example invalid, there are duplicate keys in the data. You will always need unique keys for individual instances, just like you need unique keys for each map value. Can you show the actual final data you would need for the resource in question?

Ouh, I wasn’t aware that for for_each I need unique keys but I’ve tested and indeed. Maybe I’ve started story from the middle. Let me describe whole scenario.

I’m working on AWS IAM module which will do few things:

  1. Create IAM Roles
  2. Get IAM Policy ARN
  3. Attach IAM Policies to IAM Roles

My current local values are:

locals {

  aws_iam_roles = {

    role1 = {
      role_name = "Role1"
      role_policies   = ["Policy1", "Policy2"]
    }

    role2 = {
      role_name = "Role2"
      role_policies = ["Policy3"]
    }
  }
}

Based on that local input I’m creating IAM Roles and using data block to get all Policies ARNs.

locals {
  role_policies_merged = toset(flatten([for r, p in local.aws_iam_roles : p.role_policies]))
  policy_role_pairs    = flatten([for r, p in local.aws_iam_roles : [for n in p.role_policies : { (p.role_name) = n }]])
}
data "aws_iam_policy" "role_policy" {
  for_each = local.role_policies_merged
  name     = each.value
}

Here I need the pairs: Role - Policy to run for_each over them.
My current issue is here:

resource "aws_iam_role_policy_attachment" "role_policy" {
  for_each   = { for r, p in local.policy_role_pairs : r => p }
  role       = each.key
  policy_arn = data.aws_iam_policy.role_policy[each.key].arn
}

I hope now it’s more clear.

You must have a unique key for each instance. One option is to create the key from the combination of the key-value pair. Maybe something like this (just typing this out, so assignments and values may not be quite correct)

policy_role_pairs = { for pair in concat([ for role_name, role in local.aws_iam_roles : 
    setproduct([role_name], role.role_policies)]...) : 
  format("%s_%s", pair...) => pair
}
...
resource "aws_iam_role_policy_attachment" "role_policy" {
  for_each   = for each in local.policy_role_pairs 
  role       = each[0]
  policy_arn = data.aws_iam_policy.role_policy[each[1]].arn
}

Hello @jbardin ,
Thank you for your quick and amazing support.
It works for me now with grouping Role - Policy pairs with unique index following your suggestion and attachment block like:

resource "aws_iam_role_policy_attachment" "role_policy" {
  for_each = local.policy_role_pairs
  role     = each.value[0]
  policy_arn = data.aws_iam_policy.role_policy[each.value[1]].arn
}

Best Regards!