For_each on aws_subnet_ids

Hello… terraform newbie here =)

I’m trying to follow this doc using for_each. Heres my twist:

data "aws_subnet_ids" "public" {
    vpc_id = aws_vpc.js_vpc.id

    tags = {
        Scope = "Public"
    }
}

resource "aws_route_table_association" "public" {
    depends_on = [aws_subnet.public_subnets]
    for_each = data.aws_subnet_ids.public.ids
    subnet_id = each.value
    route_table_id = aws_route_table.public.id
}

Understandably for_each doesnt know whats going on and screamed at me with…

$ terraform plan
...
------------------------------------------------------------------------
Error: Invalid for_each argument

  on ../../modules/vpc/route-tables.tf line 88, in resource "aws_route_table_association" "public":
  88:     for_each = data.aws_subnet_ids.public.ids

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.

So I proceed to append -target into the cmd

$ terraform plan -target=aws_subnet.public_subnets

Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

No changes. Infrastructure is up-to-date.

This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.

Warning: Resource targeting is in effect

You are creating a plan with the -target option, which means that the result
of this plan may not represent all of the changes requested by the current
configuration.
		
The -target option is not for routine use, and is provided only for
exceptional situations such as recovering from errors or mistakes, or when
Terraform specifically suggests to use it as part of an error message.

This seems to me that terraform didnt do anything… Whats the recommended cmd to go about this?

Hi @MrAtheist,

In your case it seems like aws_vpc.js_vpc.id is the source of the problem: Terraform can’t request the subnet ids for that VPC until the VPC id is known. Therefore one way to get this to work would be to -target the VPC itself, like this:

terraform plan -target=aws_vpc.js_vpc

With that said, this configuration is confusing me a little, because a VPC that you’ve only just created would not have any subnets in it, and so this data source would presumably return an empty set. I see in your second block a reference to aws_subnet.public_subnets, which suggests that this configuration is also managing those subnets, in which case we should not need to retrieve them with a data resource.

You didn’t show the source code for aws_subnet.public_subnets but assuming that it’s also using for_each in a similar way, here’s a way to write that without the data resource at all:

resource "aws_route_table_association" "public" {
    for_each = aws_subnet.public_subnets

    subnet_id      = each.value.id
    route_table_id = aws_route_table.public.id
}

This is a more straightforward way to write down the intent “create one route table association per subnet”. If aws_subnet.public_subnets is itself using for_each then its value will be a map that is automatically compatible with for_each, causing the route table associations to be identified by the same keys as the subnets and each.value in the block representing the corresponding aws_subnet object, so we can get its id using each.value.id.

As a general rule, a particular configuration should either be managing an object or reading an object, not both at the same time. If you try to read something that the configuration is also responsible for creating then you need to introduce additional complexity to make sure the create happens before the read: the example in your original question is likely to fail because the data.aws_subnet_ids.public resource will be read before all of the instances of aws_subnet.public_subnets have been created.

1 Like