-
- Notifications
You must be signed in to change notification settings - Fork 4.6k
Closed
Description
Description
Usage of output database_route_table_ids in another module causes error "Invalid for_each argument" during first plan/apply.
Cause seems to be that the output delivers a tuple during first plan/apply, because the route_table is still not existing, but a list after the first apply.
This seems to be related to hashicorp/terraform#31102, but the usage of tolist() function, did not help in the exampe below.
- ✋ I have searched the open/closed issues and my issue is not listed.
Versions
- Module version [Required]: 3.16.1
- Terraform version: Terraform v0.15.5
- Provider version(s): v4.38.0
Reproduction Code [Required]
provider "aws" { region = "eu-central-1" } locals { name = "ex-${replace(basename(path.cwd), "_", "-")}" region = "eu-central-1" # Normaly this is a variable containg a more complex map with multiple cidrs # routed to different tgws routes_to_transit_gateway = { "example_with_simple_routing" = { "tgw_id" = "tgw-example-id" # Normaly a real existing tgw id "destination_cidrs" = ["0.0.0.0/0"] "name" = "example" "subnet_name" = "app" } } ########################################################### # Generate a list containing all vpc route_table_ids with # associated subnet names route_table_ids_list = flatten(concat([ for route_table_id in module.vpc.public_route_table_ids : { "route_table_id" = route_table_id, "subnet_name" = "web" } ], [ for route_table_id in module.vpc.private_route_table_ids : { "route_table_id" = route_table_id, "subnet_name" = "app" } ], [ for route_table_id in module.vpc.database_route_table_ids : { "route_table_id" = route_table_id, "subnet_name" = "data" } ], [ ])) ##################################################### # Generate a list of maps of all required routes to # transit gateways in this vpc routes_to_transit_gateway_per_table = flatten([ for tgw_entry in local.routes_to_transit_gateway : [ for destination_cidr in tgw_entry.destination_cidrs : [ for rtb_value in local.route_table_ids_list : [ { "route_entry_name" = "${tgw_entry.name}_${rtb_value.subnet_name}_${destination_cidr}" # Add route table id and destination CIDR to each tgw attachment entry "route_table_id" = rtb_value.route_table_id, "destination_cidr" = destination_cidr "tgw_id" = tgw_entry.tgw_id "routes_to_transit_gateway_subnet_name" = tgw_entry.subnet_name # for debugging "route_table_ids_map_subnet_name" = rtb_value.subnet_name # for debugging } ] if contains([rtb_value.subnet_name], tgw_entry.subnet_name) || ( contains(["default"], tgw_entry.subnet_name) && rtb_value.subnet_name != "dmz" ) ] ] ]) } ################################################################################ # VPC Module ################################################################################ module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "3.16.1" name = local.name cidr = "10.0.0.0/16" azs = ["${local.region}a", "${local.region}b"] private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] database_subnets = ["10.0.3.0/24", "10.0.4.0/24"] enable_ipv6 = false enable_nat_gateway = false single_nat_gateway = true public_subnet_tags = { Name = "overridden-name-public" } vpc_tags = { Name = "vpc-name" } } ##################################################### # Generate all route to all tgws from one map resource "aws_route" "dynamic" { for_each = { for route in local.routes_to_transit_gateway_per_table : route.route_entry_name => route } route_table_id = each.value.route_table_id destination_cidr_block = each.value.destination_cidr transit_gateway_id = each.value.tgw_id } output "debug_routes_to_transit_gateway_per_table" { value = local.routes_to_transit_gateway_per_table } Steps to reproduce the behavior:
terraform init & terraform plan
Expected behavior
terraform plan shows, that the following resource will be created
# aws_route.dynamic["example_app_0.0.0.0/0"] will be created + resource "aws_route" "dynamic" { + destination_cidr_block = "0.0.0.0/0" + id = (known after apply) + instance_id = (known after apply) + instance_owner_id = (known after apply) + network_interface_id = (known after apply) + origin = (known after apply) + route_table_id = (known after apply) + state = (known after apply) + transit_gateway_id = "tgw-example-id" } Actual behavior
terraform plan shows the following error message:
$ terraform plan ╷ │ Error: Invalid for_each argument │ │ on main.tf line 110, in resource "aws_route" "dynamic": │ 110: for_each = { │ 111: for route in local.routes_to_transit_gateway_per_table : route.route_entry_name => route │ 112: } │ ├──────────────── │ │ local.routes_to_transit_gateway_per_table is tuple with 1 element │ │ 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. Solution
Can be solved by the following change:
diff --git a/outputs.tf b/outputs.tf index 9d93dda..c5c0200 100644 --- a/outputs.tf +++ b/outputs.tf @@ -245,7 +245,7 @@ output "private_route_table_ids" { output "database_route_table_ids" { description = "List of IDs of database route tables" - value = try(coalescelist(aws_route_table.database[*].id, aws_route_table.private[*].id), []) + value = length(aws_route_table.database[*].id) > 0 ? aws_route_table.database[*].id : aws_route_table.private[*].id } output "redshift_route_table_ids" { SungHyeon
Metadata
Metadata
Assignees
Labels
No labels