我有以下本地列表:
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
块,但我想看看是否有可能只用一个。
如前所述,您不能使用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]
}
您已经达到了Terraform的限制:
https://www.terraform.io/docs/language/meta-arguments/for_each.html limitations-on-values-used-in-for_each
for_each中使用值的限制
map的键(或字符串集合中的所有值)必须是已知的值,否则您将得到for_each有依赖关系的错误消息,在应用之前无法确定,并且可能需要-target。
表达式有相同的限制:
for_each元参数接受map或set表达式。然而,与大多数参数不同的是,for_each值必须在Terraform执行任何远程资源操作之前已知。这意味着for_each不能引用任何在应用配置之后才知道的资源属性(例如创建对象时远程API生成的唯一ID)。
在这种情况下使用for_each
需要区分您在Terraform配置中给这些对象的标签和远程系统分配给它们的标签/id,因为远程系统的标识符可以随着时间的推移而改变,并且通常在应用之后才知道,但是Terraform需要这些键在这些实例的整个生命周期中保持一致,包括在最初创建它们的计划阶段。
对于这种情况,这意味着每个策略ARN都应该有一个直接在Terraform配置中决定的逻辑名称,这与AWS API为您决定的ARN是分开的。
local.default_iam_policy
值在配置中是静态定义的,所以从技术上讲,这些区别是不需要的,但是由于我们将与动态生成的ARNs相结合,因此更容易将相同的约定应用于两者,因此我将首先调整本地值为映射,其中键将是这些策略的本地标识符:
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之间的分裂中,这里没有真正需要捕获的任何特殊细微差别,因此我在这里保持简单,并建立了一个约定,即本地名称是策略的name
,这是我们可以系统地从"custom"导出的东西;的:
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
的值也可能在apply之后是已知的,在这种情况下,我们需要一种不同的策略来为每个实例分配一个静态键。
所有这些的结果是,您的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"]
- 等等,无论您的自定义策略有什么名称