Terraform aws_iam_policy_attachment Policy 竞争问题

自从  Terraform  resource "aws_iam_role" 不推荐使用 "inline_policy" 和  "managed_policy_arns" 以后,就尝试了用 "aws_iam_policy_attachment" 来为 iam role 指定 AWS  内置和自定义的 IAM policy。因为在官方文档 aws_iam_role 中最先看到的就是 aws_iam_policy_attachment, 其实仔细阅读该文档的话,建议是

  1. 用 "aws_iam_role_policy" 来代替 "inline_policy"
  2. 用  "aws_iam_role_policy_attachment"  来代替 "managed_policy_arns"

然后在项目中为避免 inline_policy 和 managed_policy_arns 的警告而选择了通用的 aws_iam_policy_attachment, 同时原来也用的 "aws_iam_policy",而非 "aws_iam_role_policy。 所以才撞入到以前一直用 aws_iam_role(inline_policy, managed_policy_arns) + aws_iam_policy 就没出过问题。

问题重现

下面来重现一下问题,想要在独产的两个模块中(有各自的 terraform 状态文件),分别创建一个 IAM role,分别赋予自定义的 Policy 和系统 Policy AWSLambdaBasicExecutionRole, 即

IAM Role: test1-lambda-roleIAM Role: test2-lambda-role               

它们的 Terraform 代码相似,分别是两个目录中 test1/main.tf 和  test2/main.tf

test1/main.tf 文件内容为

test2/main.tf,文件内容与 test1/main.tf 基本一致,只是把所有出现的 "test1" 替换为  "test2",所以此处不再列出来

第一次执行创建的 test1-lambda-role 和 test2-lambda-role 的权限都正常, 就是如最前面两张图所显示的那样

创建 test1-lambda-role

cd test1
terraform apply

完后用命令 aws iam list-attached-role-policies --role-name test1-lambda-role 看到的

创建 test2-lambda-role

cd ../test2
terraform apply

完后用命令 aws iam list-attached-role-policies --role-name test2-lambda-role 看到的

现在一切正常。但如果再回过头再执行 test1 的 terraform apply 时奇怪的事情就发生了

cd ../test1
terraform apply

现在看到的 plan 是

明明是在操作 test1 模块,却看到了 test2-lambda-role 的内容,而且 AWSDeepRacerLambdaAccessRole 也是凭空出现的。

输入  yes 应用更改,然后查看 test1-lambda-role 和 test2-lambda-role 是否有变化

test1-lambda-role 没问题,但 Terraform 在执行 test1 模块时却把 test2-lambda-role 给影响了,把它持有的 AWSLambdaBasicExecutionRole 给拿走了。

再执行 test2 的 Terraform 代码

cd ../test2
terraform apply

plan 里的显示的内容更有意思了

输入 yes, 应用更改,再检查 test1-lambda-role 和 test2-lambda-role 的内容

看到应用 test2 时,把 test1-lambda-role 的 AWSLambdaBasicExecutionRole 给偷走了,相当于是因为 test1-lambda-role 和  test2-lambda-role 共享了 AWSLambdaBasicExeutionRole,执行 test1 和 test2 时会在这两个 role 之间移动 AWSLambdaBasicExecutionRole,都试图独占这个 Policy。

此时再回到 test1 去执行 terraform 脚本,那么  AWSLambdaBasicExecutionRole 又被移动到了 test1-lambda-role。如果在该  AWS 帐号下有其他更多的 IAM Role 使用了这种  Terraform 脚本来创建的话,那么会有更多的 IAM Role 加入到这种争夺 AWSLambdaBasicExecutionRole 的混战当中来。

初步怀疑(毫不相关)

怀疑是不是因为  aws_iam_policy_attachment 后的标识有问题,把它们换成带特定前缀的标识

resource "aws_iam_policy_attachment" "lambda-execution-role" 换成 resource "aws_iam_policy_attachment" "test1-lambda-execution-role"

resource "aws_iam_policy_attachment" "lambda-policy" 换成 resource "aws_iam_policy_attachment" "test1-lambda-policy"

在 test2 中作相应用改动,用 test2 作为前缀,但故障依旧。也证明了以往的经验,resource 的标识作用域只在当前模块下的相同资源。

只尝试用 aws_iam_role_policy_attachment(故障依旧)

后来又尝试了把 aws_iam_policy_attachment 替换成了特定的 aws_iam_role_policy_attachment

比如在  test1/main.tf 中的两个 aws_iam_role_policy_attachment 分别变为

使用  aws_iam_role_policy_attachment 不用指定 name 了,并且 role 是单数

在 test2/main.tf 中也作相应的修改,注意 test1 的地方换成 test2 就是

没有什么变化,还是一样的故障,互相争抢 AWSLambdaBasicExeutionRole

把 aws_iam_policy 替换成 aws_iam_role_policy(问题解决)

由于 aws_iam_role_policy 自带  role 属性,所以就可以少写下面这个 aws_iam_policy_attachment

resource "aws_iam_policy_attachment" "lambda-policy" {

所以整个  test1/main.tf 除了 aws_iam_role 外的内容就是

在 test2/main.tf 也作相应的修改

这种改法问题可以得到解决,test1-lamba-role 和  test2-lambda-role 不再相关了,此时 test1-lambda-inline-policy (或 test2-lambda-inline-policy)不再是作为 Customer managed policy 了, 而是真正的 Customer inline

这样也更贴合 inline-policy 的初衷,原本就是特别为该 Role 创建的 policy 应该保持为私有  Customer inline, 而不该出现在 AWS IAM 的 Policies 列表中。

这里我们恰好用 aws_iam_role_policy_attachment 避开了一个问题,如果把这里的 aws_iam_role_policy_attachment 换回成 aws_iam_policy_attachment 依然会出现争夺 AWSLambdaBasicExecutionRole 的问题。稍为小结一下,出现故障的三种情况分别是

  1. aws_iam_policy + aws_iam_policy_attachement
  2. aws_iam_policy + aws_iam_role_policy_attachment
  3. aws_iam_role_policy + aws_iam_policy_attachement

用 aws_iam_role_policy + aws_iam_policy_attachment 就正常了,所以 aws_iam_role_policy_attachement 严格来说不能算作 aws_iam_policy_attachment 的泛化。

另一种解法(aws_iam_policy + aws_iam_role_policy_attachment(for_each))

看到上一节的 #2 简单用 aws_iam_policy 和 aws_iam_role_policy_attachment 还是有问题,  但是配合后者的 for_each 就不一样了,在 test1/main.tf 中除去 aws_iam_role 之外的代码可以写成

test2/main.tf 中也作相应的更改,这样的话执行 test1 和 test2 的 terraform 后,test1-lambda-role 和 test2-lambda-role 也能互不相关

重试 aws_iam_policy + aws_iam_policy_attachment(for_each)(仍然失败)

但是对 aws_iam_policy_attachment 还存有一线希望的话,重新试下 aws_iam_policy + aws_iam_policy_attachment(for_each) 的组合

依然不奏效,问题依旧。

总结

通过以上种种尝试,必须要总结一下有效的解决办法是

  1. aws_iam_role_policy + aws_iam_role_policy_attachment: 在 aws_iam_role_policy 可以指定关联的 iam_role, 将此作为 Customer inline policy, 用 aws_iam_role_policy_attachment 挂载别的 policy。有了 aws_iam_role_policy 的加持,即使使用多个  aws_iam_role_policy_attachment 或是 for_each 都可以挂载上多个其他的  policy。
  2. aws_iam_policy 时, 只用一个 aws_iam_role_policy_attachment, 在其中用 for_each 可以同时挂载包括自定义的或系统定义的多个 policy

以上第一种方法为首选,且保持专属的 Policy 为  Customer inline。

用 aws_iam_role_policy_attachment 关联的 policy 是 AWS managed 或 Customer managed.

请优先使用 role_policy 相关的资源,能用  aws_iam_role_policy 就不用  aws_iam_policy, 能用  aws_iam_role_policy_attachment 就不用 aws_iam_policy_attachment。从本文的情景来看使用 aws_iam_policy_attachment 总是会出现竞争共有 Policy 的问题。

最后,以上内容以供参考,实际表现也可能有所不同。

本文链接 https://yanbin.blog/terraform-aws_iam_policy_attachment-policy-issue/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

guest

0 Comments
Inline Feedbacks
View all comments