如何修复Terraform for_each的IAM策略绑定错误?转map(object(list))可行吗?
在添加IAM策略时,Terraform代码出现以下错误,原因是部分属性需在apply后才能确定,不想执行多次apply命令,该如何修复?将default_policies转换为map(object(list))能否解决此问题?
代码片段
locals { default_policies = toset([ "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess", "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy", "arn:aws:iam::aws:policy/AmazonS3FullAccess", "arn:aws:iam::aws:policy/AmazonEC2FullAccess", "${aws_iam_policy.assume_prod_policy.arn}" ]) } data "aws_iam_policy_document" "assume_prod_policy_document" { statement { actions = ["sts:AssumeRole"] effect = "Allow" resources = ["arn:aws:iam::${local.account_id}:role/${var.prod_role}"] } } resource "aws_iam_policy" "assume_prod_policy" { name = "amp_assume_ecr_prod_policy" policy = data.aws_iam_policy_document.assume_prod_policy_document.json } # Attach policy resource "aws_iam_role_policy_attachment" "ga_policy_data" { for_each = local.default_policies policy_arn = each.value role = aws_iam_role.xxx.name }
错误信息
Error: Invalid for_each argument
on main.tf line 74, in resource "aws_iam_role_policy_attachment" "ga_policy_data":
74: for_each = local.default_policies
├────────────────
│ local.default_policies is set of string with 5 elementsThe "for_each" set includes values derived from resource attributes that cannot be
determined until apply, and so Terraform cannot determine the full set of keys that
will identify the instances of this resource.When working with unknown values in for_each, it's better to use a map value where
the keys are defined statically in your configuration and where only the values
contain apply-time results.Alternatively, you could use the -target planning option to first apply only the
resources that the for_each value depends on, and then apply a second time to fully
converge.
核心问题
错误根源是for_each使用的集合中包含aws_iam_policy.assume_prod_policy.arn这个仅能在apply阶段确定的值,而Terraform需要在plan阶段明确for_each的所有标识键,无法提前预知动态ARN,因此触发报错。
修复方法
按照错误提示的建议,改用带静态键的map结构,让Terraform在plan阶段就能确定所有实例的标识(即map的键),值可以保留动态生成的ARN。具体修改如下:
- 将
local.default_policies改为map类型,用静态策略名称作为键,对应ARN作为值:
locals { default_policies = { ecr_full_access = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess", eks_worker_node = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy", s3_full_access = "arn:aws:iam::aws:policy/AmazonS3FullAccess", ec2_full_access = "arn:aws:iam::aws:policy/AmazonEC2FullAccess", assume_prod = aws_iam_policy.assume_prod_policy.arn } }
- 调整
aws_iam_role_policy_attachment的for_each和policy_arn字段:
resource "aws_iam_role_policy_attachment" "ga_policy_data" { for_each = local.default_policies policy_arn = each.value role = aws_iam_role.xxx.name }
修改后,map的所有键都是静态定义的,Terraform在plan阶段就能明确要创建5个attachment实例,每个实例的标识对应map的键,而动态生成的ARN仅作为值存在,不影响plan阶段的资源识别,一次apply即可完成所有资源部署。
关于map(object(list))的疑问
不需要转换为map(object(list)),你的场景中每个元素只是单个ARN字符串,用简单的map(string)就足够解决问题。map(object(list))是更复杂的嵌套结构,适用于每个元素包含多属性或列表的场景,在这里使用反而会增加配置复杂度,完全没必要。
内容的提问来源于stack exchange,提问作者user6826691

