For_each invalid argument - values derived from resources attributes

I have seen this in a few example and in the official documentation.

data "kubectl_path_documents" "karpenter_provisioners" {
  pattern = "${path.module}/provisioners/*.yaml"
  vars = {
    azs                     = join(",", var.vpc_config.azs)
    subnets                 = join(",", module.vpc_eks.private_subnets)
    instance_types          = join(",", var.ng_config.instance_types)
    instance_profile        = module.eks_bp.managed_node_group_iam_instance_profile_id[0]
    eks_cluster_id          = local.cluster_name
    eks_vpc_name            = module.vpc_eks.name
    security_group          = module.eks_bp.worker_node_security_group_id
    ng_volume_size          = var.ng_config.volume_size
  }
}

resource "kubectl_manifest" "karpenter_provisioner" {
  for_each  = toset(data.kubectl_path_documents.karpenter_provisioners.documents)
  yaml_body = each.value

  depends_on = [module.eks_bp_k8s_addons]
}

Error

 Error: Invalid for_each argument
│
│   on karpenter.tf line 57, in resource "kubectl_manifest" "karpenter_provisioner":
│   57:   for_each  = toset(data.kubectl_path_documents.karpenter_provisioners.documents)
│     ├────────────────
│     │ data.kubectl_path_documents.karpenter_provisioners.documents is a list of string, known only after apply
│
│ The "for_each" set includes values derived from resource attributes that cannot be determined until apply, and so Terraform cannot determine the full set of keys that will identify the instances of this resource.
│
│ When working with unknown values in for_each, it's better to use a map value where the keys are defined statically in your configuration and where only the values contain apply-time results.
│
│ Alternatively, you could use the -target planning option to first apply only the resources that the for_each value depends on, and then apply a second time to fully converge.

Associated doc: Terraform Registry

Is there a work around that does include, commenting out the resource “kubectl_manifest” do an apply, then uncomment and do an apply again?

OH, I solved my own issue. The example I based this on uses ${path.module} variable. When I changed do using ‘./provisioners/*.yaml’ it worked.

Hi @ned.hanks,

It seems like there isn’t really any alternative to this result only being known after apply, because the data block configuration itself depends on information that can’t be known until some other objects have been created.

Therefore I think the final paragraph of the error message is the solution that applies in your case. You can run the following series of commands to bootstrap this configuration:

  • terraform apply -target=data.kubectl_path_documents.karpenter_provisioners
  • terraform apply

The first command above constrains Terraform to perform only the actions required to successfully read that data source. After that, all of the necessary objects will already be known to Terraform and so you can just use terraform apply normally moving forward, as long as you don’t replace any of the objects whose results are used by this data source. (If you do replace any of them then you can repeat this extra bootstrapping step to get back to normal again).

There might be ways to refactor to avoid this extra step, but I expect it will require quite a different approach than you have taken so far and so I can’t advise on that in detail without being able to see your entire configuration to understand the data flows.

Changing from path.module unfortunately doesn’t seem like a plausible solution to this problem because Terraform always knows the path to the current module during planning. I think something else must have changed between your runs that made it work.

1 Like