The "for_each" value depends on resource attributes that cannot be determined until apply

As so many people before me (and most probably after me), I ran into the following dreaded problem:

The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.

I did some digging and I must admit that no explanation really gives full insights into what the actual problem is. I thought I saw someone mentioning somewhere that for_each can’t rely on variable parameters, which seems a bit silly as that is maybe one of the only reasons it’s useful to use (or at least a very important one).

I’ll illustrate with an example. In my code I’m creating a number of service accounts:

resource "google_service_account" "service_account_one" {
  project     = var.project_id
  account_id  = "sa-one"
}

resource "google_service_account" "service_account_two" {
  project     = var.project_id
  account_id  = "sa-two"
}

Now I want to give these service accounts a set of roles on particular objects. For example, granting them access to a storage bucket. Instead of copy/pasting the same resource 3 times, I thought I’d be smart and solve it with a locals list.

locals {
  service_account_emails = [
    google_service_account.service_account_one.email,
    google_service_account.service_account_two.email,
  ]
}

And then give them the desired IAM role:

resource "google_storage_bucket_iam_member" "configuration_access" {
  for_each = toset(local.service_account_emails)
  bucket   = google_storage_bucket.configuration_bucket.name
  member   = "serviceAccount:${each.value}"
  role     = "roles/storage.admin"
}

But, as you already guessed:

β•·
β”‚ Error: Invalid for_each argument
β”‚ 
β”‚   on ../../../main.tf, in resource "google_storage_bucket_iam_member" "configuration_access":
β”‚  345:   for_each = toset(local.service_account_emails)
β”‚     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚     β”‚ local.service_account_emails is tuple with 2 elements
β”‚ 
β”‚ The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.
β•΅

So, can someone shed some light on what is happening in the background here? As you can see, it’s a fixed list with 2 elements, so I have no idea why Terraform can’t predict how many elements are going to be created.

This isn’t the first time I’m running into this problem. I have other examples where it’s problematic as well, especially with flattened lists and passing in variables into a module.

Is there work underway to solve this problem or at least give a better error message and guidance on how to solve this?

Hi @debakkerbjorn,

The problem here is not that terraform cannot determine the length of the list, in fact you could use count = length(local.service_account_emails) and it should work just fine. The problem is that the values of the list are unknown, so there is no way to determine what the index key for each of your resource instances is going to be.

The simplest resolution here, since you already are statically defining the structure of the values, is to use a map with predefined keys rather than a set.

locals {
  service_account_emails = {
    one = google_service_account.service_account_one.email,
    two = google_service_account.service_account_two.email,
  }
}
1 Like

Wanted to say that this solution saved me a lot of headache trying to solve this for_each problem. Changing it to a map with the keys did indeed satisfy Terraform to ignore the β€œunknown” number of objects generated from my other modules I was passing in. Thank you!