Terraform for_each循环中的引用计数资源

Terraform for_each循环中的引用计数资源,terraform,terraform-provider-aws,Terraform,Terraform Provider Aws,我目前正在尝试利用for_each函数创建aws_路由资源,并将这些路由与使用count函数构建的许多aws_路由表资源相关联。我想知道是否有一种方法可以让我仍然使用aws_route资源中的for_each函数并引用aws_route_表资源 我的最终目标是创建~5条路由并将其应用于~7条路由表 以下是我的代码供参考: resource "aws_route_table" "private" { vpc_id = aws_vpc.vpc.id c

我目前正在尝试利用for_each函数创建aws_路由资源,并将这些路由与使用count函数构建的许多aws_路由表资源相关联。我想知道是否有一种方法可以让我仍然使用aws_route资源中的for_each函数并引用aws_route_表资源

我的最终目标是创建~5条路由并将其应用于~7条路由表

以下是我的代码供参考:

resource "aws_route_table" "private" {
  vpc_id = aws_vpc.vpc.id
  count  = length(split(",", var.private_subnets))

  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = join(",", aws_nat_gateway.nat.*.id)
  }

  route {
    cidr_block = var.stage_vpc_cidr
    vpc_peering_connection_id = var.stage_peering_connection_id
  }

  route {
    cidr_block = var.production_vpc_cidr
    vpc_peering_connection_id = var.production_peering_connection_id
  }

  tags = {
    Name        = "${var.name}-private.${element(split(",", var.azs), count.index)}"
    Department  = var.tag_department
    Lifecycle   = var.tag_lifecycle
    Account     = var.tag_account
    Application = var.tag_application
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_route" "vpn-route" {
  for_each = toset(var.vpn_subnets)
  route_table_id = element(aws_route_table.private.*.id, count.index)
  gateway_id = var.vpn_gateway_id
  destination_cidr_block = each.value
}
和我的变量

variable "vpn_subnets" {
  type = list
  default = ["192.168.0.0/24","192.168.2.0/24","192.168.4.0/24","192.168.5.0/24","192.168.10.0/24","192.168.253.0/24"]
}

听起来您的要求是为路由表实例和
var.vpn\u子网
元素的每个组合都有一个
aws\u路由
实例

查找两个集合元素的所有组合的操作称为笛卡尔积,Terraform必须计算该积

locals {
  route_table_vpn_subnets = [
    for pair in setproduct(aws_route_table.private, var.vpn_subnets) : {
      route_table_name       = pair[0].tags.Name
      route_table_id         = pair[0].id
      destination_cidr_block = pair[1]
    }
  ]
}
这是使用
setproduct
结合a生成一系列对象,这些对象描述了路由表ID和目标CIDR块的所有组合,如下所示:

[
  {
    route_table_name       = "example-private.0"
    route_table_id         = "rtb-12323453"
    destination_cidr_block = "192.168.0.0/24"
  },
  {
    route_table_name       = "example-private.0"
    route_table_id         = "rtb-12323453"
    destination_cidr_block = "192.168.2.0/24"
  },
  # ...
  {
    route_table_name       = "example-private.1"
    route_table_id         = "rtb-abcd"
    destination_cidr_block = "192.168.0.0/24"
  },
  {
    route_table_name       = "example-private.1"
    route_table_id         = "rtb-abcd"
    destination_cidr_block = "192.168.2.0/24"
  },
  # ...
]
此数据结构现在满足每个的两个关键要求:

  • 我们要创建的每个实例都有一个集合元素

  • 每个对象都有一组属性,这些属性可以一起用作根据配置中确定的值(而不是远程系统确定的值)构建的唯一标识符

    在这种情况下,通过同时包含
    route\u table\u name
    route\u table\u id
    ,可以实现这一点,因为名称由您的配置选择,而id由远程系统选择,因此id不适合用作实例标识符

要在
中为每个
使用它,我们只需再执行一个步骤,将其投影到一个映射中,在该映射中,这些唯一属性值组合成唯一的字符串键:

resource "aws_route" "vpn" {
  for_each = {
    for obj in local.route_table_vpn_subnets :
    "${obj.route_table_name}:${obj.destination_cidr_block}" => obj
  }

  route_table_id         = each.value.route_table_id
  gateway_id             = var.vpn_gateway_id
  destination_cidr_block = each.value.destination_cidr_block
}
表达式中的关键表达式告诉Terraform分配这些实例地址,如下所示,这应该满足在所有实例中都是唯一的要求:

  • aws_route.vpn[“示例private.0:192.168.0.0/24”]

  • aws_route.vpn[“示例private.0:192.168.0.2/24”]

  • aws_route.vpn[“示例private.1:192.168.0.0/24”]

  • aws_route.vpn[“示例private.1:192.168.0.2/24”]


请注意,由于正在为路由表使用数字索引,如果更改
var.private_子网的拆分中的元素数,则可能会导致路由表重新编号。因为编号是名称的一部分,而名称是每个路由实例的唯一键的一部分,这也会导致路由实例被重新编号,我之所以提到这一点,是因为对每个
使用
的一个典型原因是为了避免这种重新编号


您可能会遵循另一种设计,即使用满足上述两个标准的对象集合来描述路由表,以便也可以使用有意义的唯一键来声明路由表实例,但这将是一个非常重要的变化,超出了您所要求的范围,因此我将不在这里详细介绍。

哇,谢谢您的回答。你不仅提供了答案,而且还帮助我理解了背后的逻辑!顺便说一下,应该将destination_cidr_块设置为等于each.value.destination_cidr_块而不是each.value.route_table_id。再次感谢!