问题描述
在使用Terraform时,遇到了一个问题:他需要将多个Terraform根模块添加到一个现有的策略中,该策略为S3存储桶提供只读权限,每个模块都有自己的存储桶。用户无法确定如何将模块添加到现有策略中,并将新值(新存储桶)合并到策略中。
在最初的尝试中,用户只是将一个新的策略附加到角色上(实际上有三个策略,一个用于开发、一个用于演练、一个用于生产)。但他们只能附加最多20个策略。因此,用户使用了AWS命令行界面(AWS CLI),将所有生产存储桶列在一个策略中,演练和开发也是如此。这一步已经很成功。
现在,用户希望通过Terraform实现相同的目标。他已经创建了一个子模块,根模块可以调用该子模块,并传递他们的存储桶信息。用户使用了如下所示的Terraform代码定义了新的策略:
data "aws_iam_policy_document" "read_only" {
statement {
actions = [
"s3:ListBucket",
"s3:GetObject",
"s3:GetBucketLocation",
]
effect = "Allow"
resources = [
"${var.bucket_arn}/*",
var.bucket_arn,
]
}
}
他还可以通过以下代码查找现有策略:
data "aws_iam_policy" "existing" {
name = "read-only-s3-${terraform.workspace}"
}
并使用以下代码合并新策略和现有策略:
data "aws_iam_policy_document" "merged" {
source_json = [
data.aws_iam_policy_document.read_only.json,
data.aws_iam_policy.existing.json
]
}
然后,用户尝试创建资源:
resource "aws_iam_policy" "read_only_policy" {
name = "read-only-s3-${terraform.workspace}"
path = "/"
policy = data.aws_iam_policy_document.merged.json
}
但是,如果没有data.aws_iam_policy.existing
,AWS无法找到现有策略,Terraform将抛出错误。
虽然关于如何合并策略有很多文档,但关于如何确定是否存在现有策略的方法却很少。用户可以重新创建整个策略,如果他能找到现有的策略并将其与新策略合并,或者仅仅知道现有策略中存储桶的名称,那将是理想的。但是现有的策略是在其他根模块中创建的,而且没有办法通过名称以外的方式查找存储桶(没有tags
或在data "aws_s3_bucket"
上的filter
)。
用户想知道是否有一种方法可以确定是否存在现有策略,而不会抛出错误?然后他可以获取其策略文档并使用source_json
方法。用户是否可以将新的策略文档合并到现有的文档中(无论存在与否)?用户对其他方法也持开放态度,但是他真的希望将其保留在IAM中,而不是使用存储桶策略,因为后者过于不透明和分散。
解决方案
请注意以下操作注意版本差异及修改前做好备份。
方法1:使用外部数据源检查策略是否存在
在Terraform中,可以使用外部数据源来检查策略是否存在。以下是一个示例,演示如何使用外部数据源来实现这个目标。
- 首先,创建一个外部数据源,用于检查策略是否存在。在Terraform配置文件中添加以下内容:
data "external" "iam-policy-exists" {
program = ["bash", "${path.module}/iam-policy-exists.sh"]
query = {
policy = "read-only-s3-${terraform.workspace}"
}
}
- 创建一个bash脚本,用于检查IAM策略是否存在。在与Terraform配置文件相同的目录下创建一个名为
iam-policy-exists.sh
的文件,并将以下内容添加到该文件:
#!/bin/bash
aws iam get-policy --policy-arn arn:aws:iam::ACCOUNT_ID:policy/read-only-s3-${1}
# 如果策略存在,返回状态码0,否则返回非零状态码
if [ $? -eq 0 ]; then
echo '{"exists":"yes"}'
else
echo '{"exists":"nope"}'
fi
请将ACCOUNT_ID
替换为您的AWS帐户ID。
- 现在,在Terraform中使用
data "aws_iam_policy"
来检查策略是否存在,并根据结果来决定是否创建新策略。在Terraform配置文件中添加以下内容:
data "aws_iam_policy" "existing" {
count = data.external.iam-policy-exists.exists == "yes" ? 1 : 0
name = "read-only-s3-${terraform.workspace}"
}
resource "aws_iam_policy" "read_only_policy" {
count = data.external.iam-policy-exists.exists == "nope" ? 1 : 0
name = "read-only-s3-${terraform.workspace}"
path = "/"
policy = data.aws_iam_policy_document.merged.json
}
- (可选) 如果您想要获取合并策略的JSON表示,可以使用
locals
块将结果保存到本地。在Terraform配置文件中添加以下内容:
“`hcl
locals