For_each over which data structure?

I’ve attempted to get this working many, many different ways but keep falling short with the implementations. I need to grab a list of server names from a map and get the product of that list and another list of ports in order to iterate over each grouping into a bigip_ltm_pool_attachment resource block:

    node   = {
      "node1" = "192.168.1.1"
      "node2" = "192.168.1.2"
    }
    ports = [7001,7002,7003,7004,7005,7006,7007,7008,7009,7010]

    poolattachports = [for pair in setproduct(keys(local.node), local.ports): {host: pair[0], port: pair[1]}]

    resource "bigip_ltm_pool_attachment" "pool_attach" {
      for_each = ...
    }

The high level idea is to create an attachment for each possible combination of server/port. I’ve never really seen an example online for this where you start with 2 lists and create a data structure that can be for_each’d in a resource. The only way I’ve seen it done is with dynamic blocks and I don’t think they apply here, right?

Hi @rleonetti,

The main thing about for_each is to produce a data structure that has one element for each instance you want to create and each element contains enough information to populate the resource configuration.

Your poolattachports expression here seems to meet that requirement. I’m not really familiar with the bigip provider but based on a quick read of the documentation I think the following would work:

locals {
  nodes = {
    "node1" = "192.168.1.1"
    "node2" = "192.168.1.2"
  }
  ports = [7001,7002,7003,7004,7005,7006,7007,7008,7009,7010]
}

resource "bigip_ltm_node" "example" {
  for_each = local.nodes

  name    = "/Common/${each.key}"
  address = each.value
}

locals {
  node_ports = [for pair in setproduct(keys(bigip_ltm_node.example), local.ports): {host: pair[0], port: pair[1]}]
}

resource "bigip_ltm_pool_attachment" "attach_node" {
  for_each = { for np in local.node_ports : "${np.host}:${np.port}" => np }

  pool = bigip_ltm_pool.example.name
  node = each.key
}

The local.node_ports above is essentially the same as your poolattachports except that it refers to bigip_ltm_node.example instead of directly to local.nodes; that tells Terraform that the bigip_ltm_pool_attachment resource depends (indirectly) on the bigip_ltm_node resource, so the pool attachments won’t be created until both the nodes and pool (not shown) have been created.

It looks like the node argument of bigip_ltm_pool_attachment expects a node:port string, and Terraform also requires a unique string to identify each of the instances so I wrote this to use the same string for both purposes, which causes it to appear as each.key inside the resource block. Terraform will therefore understand these instances as having addresses like this:

  • bigip_ltm_pool_attachment.attach_node["node1:7001"]
  • bigip_ltm_pool_attachment.attach_node["node1:7002"]
  • bigip_ltm_pool_attachment.attach_node["node2:7001"]
  • bigip_ltm_pool_attachment.attach_node["node2:7002"]
1 Like

Thank you! The only change I made was:

  node     = "${bigip_ltm_node.node[each.value["host"]].name}:${each.value["port"]}"

I’m so relieved to have this working lol