“Invalid for_each argument” with flattened maps

Hi all,

I again have issues with Terraform failing with Invalid for_each argument, this time using flattened maps.

Version:

Terraform v0.15.3
on linux_amd64
+ provider registry.terraform.io/hashicorp/null v3.1.0

Here is an simplified example of the original code to reproduce:

locals {
  networks = {
    "network-1" : { "range" = "1.2.3.0/24" },
    "network-2" : { "range" = "5.6.7.0/24" }
  }

  servers = {
    "server-1" : {
      name = "server-1"
      networks = [
        {
          "name"       = "network-1"
          "network_id" = null_resource.networks["network-1"].id
        },
        {
          "name"       = "network-2"
          "network_id" = null_resource.networks["network-2"].id
        }
      ]
    }
  }

  server_networks = {
    for network in flatten([
      for server in local.servers : [
        for index in range(0, length(server.networks)) : merge(element(server.networks, index), {
          "name"   = "network-${index}"
          "server" = server.name
        })
      ] if(try(server.networks, null) != null)
    ]) : "${network.server}:${network.name}" => network
  }
}

resource "null_resource" "networks" {
  for_each = local.networks
}

resource "null_resource" "servers" {
  for_each = local.servers
}

resource "null_resource" "server_networks" {
  for_each = local.server_networks
}

output "networks" {
  value = null_resource.networks
}

output "servers" {
  value = null_resource.servers
}

output "server_networks" {
  value = null_resource.server_networks
}

which gives:

Error: Invalid for_each argument

  on main.tf line 45, in resource "null_resource" "server_networks":
  45:   for_each = local.server_networks
    ├────────────────
    │ local.server_networks will be known only after apply

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.

Using a staged apply works as expected, but not from scratch.

I’m aware of the for_each requirements, where the keys (or number of objects) must be known at the planning stage, which I assume is fulfilled in the sample.

Do I miss something?

1 Like

Here is the final output using staged apply:

networks = {
  "network-1" = {
    "id" = "5824789619747750296"
  }
  "network-2" = {
    "id" = "4601793179585989471"
  }
}

servers = {
  "server-1" = {
    "id" = "8273298805161842204"
  }
}

server_networks = {
  "server-1:network-0" = {
    "id" = "337995223402608304"
  }
  "server-1:network-1" = {
    "id" = "6270752628704934627"
  }
}

Duh, it actually works without the if!

  server_networks = {
    for network in flatten([
      for server in local.servers : [
        for index in range(0, length(server.networks)) : merge(element(server.networks, index), {
          "name"   = "network-${index}"
          "server" = server.name
        })
      ]
    ]) : "${network.server}:${network.name}" => network
  }
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.

Outputs:

networks = {
  "network-1" = {
    "id" = "675054097999314403"
  }
  "network-2" = {
    "id" = "8696077582297215930"
  }
}

servers = {
  "server-1" = {
    "id" = "5721401963329095335"
  }
}

server_networks = {
  "server-1:network-0" = {
    "id" = "3006102988268496706"
  }
  "server-1:network-1" = {
    "id" = "3224933364804687326"
  }
}