How to combine multiple for_each resources outputs to be used in another for_each resource

55 Views Asked by At

I have a terraform project to configure our Gitlab repos:

variable "backend_repos" {
  type    = list(string)
  default = ["backend-1", "backend-2"]
}

variable "web_repos" {
  type    = list(string)
  default = ["web-1", "web-2"]
}

resource "gitlab_project" "backend" {
  for_each = var.backend_repos
  name = each.key
  ...
}

resource "gitlab_project" "web" {
  for_each = var.web_repos
  name = each.key
  ...
}

I then then want to configure configue branch protections for both backend and web repos in the exact same way, but I can't figure out how to combine the two resources.

An incorrect example would be:

resource "gitlab_branch_protection" "develop" {
  for_each = { for project in concat(gitlab_project.backend, gitlab_project.web) : project.name -> project.id }
  project = each.value
  branch  = "develop"
  ...
}

I've tried so many different ways but none of them seem to work:

  • { for project in concat(gitlab_project.backend, gitlab_project.web) : project.name -> project.id }
  • { for project in concat(gitlab_project.backend[*], gitlab_project.web[*]) : project.name -> project.id }
  • { for project in setunion(gitlab_project.backend, gitlab_project.web) : project.name -> project.id }
  • { for project in setunion(gitlab_project.backend[*], gitlab_project.web[*]) : project.name -> project.id }

If however I change to just a single one of the resources, it works as expected:

resource "gitlab_branch_protection" "develop" {
  for_each = { for project in gitlab_project.backend : project.name -> project.id }
  project = each.value
  branch  = "develop"
  ...
}
1

There are 1 best solutions below

0
On

TDLR

Use merge without any wildcards:

resource "gitlab_branch_protection" "develop" {
  for_each = { for project in merge(gitlab_project.backend, gitlab_project.web) : project.name -> project}
  project = each.value.id
  branch  = "develop"
  ...
}

Explanation

concat and setunion produce a list of nested objects when used with wildcards (e.g. concat(gitlab_project.backend[*], gitlab_project.web[*])):

[
  {
    "backend-1": {
      "id": 1
      "name": "backend-1",
      ...
    },
    "backend-2": {
      "id": 2
      "name": "backend-2",
      ...
    }
  },
  {
    "web-1": {
      "id": 3
      "name": "backend-1",
      ...
    },
    "web-2": {
      "id": 4
      "name": "web-2",
      ...
    }
  }
]

whereas merge produces a flattened map of the two:

{
  "backend-1": {
    "id": 1
    "name": "backend-1",
    ...
  },
  "backend-2": {
    "id": 2
    "name": "backend-2",
    ...
  },
  "web-1": {
    "id": 3
    "name": "backend-1",
    ...
  },
  "web-2": {
    "id": 4
    "name": "web-2",
    ...
  }
}