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, 其实仔细阅读该文档的话,建议是
然后在项目中为避免 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 就没出过问题。
IAM Role: test1-lambda-role
IAM 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
创建 test2-lambda-role
现在一切正常。但如果再回过头再执行 test1 的 terraform apply 时奇怪的事情就发生了
明明是在操作 test1 模块,却看到了 test2-lambda-role 的内容,而且 AWSDeepRacerLambdaAccessRole 也是凭空出现的。
输入 yes 应用更改,然后查看 test1-lambda-role 和 test2-lambda-role 是否有变化
test1-lambda-role 没问题,但 Terraform 在执行 test1 模块时却把 test2-lambda-role 给影响了,把它持有的 AWSLambdaBasicExecutionRole 给拿走了。
再执行 test2 的 Terraform 代码
输入 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 的混战当中来。
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 的标识作用域只在当前模块下的相同资源。
比如在 test1/main.tf 中的两个 aws_iam_role_policy_attachment 分别变为
使用 aws_iam_role_policy_attachment 不用指定 name 了,并且 role 是单数
在 test2/main.tf 中也作相应的修改,注意 test1 的地方换成 test2 就是
没有什么变化,还是一样的故障,互相争抢 AWSLambdaBasicExeutionRole
在 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 的问题。稍为小结一下,出现故障的三种情况分别是
用 aws_iam_role_policy + aws_iam_policy_attachment 就正常了,所以 aws_iam_role_policy_attachement 严格来说不能算作 aws_iam_policy_attachment 的泛化。
test2/main.tf 中也作相应的更改,这样的话执行 test1 和 test2 的 terraform 后,test1-lambda-role 和 test2-lambda-role 也能互不相关
依然不奏效,问题依旧。
以上第一种方法为首选,且保持专属的 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's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
- 用 "aws_iam_role_policy" 来代替 "inline_policy"
- 用 "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-role
IAM Role: test2-lambda-role

它们的 Terraform 代码相似,分别是两个目录中 test1/main.tf 和 test2/main.tf
test1/main.tf 文件内容为
1resource "aws_iam_role" "test1_lambda_role" {
2 name = "test1-lambda-role"
3 assume_role_policy = jsonencode(
4 {
5 Version = "2012-10-17",
6 Statement = [
7 {
8 Effect = "Allow",
9 Principal = {
10 Service = "lambda.amazonaws.com"
11 },
12 Action = "sts:AssumeRole"
13 }
14 ]
15 })
16}
17
18resource "aws_iam_policy_attachment" "lambda-execution-role" {
19 name = "test1-lambda-attach-lambda-execution-role"
20 roles = [aws_iam_role.test1_lambda_role.name]
21 policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
22}
23
24resource "aws_iam_policy_attachment" "lambda-policy" {
25 name = "test1-lambda-attach-lambda-policy"
26 roles = [aws_iam_role.test1_lambda_role.name]
27 policy_arn = aws_iam_policy.test1-inline-policy.arn
28}
29
30resource "aws_iam_policy" "test1-inline-policy" {
31 name = "test1-lambda-inline-policy"
32 policy = jsonencode({
33 Version = "2012-10-17"
34 Statement = [
35 {
36 "Action" : [
37 "s3:GetObject"
38 ],
39 "Effect" : "Allow",
40 "Resource" : [
41 "arn:aws:s3:::test1/*"
42 ]
43 }
44 ]
45 })
46}test2/main.tf,文件内容与 test1/main.tf 基本一致,只是把所有出现的 "test1" 替换为 "test2",所以此处不再列出来
第一次执行创建的 test1-lambda-role 和 test2-lambda-role 的权限都正常, 就是如最前面两张图所显示的那样
创建 test1-lambda-role
cd test1完后用命令 aws iam list-attached-role-policies --role-name test1-lambda-role 看到的
terraform apply
1{
2 "AttachedPolicies": [
3 {
4 "PolicyName": "test1-lambda-inline-policy",
5 "PolicyArn": "arn:aws:iam::069762108088:policy/test1-lambda-inline-policy"
6 },
7 {
8 "PolicyName": "AWSLambdaBasicExecutionRole",
9 "PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
10 }
11 ]
12}创建 test2-lambda-role
cd ../test2完后用命令 aws iam list-attached-role-policies --role-name test2-lambda-role 看到的
terraform apply
1{
2 "AttachedPolicies": [
3 {
4 "PolicyName": "test2-lambda-inline-policy",
5 "PolicyArn": "arn:aws:iam::069762108088:policy/test2-lambda-inline-policy"
6 },
7 {
8 "PolicyName": "AWSLambdaBasicExecutionRole",
9 "PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
10 }
11 ]
12}现在一切正常。但如果再回过头再执行 test1 的 terraform apply 时奇怪的事情就发生了
cd ../test1现在看到的 plan 是
terraform apply
1 # aws_iam_policy_attachment.lambda-execution-role will be updated in-place
2 ~ resource "aws_iam_policy_attachment" "lambda-execution-role" {
3 id = "test1-lambda-attach-lambda-execution-role"
4 name = "test1-lambda-attach-lambda-execution-role"
5 ~ roles = [
6 - "AWSDeepRacerLambdaAccessRole",
7 - "test2-lambda-role",
8 # (1 unchanged element hidden)
9 ]
10 # (3 unchanged attributes hidden)
11 }
12
13Plan: 0 to add, 1 to change, 0 to destroy.明明是在操作 test1 模块,却看到了 test2-lambda-role 的内容,而且 AWSDeepRacerLambdaAccessRole 也是凭空出现的。
输入 yes 应用更改,然后查看 test1-lambda-role 和 test2-lambda-role 是否有变化
1➜ ~ aws iam list-attached-role-policies --role-name test1-lambda-role
2{
3 "AttachedPolicies": [
4 {
5 "PolicyName": "test1-lambda-inline-policy",
6 "PolicyArn": "arn:aws:iam::069762108088:policy/test1-lambda-inline-policy"
7 },
8 {
9 "PolicyName": "AWSLambdaBasicExecutionRole",
10 "PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
11 }
12 ]
13}
14➜ ~ aws iam list-attached-role-policies --role-name test2-lambda-role
15{
16 "AttachedPolicies": [
17 {
18 "PolicyName": "test2-lambda-inline-policy",
19 "PolicyArn": "arn:aws:iam::069762108088:policy/test2-lambda-inline-policy"
20 }
21 ]
22}test1-lambda-role 没问题,但 Terraform 在执行 test1 模块时却把 test2-lambda-role 给影响了,把它持有的 AWSLambdaBasicExecutionRole 给拿走了。
再执行 test2 的 Terraform 代码
cd ../test2plan 里的显示的内容更有意思了
terraform apply
1 # aws_iam_policy_attachment.lambda-execution-role will be updated in-place
2 ~ resource "aws_iam_policy_attachment" "lambda-execution-role" {
3 id = "test2-lambda-attach-lambda-execution-role"
4 name = "test2-lambda-attach-lambda-execution-role"
5 ~ roles = [
6 - "test1-lambda-role",
7 + "test2-lambda-role",
8 ]
9 # (3 unchanged attributes hidden)
10 }输入 yes, 应用更改,再检查 test1-lambda-role 和 test2-lambda-role 的内容
1➜ ~ aws iam list-attached-role-policies --role-name test1-lambda-role
2{
3 "AttachedPolicies": [
4 {
5 "PolicyName": "test1-lambda-inline-policy",
6 "PolicyArn": "arn:aws:iam::069762108088:policy/test1-lambda-inline-policy"
7 }
8 ]
9}
10➜ ~ aws iam list-attached-role-policies --role-name test2-lambda-role
11{
12 "AttachedPolicies": [
13 {
14 "PolicyName": "test2-lambda-inline-policy",
15 "PolicyArn": "arn:aws:iam::069762108088:policy/test2-lambda-inline-policy"
16 },
17 {
18 "PolicyName": "AWSLambdaBasicExecutionRole",
19 "PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
20 }
21 ]
22}看到应用 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 分别变为
1resource "aws_iam_role_policy_attachment" "lambda-execution-role" {
2 role = aws_iam_role.test1_lambda_role.name
3 policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
4}
5
6resource "aws_iam_role_policy_attachment" "lambda-policy" {
7 role = aws_iam_role.test1_lambda_role.name
8 policy_arn = aws_iam_policy.test1-inline-policy.arn
9}使用 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_attachmentresource "aws_iam_policy_attachment" "lambda-policy" {所以整个 test1/main.tf 除了 aws_iam_role 外的内容就是
1resource "aws_iam_role_policy_attachment" "lambda-execution-role" {
2 role = aws_iam_role.test1_lambda_role.name
3 policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
4}
5
6resource "aws_iam_role_policy" "test1-inline-policy" {
7 role = aws_iam_role.test1_lambda_role.name
8 name = "test1-lambda-inline-policy"
9 policy = jsonencode({
10 Version = "2012-10-17"
11 Statement = [
12 {
13 "Action" : [
14 "s3:GetObject"
15 ],
16 "Effect" : "Allow",
17 "Resource" : [
18 "arn:aws:s3:::test1/*"
19 ]
20 }
21 ]
22 })
23}在 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 的问题。稍为小结一下,出现故障的三种情况分别是
- aws_iam_policy + aws_iam_policy_attachement
- aws_iam_policy + aws_iam_role_policy_attachment
- 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 之外的代码可以写成 1resource "aws_iam_role_policy_attachment" "lambda-execution-role" {
2 for_each = {
3 inline = aws_iam_policy.test1-inline-policy.arn,
4 lambda = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
5 }
6
7 role = aws_iam_role.test1_lambda_role.name
8 policy_arn = each.value
9}
10
11resource "aws_iam_policy" "test1-inline-policy" {
12 name = "test1-lambda-inline-policy"
13 policy = jsonencode({
14 Version = "2012-10-17"
15 Statement = [
16 {
17 "Action" : [
18 "s3:GetObject"
19 ],
20 "Effect" : "Allow",
21 "Resource" : [
22 "arn:aws:s3:::test1/*"
23 ]
24 }
25 ]
26 })
27}test2/main.tf 中也作相应的更改,这样的话执行 test1 和 test2 的 terraform 后,test1-lambda-role 和 test2-lambda-role 也能互不相关
1➜ ~ aws iam list-attached-role-policies --role-name test1-lambda-role
2{
3 "AttachedPolicies": [
4 {
5 "PolicyName": "test1-lambda-inline-policy",
6 "PolicyArn": "arn:aws:iam::069762108088:policy/test1-lambda-inline-policy"
7 },
8 {
9 "PolicyName": "AWSLambdaBasicExecutionRole",
10 "PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
11 }
12 ]
13}
14➜ ~ aws iam list-attached-role-policies --role-name test2-lambda-role
15{
16 "AttachedPolicies": [
17 {
18 "PolicyName": "terraform-20251114035130238700000001",
19 "PolicyArn": "arn:aws:iam::069762108088:policy/terraform-20251114035130238700000001"
20 },
21 {
22 "PolicyName": "AWSLambdaBasicExecutionRole",
23 "PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
24 }
25 ]
26}重试 aws_iam_policy + aws_iam_policy_attachment(for_each)(仍然失败)
但是对 aws_iam_policy_attachment 还存有一线希望的话,重新试下 aws_iam_policy + aws_iam_policy_attachment(for_each) 的组合 1resource "aws_iam_policy_attachment" "lambda-execution-role" {
2 name = "test1-lambda-attach-policy"
3 for_each = {
4 inline = aws_iam_policy.test1-inline-policy.arn,
5 lambda = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
6 }
7
8 roles = [aws_iam_role.test1_lambda_role.name]
9 policy_arn = each.value
10}
11
12resource "aws_iam_policy" "test1-inline-policy" {
13 ......依然不奏效,问题依旧。
总结
通过以上种种尝试,必须要总结一下有效的解决办法是- 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。
- 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's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。