Bakka
Bakka

Reputation: 49

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      = "[email protected]",
      account             = "123456789012",
      permission_set_name = "system-audit"
    },
    {
      principal_name      = "[email protected]",
      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 = {
  "[email protected]" = {
    "account" = "123456789012"
    "permission_set_name" = "system-audit"
    "principal_name" = "[email protected]"
  }
  "[\"234567890123\",\"345678901234\"][email protected]" = {
    "account" = [
      "234567890123",
      "345678901234",
    ]
    "permission_set_name" = "system-admin"
    "principal_name" = "[email protected]"
  }
}

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 = {
  "[email protected]" = {
    "account" = "123456789012"
    "permission_set_name" = "system-audit"
    "principal_name" = "[email protected]"
  }
  "[email protected]" = {
    "account" = "234567890123"
    "permission_set_name" = "system-admin"
    "principal_name" = "[email protected]"
  }
  "[email protected]" = {
    "account" = "345678901234"
    "permission_set_name" = "system-admin"
    "principal_name" = "[email protected]"
  }
}

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      = "[email protected]",
  account             = [ "234567890123", "345678901234" ]
  permission_set_name = ["system-admin", "system-admin"]
}

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

Upvotes: 0

Views: 1233

Answers (2)

Bakka
Bakka

Reputation: 49

Thanks Chris Doyle with the answer above, and to go further, because I wanted to have the possibility of also having a list at the level of permission_set_name

terraform {

}

locals {
  account_assignments = [
    {
      principal_name      = "[email protected]",
      account             = "111111111111"
      permission_set_name = "system-foo"
    },
    {
      principal_name      = "[email protected]",
      account             = ["111111111111", "222222222222"]
      permission_set_name = ["network-foo", "network-bar"]
    }
  ]

  account_assignment_maps = merge([
    for pn in local.account_assignments : {
      for key, account in try(tolist(pn["account"]), [pn["account"]]) :
      join("-", [pn["principal_name"], account, key]) => {
        for permission_set_name in try(tolist(pn["permission_set_name"]), [pn["permission_set_name"]]) :
        join("-", [pn["principal_name"], account, permission_set_name]) =>
        { principal_name = pn["principal_name"], account = account, permission_set_name = permission_set_name }
      }
    }
  ]...)

  account_assignment_flat = flatten([
    for policy, policies in local.account_assignment_maps : [
      for key, value in policies : {
        principal_name      = value.principal_name
        account             = value.account
        permission_set_name = value.permission_set_name
      }
    ]
  ])

  account_assignment_map = {
    for policy in local.account_assignment_flat : format("%s-%s-%s", policy.principal_name, policy.account, policy.permission_set_name) => policy
  }
}

output "account_assignment_map" {
  value = local.account_assignment_map
}

Outputs

Outputs:

account_assignment_map = {
  "[email protected]" = {
    "account" = "111111111111"
    "permission_set_name" = "system-foo"
    "principal_name" = "[email protected]"
  }
  "[email protected]" = {
    "account" = "111111111111"
    "permission_set_name" = "network-bar"
    "principal_name" = "[email protected]"
  }
  "[email protected]" = {
    "account" = "111111111111"
    "permission_set_name" = "network-foo"
    "principal_name" = "[email protected]"
  }
  "[email protected]" = {
    "account" = "222222222222"
    "permission_set_name" = "network-bar"
    "principal_name" = "[email protected]"
  }
  "[email protected]" = {
    "account" = "222222222222"
    "permission_set_name" = "network-foo"
    "principal_name" = "[email protected]"
  }
}

Upvotes: 0

Chris Doyle
Chris Doyle

Reputation: 12209

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      = "[email protected]",
      account             = "123456789012",
      permission_set_name = "emobg-sso-system-audit"
    },
    {
      principal_name      = "[email protected]",
      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 = {
  "[email protected]" = {
    "account" = "123456789012"
    "permission_set_name" = "emobg-sso-system-audit"
    "principal_name" = "[email protected]"
  }
  "[email protected]" = {
    "account" = "234567890123"
    "permission_set_name" = "emobg-sso-system-admin"
    "principal_name" = "[email protected]"
  }
  "[email protected]" = {
    "account" = "345678901234"
    "permission_set_name" = "emobg-sso-system-admin"
    "principal_name" = "[email protected]"
  }
}

Upvotes: 1

Related Questions