Query for_each or count subnets

So I have a query here , I have been researching the newer for_each and the slightly older method count.

For example

    resource "aws_subnet" "public" {
      count = var.create_igw ? local.availability_zones_count : 0
      availability_zone       = data.aws_availability_zones.available.names[count.index]
      cidr_block              = var.pub_cidrspace == "large" ? cidrsubnet(var.vpc_cidr_pub, 8, count.index) : cidrsubnet(var.vpc_cidr_pub, 6, count.index)
      map_public_ip_on_launch = true
      vpc_id                  = aws_vpc.main.id
    }

Works as expected using the count iterator, I also tried the below method

resource "aws_subnet" "private_subnet" {
  for_each          = var.size == "large" ? var.subnet_map : var.subnet_map_s
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.cidrspace == "large" ? cidrsubnet(var.vpc_cidr, 11, each.key) : cidrsubnet(var.vpc_cidr, 8, each.key)
  availability_zone = length(regexall("^[a-z]{2}-[a-z]+-[0-9][a-z]", element(var.availability_zone_list, each.key))) > 0 ? element(var.availability_zone_list, each.key) : null
  
  tags = {
    Name = "private_subnet_{each.key}_${element(var.availability_zone_list, each.key)}"
  }
}

I am trying to use conditional logic, for both subnet counts and cidr blocks. But really trying to understand if for_each is the preferred method. I grasp the idea behind for_each in that if you remove an entry it won’t behave like count which is indexed linked. I think an idea I use count for is for example ,

resource "aws_vpc_endpoint" "s3" {
  count        = var.s3_endpoints ? 1 : 0
  vpc_id       = aws_vpc.main.id
  service_name = "com.amazonaws.eu-west-2.s3"
}

resource "aws_vpc_endpoint_route_table_association" "s3_endpoints" {
  count           = var.s3_endpoints ? 1 : 0
  route_table_id  = aws_default_route_table.private_route.id
  vpc_endpoint_id = aws_vpc_endpoint.s3[0].id
}

Any feedback on the best approach would be great here

Hi @fozzy77,

I’m not sure I fully understand what your goal is here, but it might interest you to know that a different way to create a conditional for_each is to write a for expression with an if clause to filter out any elements you don’t want to create instances for.

The simplest form of this is to get the same conditional result for every element in the map, which means that it’ll specify either all of the elements or none of the elements dependingo n the condition result:

  for_each = {
    for k, v in var.example : k => v
    if var.create_thing
  }

If you set var.create_thing to false then this’ll filter out all of the elements of var.example, causing Terraform to see zero instances of this resource. If you set it to true then all of the elements will pass through as-is and so Terraform will see one instance for each element.

However, since a for expression creates some local symbols (k and v in the above example) to refer to the current element, you can also use this to filter out only some of the elements:

  for_each = {
    for k, v in var.example : k => v
    if v.is_large
  }

The above assumes that var.example is a map of objects that all have an is_large boolean attribute, which v.is_large therefore refers to.

All of these options are appropriate in different situations. There isn’t a single answer to which one is “best” for all situations. However, if you have some specific situations in mind that you are interested in then I might be able to share some tradeoffs that I would make to decide which to use in those cases particularly.

Hi , and thanks for this. I am slowly but surely absorbing all these things. I literally didn’t even know how to login to terraform or aws 14 months back :-). I fully appreciate your pointers