How to use count and for_each toghther?

Hello. when I use count and for_each , I have below issue:
if var.environment=dev , I’ll create vpc_endpoint like below:

resource "aws_vpc_endpoint" "vpce_mct_edmz" {
  count = var.environment == "dev" ? 1:0
  vpc_id            = module.edmz-vpc.vpc_id
  service_name      = aws_vpc_endpoint_service.mct_endpoint_service[count.index].service_name
  vpc_endpoint_type = "Interface"

  security_group_ids = [aws_security_group.vpce_sg_edmz.id]

  subnet_ids          = module.edmz-vpc.private_subnet_ids
  private_dns_enabled = false
  tags = merge(local.common_tags, { "Name" = "vpce_mct_edmz" })
}

and if dev ,I also need to use the aws_network_interface ip of endpoint like below:

data "aws_network_interface" "vpce_mct_edmz_eni" {
  for_each = var.environment == "dev" ? data.aws_vpc_endpoint.vpce_mct_edmz.network_interface_ids :{}
  id       = each.value
}

it will raise below error:

│   on data.tf line 96, in data "aws_network_interface" "vpce_mct_edmz_eni":
│   96:   for_each = var.environment == "dev" ? data.aws_vpc_endpoint.vpce_mct_edmz.network_interface_ids :{}
│
│ Because data.aws_vpc_endpoint.vpce_mct_edmz has "count" set, its attributes must be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│     data.aws_vpc_endpoint.vpce_mct_edmz[count.index]

also try like below :

data "aws_network_interface" "vpce_mct_edmz_eni" {
  for_each = var.environment == "dev" ? data.aws_vpc_endpoint.vpce_mct_edmz[count.index].network_interface_ids :{}
  id       = each.value
}

it will raise below error:

 Error: Reference to "count" in non-counted context
│
│   on data.tf line 96, in data "aws_network_interface" "vpce_mct_edmz_eni":
│   96:   for_each = var.environment == "dev" ? data.aws_vpc_endpoint.vpce_mct_edmz[count.index].network_interface_ids :{}
│
│ The "count" object can only be used in "module", "resource", and "data" blocks, and only when the "count" argument is set.
╵

who know how to achieve my request ? thx.

Hi @bob.j.zhang,

When you declare a resource using count, its value for references elsewhere in the module is a list of objects rather than a single object, and so you need to tell Terraform which element of that list you want to use.

However, in your case you have written a count expression that can only possibly produce 0 or 1 as its result, so you can assume that there will either be zero or one elements of the list, and use the length of the list as the guarding condition like this.

For example:

  for_each = (
    length(data.aws_vpc_endpoint.vpce_mct_edmz) != 0 ?
    data.aws_vpc_endpoint.vpce_mct_edmz[0].network_interface_ids :
    {}
  )

The condition predicate makes sure that there’s at least one element in the list, and so [0] is definitely a valid index to access.

The error message talks about count.index because it’s guessing that you might have intended to correlate between instances of multiple resources that all have the same count value, but that specific suggestion is not correct for your case because your second resource has a different number of instances and different instance keys than the first one.

1 Like

hi , thx for your reply , and I’ve tried. it works. many thx.

hi apparentlymart,
as your suggestion , I use below and it works for dev , and I have more than one subnet , so I need to use instead of {} , which you mention.
data “aws_vpc_endpoint” “vpce_mct_edmz” {
count = var.environment == “dev” ? 1:0
tags = {
“Name” = “vpce_mct_edmz”
}
}

data “aws_network_interface” “vpce_mct_edmz_eni” {
for_each = (
length(data.aws_vpc_endpoint.vpce_mct_edmz) != 0 ?
data.aws_vpc_endpoint.vpce_mct_edmz[0].network_interface_ids :

)
id = each.value
}
but in my preprod env(var.environment == “preprod”) , I don’t need them , but when I run “terraform plan”, it raise below errors:


│ Error: Invalid for_each argument

│ on data.tf line 91, in data “aws_network_interface” “vpce_mct_edmz_eni”:
│ 91: for_each = (
│ 92: length(data.aws_vpc_endpoint.vpce_mct_edmz) != 0 ?
│ 93: data.aws_vpc_endpoint.vpce_mct_edmz[0].network_interface_ids :
│ 94:
│ 95: )
│ ├────────────────
│ │ data.aws_vpc_endpoint.vpce_mct_edmz is empty tuple

│ The given “for_each” argument value is unsuitable: the “for_each” argument must be a map, or set of strings, and you have provided a value of type tuple.

could you help give more advise ? thx.

fixed.
data “aws_network_interface” “vpce_mct_edmz_eni” {
for_each = (
length(data.aws_vpc_endpoint.vpce_mct_edmz) != 0 ?
toset(data.aws_vpc_endpoint.vpce_mct_edmz[0].network_interface_ids) :
toset()
)
id = each.value
}