How to flatten sets within setproduct loop?

Summary

Hi, I’m looking for a way to flatten() between setproduct() and for_each. I’ve provided examples stepping through my progress to make the query clearer. Thanks for your time.

Examples

Simple

For example, to iterate over a set, I would:

env = toset(["dev", "test", "qa", "staging", "prod"])
...
for_each = local.env

# "dev", "test", "qa", "staging", "prod"

Product

For example, to iterate over all possible combinations of two sets, I would:

env  = toset(["dev", "test", "qa", "staging", "prod"])
area = toset(["front", "back", "mid"])
...
for_each  = { for v in setproduct(local.env, local.area) : "${v[0]}-${v[1]}" => v }

# "front-dev", "front-test", "front-qa", "front-staging", "front-prod"
# "back-dev",  "back-test",  "back-qa",  "back-staging",  "back-prod"
# "mid-dev",   "mid-test",   "mid-qa",   "mid-staging",   "mid-prod"

Mixed

As for my query, how do I go about mixing flatten() and setproduct() within nested sets in for_each for the desired output shown below?

env = {
  "uat"  = toset(["dev", "test", "qa"])
  "live" = toset(["staging", "prod"])
}
area = toset(["front", "back", "mid"])
...
# Desired output ->
# "uat-front-dev", "uat-front-test", "uat-front-qa", "live-front-staging", "live-front-prod"
# "uat-back-dev",  "uat-back-test",  "uat-back-qa",  "live-back-staging",  "live-back-prod"
# "uat-mid-dev",   "uat-mid-test",   "uat-mid-qa",   "live-mid-staging",   "live-mid-prod"

Thanks,
Rishav

locals {
  env = {
    "uat"  = toset(["dev", "test", "qa"])
    "live" = toset(["staging", "prod"])
  }
  area = toset(["front", "back", "mid"])

  product = flatten([for k, v in local.env : [for s in setproduct(local.env[k], local.area) : "${k}-${s]0]}-${s[1]}"]])
}

output "product" {
  value = local.product
}

# This gives you your desired output in to products
# "uat-front-dev", "uat-front-test", "uat-front-qa", "live-front-staging", "live-front-prod"
# "uat-back-dev",  "uat-back-test",  "uat-back-qa",  "live-back-staging",  "live-back-prod"
# "uat-mid-dev",   "uat-mid-test",   "uat-mid-qa",   "live-mid-staging",   "live-mid-prod"
1 Like

No way, that is exactly what I was looking for! Huge thanks for sharing your response.
I have to ask, how did you figure this out: any learning resources? It’d be great if I can become comfortable with more complex/nested for loops.

For my for_each use-case, I’ve adapted it as follows:

for_each = toset(flatten([for k, v in local.env : [for s in setproduct(local.env[k], local.area) : "${k}-${s[0]}-${s[1]}"]]))

Where I can still refer to each individual segment using "${split("-", each.value)[1]}, which is a bit of a mouthful but works perfectly fine.

Thanks once again for your time,
Rishav

1 Like

Glad that it worked for your @Rishav !! I was mentored by good people I guess. I always refer to terraform doco, that is the best place.

1 Like

Along this same thread, is it possible to adapt your solution to flatten() just the set of strings like so:

locals {
  env = {
    "uat"  = toset(["dev", "test", "qa"])
    "live" = toset(["staging", "prod"])
  }
}

# Desired output ->
# "uat-dev", "uat-test", "uat-qa", "live-staging", "live-prod"

I’ve found the following to be a valid workaround, but I reckon there has to be a clearer method:

product = flatten([for k, v in local.env : [for s in setproduct(local.env[k], [""]) : "${k}-${s[0]}"]])

Thanks,
Rishav

I don’t think we even need to use setproduct here:

product = flatten([for k, v in local.env : [for s in v : "${k}-${s}"]])
1 Like

Thanks once again, you make it look easy!