从 terraform 中的嵌套地图按程序生成单项地图

Produce single item maps procedurally from nested map in terraform

我有一个按 OU 划分的帐户名称和 ID 的嵌套映射变量,例如:

variable "aws_accounts" {
  type = map(map(any))
  default = {
    first_ou = {
      first_account  = "111111111"
      second_account = "222222222"
    }
    second_ou = {
      third_account  = "333333333"
      fourth_account = "444444444"
    }
  }
}

这非常适合将 account_name 的映射作为子变量传递给 account_id 以供您执行操作,并且所讨论的模块被构造为接受映射输入。

我还想渲染一个本地,这样我也可以引用单个帐户,但可以为它们获取映射值,而无需维护单独的变量列表,例如

local.first_account = {
  first_account  = "111111111"
}

local.second_account = {
  second_account = "222222222"
}

local.third_account = {
  third_account = "33333333"
}

等等

我尝试了各种技术但没有成功:

我不知道如何迭代地引用数组中的每个映射 - 大多数文档似乎都是基于列表的,当我尝试执行 for_each 时,我得到

The "each" object can be used only in "module" or "resource" blocks, and only when the "for_each" argument is set.

可能不是您要找的东西,但可能会有所帮助:

locals {
  accounts = merge(var.aws_accounts["first_ou"], var.aws_accounts["second_ou"])
}

如果您需要以更动态的方式执行此操作:

locals {
  accounts = zipmap(
    flatten([for item in var.aws_accounts : keys(item)]), 
    flatten([for item in var.aws_accounts : values(item)])
  )
}

现在您可以使用 local.accounts["first_account"] 等访问每个帐户

根据你的例子,你似乎想把你的两级映射变成一个单级映射,其中键是账户名,“OU 名称”只是被丢弃。

这是实现该目标的一种方法:

locals {
  account_ids = merge(values(var.aws_accounts)...)
}

这首先使用 values 从顶级地图中获取值,生成地图列表。

然后它使用 merge 从每个地图中获取所有元素并将它们组合成一个新地图。我使用 ... 符号告诉 Terraform 它应该将列表中的每个元素视为 merge 的单独参数,而不是将整个列表作为单个参数传递。

将它们合并在一起后,您可能会再次将它们分开,使用 for 表达式分别创建一个单独的地图。

locals {
  account_maps = tomap({
    for k, id in local.account_ids :
    k => { (k) = id }
  })
}

好的 所以在 Martin Atkins and Bryan Heden 的帮助下我找到了这个问题的答案。它不是很漂亮,但确实有效:

variable "aws_accounts" {
  type = map(map(any))
  default = {
    first_ou = {
      first_account  = "111111111"
      second_account = "222222222"
    }
    second_ou = {
      third_account  = "333333333"
      fourth_account = "444444444"
    }
  }
}

locals {
  # gives single map from nested map
  account_ids = merge(values(var.aws_accounts)...)
  #   gives separate structured map for each key
  single_accounts_maps = {
    for account, id in local.account_ids :
    account => {
      account = account
      id      = id
    }
  }
  #   gives map where values = keys plus values
  single_accounts_maps_joined = zipmap(
    flatten([for item in var.aws_accounts : keys(item)]),
    [for item in local.single_accounts_maps :
    join(" = ", values(item))]
  )

  # gives nested map by key = {key = "value"}
  single_accounts_maps_keys_values = {
    for item in local.single_accounts_maps_joined :
    (split(" = ", item)[0]) => {
      (split(" = ", item)[0]) = (split(" = ", item)[1])
    }
  }
}

我想要的输出:

terraform console
> local.single_accounts_maps_keys_values
{
  "first_account" = {
    "first_account" = "111111111"
  }
  "fourth_account" = {
    "fourth_account" = "444444444"
  }
  "second_account" = {
    "second_account" = "222222222"
  }
  "third_account" = {
    "third_account" = "333333333"
  }
}

在与 Martin Atkins 的讨论和他随后在下面进行的编辑之后,我建议他的答案更简单、更清晰、更优雅,尽管 tomap() 嵌套似乎是不必要的,即做

locals {
  account_ids = merge(values(var.aws_accounts)...)
  account_maps = {
    for k, id in local.account_ids :
    k => { (k) = id }
  }
}