Terraform 从具有公共值的映射中获取第一个唯一键

Terraform 从具有公共值的映射中获取第一个唯一键,terraform,Terraform,我有一个部署定义,其中我有一个CIDR块到区域的映射。CIDR当然需要是唯一的,但值可以重复。在本例中,我将CIDR块转到三个不同的区域,us-west-1,us-west-2,以及us-east-1 cidr_region_map = { "10.0.0.0/24" = "us-west-1", "10.0.1.0/24" = "us-east-1", "10.0.3.0/24"

我有一个部署定义,其中我有一个CIDR块到区域的映射。CIDR当然需要是唯一的,但值可以重复。在本例中,我将CIDR块转到三个不同的区域,
us-west-1
us-west-2
,以及
us-east-1

cidr_region_map = {
  "10.0.0.0/24" = "us-west-1", 
  "10.0.1.0/24" = "us-east-1", 
  "10.0.3.0/24" = "us-east-1",
  "10.0.4.0/24" = "us-west-1", 
  "10.0.5.0/24" = "us-west-2", 
  "10.0.6.0/24" = "us-east-1",
}
我需要得到一个数组,在这里我可以从这张地图中为每个区域挑选出第一个CIDR。这意味着我最终会得到这样一个列表:

[
“10.0.1.0/24”,//us-east-1
“10.0.0.0/24”//us-west-1
“10.0.5.0/24”//us-west-2
]

此列表中应忽略来自我们已经考虑的地区的任何其他CIDR。

任何人都可以快速复制/粘贴的解决方案

操场。tf

locals {
  cidr_region_map = {
    "10.0.0.0/24" = "us-west-1", 
    "10.0.1.0/24" = "us-east-1", 
    "10.0.3.0/24" = "us-east-1",
    "10.0.4.0/24" = "us-west-1", 
    "10.0.5.0/24" = "us-west-2", 
    "10.0.6.0/24" = "us-east-1",
  }

  group_by = values(
    zipmap(
      reverse(values(local.cidr_region_map)),
      reverse(keys(local.cidr_region_map))
    )
  )
}

output "test_out_2" {
  value = local.group_by
}
输出

$ terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

test_out_2 = [
  "10.0.1.0/24",
  "10.0.0.0/24",
  "10.0.5.0/24",
]

工作原理 为了简单起见,我将解释这个魔法如何与“变量”一起工作,以更好地可视化正在发生的事情

keys(map)   = CIDR: ["10.0.0.0/24", "10.0.1.0/24", "10.0.3.0/24", "10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
values(map) = VALS: ["us-west-1", "us-east-1", "us-east-1", "us-west-1", "us-west-2", "us-east-1"]
地图在地形中的工作方式是它们只能有一把钥匙。如果尝试将现有密钥添加到映射,它将替换旧密钥。例如,如果你这样做

{
“us-east-1”=“10.0.3.0/24”//将删除此选项
“美国东部-1”=“10.0.1.0/24”
}
首先,我们要反转原始的键/值列表,这样列表中的键/值越低,优先级就越低,因为第一个键/值将稍后添加到映射中,以便覆盖原始值

r_CIDR = reverse(CIDR) = ["10.0.6.0/24", "10.0.5.0/24", "10.0.4.0/24", "10.0.3.0/24", "10.0.1.0/24", "10.0.0.0/24"]
r_VALS = reverse(VALS) = ["us-east-1", "us-west-2", "us-west-1", "us-east-1", "us-east-1", "us-west-1"]
现在我们已经反转了这些值,我们使用
zipmap
来创建一个新的映射

zipmap(r_VALS,, r_CIDR) = { 
  "us-east-1" = "10.0.6.0/24" // this will be dropped
  "us-west-2" = "10.0.5.0/24"
  "us-west-1" = "10.0.4.0/24" // this will be dropped 
  "us-east-1" = "10.0.3.0/24" // this will be dropped
  "us-east-1" = "10.0.1.0/24"
  "us-west-1" = "10.0.0.0/24"
}
在内部删除所有重复的键之后,您将得到以下映射

zipmap(r_VALS,, r_CIDR) = { 
  "us-west-2" = "10.0.5.0/24"
  "us-east-1" = "10.0.1.0/24"
  "us-west-1" = "10.0.0.0/24"
}
最后,您获取该映射的值,剩下的是唯一的值

values(zipmap) = ["10.0.1.0/24", "10.0.0.0/24", "10.0.5.0/24"]

这个问题似乎更容易用反转贴图来解决,这样值就是键,键就是值。我们可以使用
for
表达式来实现这一点,使用
..
修饰符来激活,以允许每个区域可能存在多个CIDR范围:

locals {
  cidr_region_map = {
    "10.0.0.0/24" = "us-west-1", 
    "10.0.1.0/24" = "us-east-1", 
    "10.0.3.0/24" = "us-east-1",
    "10.0.4.0/24" = "us-west-1", 
    "10.0.5.0/24" = "us-west-2", 
    "10.0.6.0/24" = "us-east-1",
  }

  region_cidr_map = {
    for cidr, region in local.cidr_region_map : region => cidr...
  }
}
这将生成字符串列表的映射,如下所示:

{
  us-west-1 = {
    "10.0.0.0/24",
    "10.0.4.0/24",
  }
  us-east-1 = {
    "10.0.1.0/24",
    "10.0.3.0/24",
    "10.0.6.0/24",
  }
  us-west-2 = {
    "10.0.6.0/24",
  }
}
因为for表达式只有在键至少有一个值的情况下才会包含一个键,所以我们可以假设所有列表都至少有一个元素,因此对于第一个元素,我们可以使用
[0]
正常访问它:

这将产生以下内容,我认为符合您的要求:

{
  us-west-1 = "10.0.0.0/24"
  us-east-1 = "10.0.1.0/24"
  us-west-2 = "10.0.6.0/24"
}
(如果您只需要CIDR块本身,而不关心每个块属于哪个区域,则可以在此地图上使用
。)

请注意,它从每个区域中选择哪个CIDR块将由Terraform在表达式的
中对地图的遍历顺序决定,该顺序是按键的词法顺序。由于原始地图上有CIDR块作为键,因此决定顺序的是CIDR块的词法顺序,而这恰好与直观的数字顺序相匹配,其中每个地址都有相同的位数,但如果使用例如
10.0.10.0/24
10.0.2.0/24
,则情况并非如此,因为
10.0.10.0/24
在词汇上是“在”
10.0.2.0/24
之前”,即使在数字上不是这样

{
  us-west-1 = "10.0.0.0/24"
  us-east-1 = "10.0.1.0/24"
  us-west-2 = "10.0.6.0/24"
}