Add element to a map where the value depends on some expression

I use terraform to deploy to GCP and there’s bunch of resources that I want to deploy to all my projects hence I have a set of maps to collate information about each project:

echo "local.projects" | terraform console
toset([
  {
    "environment" = "dev"
    "project_id" = "ourorg-cdm-dev"
    "stem" = "cdm"
  },
  {
    "environment" = "dev"
    "project_id" = "ourorg-tabboardpoc-dev"
    "stem" = "tabboardpoc"
  },
  {
    "environment" = "prod"
    "project_id" = "ourorg-cdm-prod"
    "stem" = "cdm"
  },
  {
    "environment" = "test"
    "project_id" = "ourorg-cdm-test"
    "stem" = "cdm"
  },
])

and I then pass that set to my resources and use for_each to deploy stuff to all my projects - works great!

I now want to deploy a GKE cluster to some of those projects, but not to all of them. Hence I want to add a boolean element to each map stating whether or not a GKE cluster should be deployed or not and the logic is

if stem == “cdm”, then gke_cluster = true else false

In other words I want to end up with this:

toset([
  {
    "environment" = "dev"
    "project_id" = "ourorg-cdm-dev"
    "stem" = "cdm"
    "gke_cluster" = true
  },
  {
    "environment" = "dev"
    "project_id" = "ourorg-tabboardpoc-dev"
    "stem" = "tabboardpoc"
    "gke_cluster" = false
  },
  {
    "environment" = "prod"
    "project_id" = "ourorg-cdm-prod"
    "stem" = "cdm"
    "gke_cluster" = true
  },
  {
    "environment" = "test"
    "project_id" = "ourorg-cdm-test"
    "stem" = "cdm"
    "gke_cluster" = true
  },
])

I’ve fiddled around with for expressions but can’t figure out the correct syntax in order to construct what I need. Can anyone here figure it out given I’m not having much luck?

Hi @jamiekt,

It sounds like your goal here is to pre-compute that derived gke_cluster attribute so that you can write that expression down in only one place and have all of the other references use the final boolean value.

In that case, I’d probably try something like this:

locals {
  projects_extended = {
    for p in local.projects : merge(p, {
      gke_cluster = (p.stem == "cdm")
    })
  }
}

This uses the merge function to blend a new attribute into the given objects, and then derives that attribute’s value from another attribute of the same object. You can extend the object given in the second argument to merge to include any other derived attributes you might need.

Thank you @apparentlymart, that’s very cool. It didn’t occur to me that I could reference merge’s first argument in the second argument.

I had to modify very slightly to this:

locals {
  projects_extended = [
    for p in local.projects : merge(p, {
      gke_cluster = (p.stem == "cdm")
    })
  ]
}

because I’m starting from a set but it basically worked great. Thank you very very much.

Oh yes, you’re right… I did initially write this out as a for expression to produce a map and then later noticed you were using a set, but I forgot to change the brackets. Whoops! I’m glad you got it working, though.

I have a follow-up question where I’m trying to use this local but am hitting a problem with configuring the helm provider which requires a specific cluster whereas I have many Configuring helm provider to work for multiple GKE clusters