Search code examples
terraformnested-loops

terraform nested for loop from a map with a list within


I'm trying to generate a map, from a map within a list.

locals {
  account_assignments_test = [
    {
      principal_name      = "CORP_SYSTEM_RO@hq.vw.ad",
      account             = "123456789012",
      permission_set_name = "system-audit"
    },
    {
      principal_name      = "CORP_SYSTEM_RW@hq.vw.ad",
      account             = [ "234567890123", "345678901234" ]
      permission_set_name = "system-admin"
    }
  ]
  account_assignment_map_test = {
    for pn in local.account_assignments_test : format("%v-%v-%v", pn.account, pn.principal_name, pn.permission_set_name) => pn
  }
}
output "account_assignment_map_test" {
  value = local.account_assignment_map_test
}

The outputs is clean:

Outputs:

account_assignment_map_test = {
  "123456789012-CORP_SYSTEM_RO@hq.vw.ad-system-audit" = {
    "account" = "123456789012"
    "permission_set_name" = "system-audit"
    "principal_name" = "CORP_SYSTEM_RO@hq.vw.ad"
  }
  "[\"234567890123\",\"345678901234\"]-CORP_SYSTEM_RW@hq.vw.ad-system-admin" = {
    "account" = [
      "234567890123",
      "345678901234",
    ]
    "permission_set_name" = "system-admin"
    "principal_name" = "CORP_SYSTEM_RW@hq.vw.ad"
  }
}

I do not feel comfortable with flatten, but I try it out, the idea is to loop on account first, then on principal_name (not sure if it's good idea). I've try lot of thing but without success.

  account_assignment_map_test = flatten([
      for principal_name_key, principal_name in local.account_assignments_test: [
        for account_key, account in principal_name.account : {
          principal_name_key = principal_name
          account_key  = account
#          permission_set_name_key = permission_set_name
        }
      ]
  ])

But it doesn't work, i got the following message :

│ Error: Iteration over non-iterable value
│
│   on variables_locals.tf line 66, in locals:
│   65:       for principal_name_key, principal_name in local.account_assignments_test: [
│   66:         for account_key, account in principal_name.account : {
│   67:           principal_name_key = principal_name
│   68:           account_key  = account
│   69: #          permission_set_name_key = permission_set_name
│   70:         }
│   71:       ]
│     ├────────────────
│     │ principal_name.account is "123456789012"
│
│ A value of type string cannot be used as the collection in a 'for' expression.

I would like to iterate on account list to get something like this

Outputs:

association_list = {
  "123456789012-CORP_SYSTEM_RO@hq.vw.ad-system-audit" = {
    "account" = "123456789012"
    "permission_set_name" = "system-audit"
    "principal_name" = "CORP_SYSTEM_RO@hq.vw.ad"
  }
  "234567890123-CORP_SYSTEM_RW@hq.vw.ad-system-admin" = {
    "account" = "234567890123"
    "permission_set_name" = "system-admin"
    "principal_name" = "CORP_SYSTEM_RW@hq.vw.ad"
  }
  "345678901234-CORP_SYSTEM_RW@hq.vw.ad-system-admin" = {
    "account" = "345678901234"
    "permission_set_name" = "system-admin"
    "principal_name" = "CORP_SYSTEM_RW@hq.vw.ad"
  }
}

To run a for_each from a ressource:

resource "aws_ssoadmin_account_assignment" "test" {
  for_each = local.account_assignment_map_test

  instance_arn       = local.sso_instance_arn
  permission_set_arn = aws_ssoadmin_permission_set.test[each.value.permission_set_name].arn

  principal_id   = data.aws_identitystore_group.test[each.value.principal_name].id
  principal_type = "GROUP"

  target_id   = each.value.account
  target_type = "AWS_ACCOUNT"
}

I think that I need a nested loop to solve it, but i don't know how.

And i would like to know if it's permit to go further with a new iteration but on permission_set_name:

{
  principal_name      = "CORP_SYSTEM_RW@hq.vw.ad",
  account             = [ "234567890123", "345678901234" ]
  permission_set_name = ["system-admin", "system-admin"]
}

But, I'm note sure that we can iterate infinitely ?


Solution

  • Personally I am not a fan of this type of stuff in terraform. Its meant to be a configuration language not a programming language. I prefer simple expressions. If they start to get overly complicated then to me it normally indicates an issue with the general data structure of the input. However based on your question and expected output.

    terraform {
    
    }
    
    
    locals {
      account_assignments_test    = [
        {
          principal_name      = "CORP_SYSTEM_RO@hq.vw.ad",
          account             = "123456789012",
          permission_set_name = "emobg-sso-system-audit"
        },
        {
          principal_name      = "CORP_SYSTEM_RW@hq.vw.ad",
          account             = ["234567890123", "345678901234"]
          permission_set_name = "emobg-sso-system-admin"
        }
      ]
    
      account_assignment_map_test = merge([
        for pn in local.account_assignments_test : {
          for account in try(tolist(pn["account"]), [pn["account"]]) :
            join("-", [account, pn["principal_name"], pn["permission_set_name"]]) =>
            { account = account, permission_set_name = pn["permission_set_name"], principal_name = pn["principal_name"] }
        }
      ]...)
    }
    
    output "account_assignment_map_test" {
      value = local.account_assignment_map_test
    }
    

    OUTPUT

    Outputs:
    
    account_assignment_map_test = {
      "123456789012-CORP_SYSTEM_RO@hq.vw.ad-emobg-sso-system-audit" = {
        "account" = "123456789012"
        "permission_set_name" = "emobg-sso-system-audit"
        "principal_name" = "CORP_SYSTEM_RO@hq.vw.ad"
      }
      "234567890123-CORP_SYSTEM_RW@hq.vw.ad-emobg-sso-system-admin" = {
        "account" = "234567890123"
        "permission_set_name" = "emobg-sso-system-admin"
        "principal_name" = "CORP_SYSTEM_RW@hq.vw.ad"
      }
      "345678901234-CORP_SYSTEM_RW@hq.vw.ad-emobg-sso-system-admin" = {
        "account" = "345678901234"
        "permission_set_name" = "emobg-sso-system-admin"
        "principal_name" = "CORP_SYSTEM_RW@hq.vw.ad"
      }
    }