display_name
display_name

Reputation: 129

terraform - use count and for_each together

I'm creating multiple Google Cloud Projects using a module which creates a custom service account for each one and provides that as an output eg

module "app_projects" {
  count = var.number_application_projects
  etc
}

I then have a list of IAM roles I want to assign to each Project Service account like this:

sa_roles = ["roles/run.developer","roles/appengine.deployer"]

resource "google_project_iam_member" "proj_sa_iam_roles" {
  count    = var.number_application_projects
  for_each = toset(var.sa_roles)
  project  = module.app_projects[count.index].project_id
  role     = each.value
  member   = "serviceAccount:${module.app_projects[count.index].service_account_email}"
}

This runs into the error about "count" and "for_each" being mutually-exclusive, and I can't for the life of me figure out what to use instead, any help would be appreciated!

Upvotes: 1

Views: 3089

Answers (1)

theherk
theherk

Reputation: 7576

You can probably use setproduct, but the standard method is using flatten.

They even have a section tailored for similar use cases. Flattening nested structures for for_each.

Here is an example, that doesn't use your exact resource, but should be instructive and you can actually run it to test it out.

modules/app-project/variables.tf

variable "name" {}

modules/app-project/outputs.tf

output "name" {
  value = var.name
}

modules/member/variables.tf

variable "project" {}
variable "role" {}

modules/member/outputs.tf

output "project_role" {
  value = "${var.project}-${var.role}"
}

main.tf

locals {
  roles = ["rad", "tubular"]
}

module "app_project" {
  source = "./modules/app-project"
  count  = 2

  name = "app-project-${count.index}"
}

module "project_role" {
  source = "./modules/member"
  for_each = { for pr in flatten([for p in module.app_project[*] : [
    for r in local.roles : {
      app_project_name = p.name
      role             = r
    }]
  ]) : "${pr.app_project_name}-${pr.role}" => pr }

  project = each.value.app_project_name
  role    = each.value.role
}

output "project_roles" {
  value = values(module.project_role)[*].project_role
}

terraform plan output

Changes to Outputs:
  + project_roles = [
      + "app-project-0-rad",
      + "app-project-0-tubular",
      + "app-project-1-rad",
      + "app-project-1-tubular",
    ]

In your case specifically, I think something like this would work:

resource "google_project_iam_member" "proj_sa_iam_roles" {
  for_each = { for i, pr in flatten([for p in module.app_project[*] : [
    for r in var.sa_roles : {
      app_project = p
      role        = r
    }]
  ]) : "${i}-${pr.role}" => pr }
  project = each.value.app_project.project_id
  role    = each.value.role
  member  = "serviceAccount:${each.value.app_project.service_account_email}"
}

Upvotes: 2

Related Questions