Terraform flatten Function

I’m having some difficulty getting the flatten function to work. Hoping somebody can point me in the right direction.

Testing in Terraform console I have the following output:

> {
>   "mfa" = [
>     "admin",
>     "super_admin",
>     "user",
>   ]
>   "non-mfa" = [
>     "admin",
>     "super_admin",
>     "user",
>   ]
> }

Generated by:

{for k, v in {for k, v in local.user_pools : k => merge(local.merge_user_pool, v)} : k => v.roles}

I now want to flatten the output, so that I can ultimately use it in a for_each declaration, i.e. the final output would look something like:

{
  "mfa" = "admin",
  "mfa" = "super_admin",
  "mfa" = "user",
  "non-mfa" = "admin",
  "non-mfa" = "super_admin",
  "non-mfa" = "user"
}

Would appreciate any help given.

The desired output you give is not possible to represent in Terraform, as it is an object with repeated attribute names. Each attribute must have a unique name.

Since your goal here is a for_each, I imagine the keys might have no particular requirement other than uniqueness. If that’s true, you might find creating composite keys suitable. Something like this:

merge([
  for role, users in local.role_users: {
    for user in users: "${role}-${user}" => user
  }
]...)

The result:

{
  "mfa-admin" = "admin"
  "mfa-super_admin" = "super_admin"
  "mfa-user" = "user"
  "non-mfa-admin" = "admin"
  "non-mfa-super_admin" = "super_admin"
  "non-mfa-user" = "user"
}

Thanks for the prompt reply alisdair.

Subsequent to my posting I came to realise the output I was looking for was not possible. However, your reply has got me thinking that the solution I need is possibly in fact:

{
  "mfa_admin" = {
    "user_pool" = "mfa"
    "role" = "admin"
  }
  "mfa-superadmin" = {
    "user_pool" = "mfa"
    "role" = "super_admin"
  }
  "mfa-user" = {
    "user_pool" = "mfa"
    "role" = "user"
  }
.
.
.
}

This is because the output is to be used in the AWS resource aws_cognito_user_group which will need to know both the User Pool, which are labelled either “mfa” or “non-mfa”, and the IAM role, which are labelled “user”, “admin” or “super_admin”.

The merge solution you proposed is a step in the right direction, but I need to work out how to set values for “user_pool” and “role” as I’ll be able to reference each.value.user_pool and each.value.role for the appropriate value.

Thanks for your help.

I think what you’re looking for is something like this, then:

locals {
  pool_roles = {
    "mfa" = [
      "admin",
      "super_admin",
      "user",
    ]
    "non-mfa" = [
      "admin",
      "super_admin",
      "user",
    ]
  }
}

output "result" {
  value = merge([
    for pool, roles in local.pool_roles : {
      for role in roles : "${pool}-${role}" => {
        "user_pool" = pool,
        "role"      = role,
      }
    }
  ]...)
}

Gives:

result = {
  "mfa-admin" = {
    "role" = "admin"
    "user_pool" = "mfa"
  }
  "mfa-super_admin" = {
    "role" = "super_admin"
    "user_pool" = "mfa"
  }
  "mfa-user" = {
    "role" = "user"
    "user_pool" = "mfa"
  }
  "non-mfa-admin" = {
    "role" = "admin"
    "user_pool" = "non-mfa"
  }
  "non-mfa-super_admin" = {
    "role" = "super_admin"
    "user_pool" = "non-mfa"
  }
  "non-mfa-user" = {
    "role" = "user"
    "user_pool" = "non-mfa"
  }
}