首页 > 解决方案 > Terraform For 表达式使用一个元组和两个字符串列表创建单个映射以在 for_each 中使用

问题描述

如何使用一个元组和两个字符串列表创建要在 for_each 中使用的单个对象映射

假设我有一个来自 ec2 模块的实例 ID 或实例 IP 的元组/列表和一个来自 ALB 模块的目标组 arn 的元组/列表,如下所示。

instance_ids      = ["10.1.1.1", "10.2.2.2"]
target_gropup_arn = ["arn1", "arn2"]

在 main.tf

output "out" {
  value = local.aws_lb_target_group_attachment
}
variable "target_groups" {
  type = any
  default = [
    {
      protocol = "TCP"
      port     = "22"
    },
    {
    protocol = "TCP"
    port     = "443"
    }
  ]
}
locals {
  instance_ips      = toset(["10.1.1.1", "10.2.2.2"])
  target_gropup_arn = toset(["arn1", "arn2"])

  aws_lb_target_group_attachment = { for idx in flatten([for instance_ids in local.instance_ids :
  [for tg in var.target_groups :
  [for arn in local.target_gropup_arn :
  {
    target_id = instance_ids
    target_group_arn = arn
    port = tg.port
  }]
  ]]) : "${idx.target_id}:${idx.port}:${idx.target_group_arn}" => idx }
}

当我运行 terraform apply 时,我得到以下输出,但这不是我需要的预期地图。

Outputs:

out = {
  "10.1.1.1:22:arn1" = {
    "port" = "22"
    "target_group_arn" = "arn1"
    "target_id" = "10.1.1.1"
  }
  "10.1.1.1:22:arn2" = {
    "port" = "22"
    "target_group_arn" = "arn2"
    "target_id" = "10.1.1.1"
  }
  "10.1.1.1:443:arn1" = {
    "port" = "443"
    "target_group_arn" = "arn1"
    "target_id" = "10.1.1.1"
  }
  "10.1.1.1:443:arn2" = {
    "port" = "443"
    "target_group_arn" = "arn2"
    "target_id" = "10.1.1.1"
  }
  "10.2.2.2:22:arn1" = {
    "port" = "22"
    "target_group_arn" = "arn1"
    "target_id" = "10.2.2.2"
  }
  "10.2.2.2:22:arn2" = {
    "port" = "22"
    "target_group_arn" = "arn2"
    "target_id" = "10.2.2.2"
  }
  "10.2.2.2:443:arn1" = {
    "port" = "443"
    "target_group_arn" = "arn1"
    "target_id" = "10.2.2.2"
  }
  "10.2.2.2:443:arn2" = {
    "port" = "443"
    "target_group_arn" = "arn2"
    "target_id" = "10.2.2.2"
  }
}

预期的输出是(伪输出)

out = {
  "10.1.1.1:22" = {
    "port" = "22"
    "target_id" = "10.1.1.1"
    "target_group_arn" = "arn1"
  }
  "10.1.1.1:443" = {
    "port" = "443"
    "target_id" = "10.1.1.1"
    "target_group_arn" = "arn1"
  }
  "10.2.2.2:22" = {
    "port" = "22"
    "target_id" = "10.2.2.2"
    "target_group_arn" = "arn2"
  }
  "10.2.2.2:443" = {
    "port" = "443"
    "target_id" = "10.2.2.2"
    "target_group_arn" = "arn2"
  }
}

这是必需的,以便稍后我可以使用它来使用“aws_lb_target_group_attachment”资源附加多个实例。

标签: terraform

解决方案


您的技术无法正常工作,因为您正在创建 ips 和 arns 的笛卡尔积,但根据您的示例输出,您只希望第一个 ip 与第一个 arn 分组,而第二个 ip 是与第二个 arn 分组。

这是一个有效的解决方案:

output "out" {
  value = local.aws_lb_target_group_attachment
}

variable "target_groups" {
  type = any
  default = [
    {
      protocol = "TCP"
      port     = "22"
    },
    {
      protocol = "TCP"
      port     = "443"
    }
  ]
}

locals {
  instance_ids = ["10.1.1.1", "10.2.2.2"]
  target_arns  = ["arn1", "arn2"]

  arn_id_map = [for i, id in local.instance_ids : { "target_id" : id, "target_group_arn" : local.target_arns[i] }]
  aws_lb_target_group_attachment = merge([for tg in var.target_groups :
    { for pair in local.arn_id_map :
      "${pair.target_id}:${tg.port}" => merge(pair, { "port" : tg.port })
    }
  ]...)
}

分解:

  arn_id_map = [for i, id in local.instance_ids : { "target_id" : id, "target_group_arn" : local.target_arns[i] }]

在这里,我们遍历 ips 列表并构建一个新映射,其中包含您在最终输出中想要的三个键中的两个。我们利用了 ips 和 arns 之间存在 1-1 映射这一事实。即第一个ip对应第一个arn,第二个ip对应第二个arn,以此类推。

  aws_lb_target_group_attachment = merge([for tg in var.target_groups :
    { for pair in local.arn_id_map :
      "${pair.target_id}:${tg.port}" => merge(pair, { "port" : tg.port })
    }
  ]...)

从第一个循环开始,我们迭代目标组创建一个列表推导,这意味着这一层的值将是一个列表。然后在里面我们做一个映射理解,迭代我们在上一步中创建的 ip+arn 对。

在内部,我们创建了一个映射,其结构是您想要的最终输出。内部合并是采用我们的 ip+arn 对并使用端口创建新映射的一种干净方式。

但是随着层的展开,我们最终会得到一个包含两个映射的列表,每个映射包含 2 个子映射(每个 ip+port 组合一个)。我们需要“扁平化”结构,但flatten()不适用于地图列表。

要“展平”地图,我们通常会用到merge(),但在这种情况下它不能直接工作,因为merge()期望每个参数都是一个地图。但是我们有一个地图列表。所以我们使用扩展符号 ...将列表传递给 merge 并将其扩展为 merge 可以直接接受的参数。

结果如您所愿:

out = {
  "10.1.1.1:22" = {
    "port" = "22"
    "target_group_arn" = "arn1"
    "target_id" = "10.1.1.1"
  }
  "10.1.1.1:443" = {
    "port" = "443"
    "target_group_arn" = "arn1"
    "target_id" = "10.1.1.1"
  }
  "10.2.2.2:22" = {
    "port" = "22"
    "target_group_arn" = "arn2"
    "target_id" = "10.2.2.2"
  }
  "10.2.2.2:443" = {
    "port" = "443"
    "target_group_arn" = "arn2"
    "target_id" = "10.2.2.2"
  }
}

推荐阅读