在 Terraform v1.0 中连接列表和资源输出

Concatenate a list and resource output in Terraform v1.0

我有以下本地列表:

locals {
  default_iam_policies = [
    "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy",
    "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore",
    "arn:aws:iam::aws:policy/AWSDeviceFarmFullAccess"   
  ]
}

我计划将这些策略与自定义策略一起附加到角色:

resource "aws_iam_role_policy_attachment" "default-policy-attachment" {
  for_each = toset(concat(
    local.default_iam_policies,
    [aws_iam_policy.custom-policy.arn]
  ))

  role       = aws_iam_role.this.name
  policy_arn = each.value
}

但我收到此错误消息:

│ Error: Invalid for_each argument
│
│   on main.tf line 113, in resource "aws_iam_role_policy_attachment" "default-policy-attachment":
│  113:   for_each = toset(concat(
│  114:     local.default_iam_policies,
│  115:     [aws_iam_policy.custom-policy.arn]
│  116:     ))
│     ├────────────────
│     │ aws_iam_policy.custom-policy.arn is a string, known only after apply
│     │ local.default_iam_policies is tuple with 3 elements
│
│ The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created.
│ To work around this, use the -target argument to first apply only the resources that the for_each depends on.

我想我可以把它分成两个 aws_iam_role_policy_attachment 块,但我想看看是否可以只使用一个。

您已达到 Terraform 的限制:

https://www.terraform.io/docs/language/meta-arguments/for_each.html#limitations-on-values-used-in-for_each

Limitations on values used in for_each

The keys of the map (or all the values in the case of a set of strings) must be known values, or you will get an error message that for_each has dependencies that cannot be determined before apply, and a -target may be needed.

表达式具有相同的限制:

The for_each meta-argument accepts map or set expressions. However, unlike most arguments, the for_each value must be known before Terraform performs any remote resource actions. This means for_each can't refer to any resource attributes that aren't known until after a configuration is applied (such as a unique ID generated by the remote API when an object is created).

如前所述,您不能使用 for_each。但在你的情况下 你可以使用 count:


locals {
  default_iam_policies = [
    "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy",
    "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore",
    "arn:aws:iam::aws:policy/AWSDeviceFarmFullAccess"   
  ]
  
  full_list = concat(local.default_iam_policies,[aws_iam_policy.this.arn])
}


resource "aws_iam_role_policy_attachment" "default-policy-attachment" {
  count      = length(local.full_list)

  role       = aws_iam_role.this.name
  policy_arn = local.full_list[count.index]
}

在这种情况下使用 for_each 需要区分您在 Terraform 配置中给这些对象的标签和远程系统分配给它们的 labels/ids,因为远程系统的标识符会随着时间的推移而改变,并且通常在应用之后才知道,但是 Terraform 需要这些密钥在这些实例的整个生命周期中保持一致,包括在最初创建它们的计划阶段。

具体说明这种情况,这意味着这些策略 ARN 中的每一个都应该有一个直接在 Terraform 配置中决定的逻辑名称,它与 AWS API 决定的 ARN 分开你.

local.default_iam_policy 是在配置中静态定义的 ,因此从技术上讲,这些区别并不需要,但由于我们将把它与动态生成的 ARN 对两者应用相同的约定会更容易,因此我首先将本地值调整为一个映射,其中键将是这些策略的本地标识符:

locals {
  default_iam_policies = tomap({
    CloudWatchAgentServerPolicy  = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy",
    AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore",
    AWSDeviceFarmFullAccess      = "arn:aws:iam::aws:policy/AWSDeviceFarmFullAccess"
  })
}

由于您的原始示例表明在本地 ID 和远程 ID 之间的拆分中实际上没有任何特殊的细微差别需要捕捉,因此我在这里保持简单并建立了一个约定,即本地名称是 name 的政策,这也是我们可以从“自定义”政策中系统地推导出来的东西:

resource "aws_iam_role_policy_attachment" "default-policy-attachment" {
  for_each = merge(
    local.default_iam_policies,
    { for p in aws_iam_policy.custom-policy.arn : p.name => p.arn },
  )

  role       = aws_iam_role.this.name
  policy_arn = each.value
}

以上假设您的 resource "aws_iam_policy" "custom-policy" 块为每个策略分配静态 name 值,以便 Terraform 在计划时间。这通常是正确的,但如果您从其他一些动态来源派生出您的“自定义策略”集,那么原则上 name 值也可能“在应用后已知”,在这种情况下我们会需要不同的策略来为每个实例分配静态密钥。

所有这一切的结果是您的 resource "aws_iam_role_policy_attachment" "default-policy-attachment" 块将声明具有以策略名称命名的键的实例:

  • aws_iam_role_policy_attachment.default-policy-attachment["CloudWatchAgentServerPolicy"]
  • aws_iam_role_policy_attachment.default-policy-attachment["AmazonSSMManagedInstanceCore"]
  • aws_iam_role_policy_attachment.default-policy-attachment["AWSDeviceFarmFullAccess"]
  • 以此类推,无论您的自定义策略有什么名称