0

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 ?

1
  • are you in control of the input data structure? myabe there is a better way to structure the source data Commented Oct 30, 2022 at 16:28

2 Answers 2

1

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]"
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot, and as you say, this kind of code is much more complicated to maintain. But I don't think there's another way to do it without multiplying the number of line in var files, because the account are grouped in list and would be associate to many of those rules. By doing it this way I divide by 50 the number of map to place in variable file. Maybe the idea of using a pre-script to generate the mapping before should be better, I'll think about it.
0

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]"
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.