Alex Kuzminov
Alex Kuzminov

Reputation: 435

How can I fix for_each" value depends on resource attributes that cannot be determined until apply

Context: I'm aware of the similar questions:

but I think mine is a bit different and it might be fixed by refactoring TF code since there's an additional input restriction.

My original example is very long so I came up with a minimum viable example instead:

I've got an input variable of type map that maps all possible numbers to names:

# tfvars.terraform
all_names_by_number = {
  "1" = "alex",
  "3" = "james",
  "5" = "ann",
  "8" = "paul",
}
# main.tf
locals {
  # active_names_by_number is a map as well
  # but it's a subset of all_names_by_number
  # all_names_by_number = {
  # "3" = "james",
  # "5" = "ann",
  # }
  active_names_by_number = people_resource.example.active_names_map
}

# Resource that depedns on active_names_by_number
resource "foo" "active_items" {
  for_each = local.active_names_by_number

  name                = "abc-${each.key}"
  location            = var.location

  sub_id = data.zoo.sub[each.key].id

  bar {
    bar_name = each.value
  }
}

When I run the terraform configuration above via terraform plan, I get:

Error: Invalid for_each argument

  on main.tf line 286, in resource "foo" "active_items":
 286:   for_each = for_each = local.active_names_by_number

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.

which totally makes sense since people_resource.example.active_names_map is "initialized" in runtime from another resource (response)

locals {
  active_names_by_number = people_resource.example.active_names_map
}

but given the fact that active_names_by_number is a subset of all_names_by_number (input variable), how can I refactor the terraform configuration to show TF that local.active_names_by_number is bounded?

My ideas so far:

  1. Use count instead of for_each as other answers suggest but I do need to use each.value in my example (and I can't use all_names_by_number to create extra resources.
  2. Get rid of local.active_names_by_number and use var.all_names_by_number instead -- the major downside is TF will create extra resources which is pretty expensive.
  3. Somehow write a nested for loop:
# pseudocode
for name in var.all_names_by_number:
   if name is in people_resource.example.active_names_map:
      # create an instance of foo.active_item

Upvotes: 1

Views: 6456

Answers (1)

I also faced the same issue, so I followed the method of splitting it into two modules which @Alex Kuzminov suggested. Thanks for that.

So Instead of using for_each, use count and along with that use try block.

locals {
  active_names_by_number = try(people_resource.example.active_names_map, tolist(["a", "b", "c", "d"]))
}

So initial exception will be resolved while terraform apply, then while actual resource running, it will replace with the actual content instead of a,b,c,d.

I hope this helps. Thanks.

Upvotes: 0

Related Questions