首页 > 解决方案 > Terraform:使用匹配键创建地图失败并出现“重复对象键”

问题描述

我正在尝试在此处为 GCP VPC 模块创建二级范围地图,并在我的本地人中定义以下内容:

  secondary_ranges = {
  for name, config in var.subnet_config : config.subnet_name => [
      {
        range_name    = local.ip_range_pods
        ip_cidr_range = "10.${index(keys(var.subnet_config), name)}.0.0/17"
      },
      {
        range_name    = local.ip_range_services
        ip_cidr_range = "10.${index(keys(var.subnet_config), name)}.128.0/17"
      }
    ]
  }

subnet_config 定义如下:

subnet_config   = {
    cluster1 = {
        region           = "us-east1"
        subnet_name      = "default"
    },
    cluster2 = {
        region           = "us-west1"
        subnet_name      = "default"
    }
}

如果子网名称是唯一的,这将创建辅助子网,但如果子网名称(最终成为键值)不唯一,则会失败并出现以下错误:

Two different items produced the key "default" in this 'for' expression. If duplicates are expected, use the ellipsis (...) after the value expression to enable grouping by key.

我试图弄清楚如果值是一个列表,我是否可以使用分组模式,如果可以,如何?

任何帮助将不胜感激。

标签: terraform

解决方案


如果您在这种情况下使用分组模式,那么它将对最外层的for表达式进行分组,该表达式正在生成一个映射,因为这是您要分组的键的那个。

我们可以从添加分组模式修饰符开始,看看会发生什么:

 secondary_ranges_pairs = {
   for name, config in var.subnet_config : config.subnet_name => [
     {
       range_name    = local.ip_range_pods
       ip_cidr_range = "10.${index(keys(var.subnet_config), name)}.0.0/17"
     },
     {
       range_name    = local.ip_range_services
       ip_cidr_range = "10.${index(keys(var.subnet_config), name)}.128.0/17"
     }
   ]...
 }

上面表达式的效果是创建对象列表列表的映射,其中最深的列表是每对对象,因为您的内部for表达式是如何编写的。

要将其转换为我认为您希望的对象列表的映射,您可以flatten在单独的步骤中使用:

  secondary_ranges = {
    for k, pairs in local.secondary_ranges_pairs : k => flatten(pairs)
  }

flatten递归遍历存在列表列表的数据结构,并将所有嵌套列表连接到一个平面列表中。


提醒一句:您似乎正在使用词法排序的subnet_config键来派生网络编号。这意味着,如果您将新元素添加到var.subnet_config其键排序比任何现有元素更早的元素中(例如,如果您要将 a 添加cluster0到您在问题中显示的内容中),那么您将隐含地重新编号所有后续网络,这可能会导致大量流失重新创建对象,如果这些网络包含其他对象,甚至可能无法进行更改。

我通常建议您明确说明您分配给每个网络的编号,方法是将 then 作为var.subnet_config对象的一部分。然后,您可以清楚地看到您分配了哪些号码,并确保始终为任何新网络分配一个以后的号码,而不会干扰任何现有的分配。

还有一个官方的 Terraform 模块hashicorp/subnets/cidr,旨在封装子网编号计算。该模块的设计意味着在您的用例中采用它并不完全简单(因为您一次分配两个级别的子网)但研究看看是否有任何设计权衡可能很有用与您的模块相关。


推荐阅读