我目前在通过多个其他子帐户访问的中央帐户中有一个集中式S3存储桶。我想通过采用现有的IAM存储桶策略来动态更新每个子帐户的存储桶策略,并附加一条新语句,以允许调用帐户中的IAM角色访问存储桶。
我试图结合使用aws_iam_policy_document和source_json和新语句。但是,所有这些操作将覆盖现有语句。
我想要实现的是采用现有策略json并合并或追加到新语句。任何想法如何实现这一目标?
有人能管理跨帐户s3存储桶策略并动态更新该策略以允许子帐户中的角色进行访问吗?
答案 0 :(得分:2)
aws_iam_policy_document
数据源的source_json
参数通过使用策略语句的语句ID(“ sid”)值合并策略语句来工作,因此为了使来自先前JSON的语句出现在结果中,{{ 1}}新语句中的参数必须与源文档中的参数不同。
另一个选择是在Terraform语言本身中更手动地进行转换。这要求直接使用原始语句数据结构,因此,您有责任确保稳健地处理输入并生成有效的IAM策略数据结构。
例如:
sid
然后,您可以使用locals {
policy_a = jsondecode(file("${path.module}/policy_a.json"))
policy_b = jsondecode(file("${path.module}/policy_b.json"))
policy_c = {
Version: local.policy_a.Version,
Statement: concat(
local.policy_a.Statement,
local.policy_b.Statement,
),
}
}
在模块的其他位置生成policy_c
的JSON版本。
因为这使用的是jsonencode(local.policy_c)
,所以结果实际上是将两个语句列表串联在一起,因此您需要确保结果对您自己是明智的:不会通过语句id或任何类似的规范化。
答案 1 :(得分:0)
我遇到了类似的情况,并且解决方法很复杂,涉及外部脚本和null资源。在我的情况下,我使用了多个terraform工作区,每个工作区都有自己的Cloudfront分布,但是所有人都使用S3存储桶作为其中一个起源。对于每个工作区,我需要将工作区的Cloudfront原始访问身份附加到存储桶的策略中,而不替换或破坏该策略中已经存在的条目。
该存储桶是手动创建的(在terraform外部),并将其作为数据资源导入。
我有3个外部脚本(尽管它可能是一个更复杂的脚本)-一个用于创建具有新标识的策略,一个用于创建不带标识的策略(用于destroy
),以及一个实际应用创建的策略(由null资源使用,直接调用aws
命令)。
resource "aws_cloudfront_origin_access_identity" "s3_identity" {
comment = terraform.workspace
}
data "external" "append_bucket_policy" {
program = ["scripts/append_bucket_policy.sh", data.aws_s3_bucket.bucket.id, aws_cloudfront_origin_access_identity.s3_identity.iam_arn]
}
data "external" "cleanup_bucket_policy" {
program = ["scripts/cleanup_bucket_policy.sh", data.aws_s3_bucket.bucket.id, aws_cloudfront_origin_access_identity.s3_identity.id]
}
locals {
bucket_identifiers = compact(split(",", data.external.append_bucket_policy.result["principals"]))
cleanup_policy_identifiers = compact(split(",", data.external.cleanup_bucket_policy.result["principals"]))
}
data "aws_s3_bucket" "bucket" {
bucket = "bucketname"
}
data "aws_iam_policy_document" "append_policy" {
statement {
actions = ["s3:GetObject"]
resources = ["${data.aws_s3_bucket.bucket.arn}/*"]
principals {
type = "AWS"
identifiers = local.bucket_identifiers
}
}
statement {
actions = ["s3:ListBucket"]
resources = [data.aws_s3_bucket.bucket.arn]
principals {
type = "AWS"
identifiers = local.bucket_identifiers
}
}
}
data "aws_iam_policy_document" "cleanup_policy" {
statement {
actions = ["s3:GetObject"]
resources = ["${data.aws_s3_bucket.bucket.arn}/*"]
principals {
type = "AWS"
identifiers = local.cleanup_policy_identifiers
}
}
statement {
actions = ["s3:ListBucket"]
resources = [data.aws_s3_bucket.bucket.arn]
principals {
type = "AWS"
identifiers = local.cleanup_policy_identifiers
}
}
}
resource "null_resource" "apply_policy" {
depends_on = [data.external.append_bucket_policy, data.external.append_bucket_policy, data.aws_iam_policy_document.append_policy, data.external.cleanup_bucket_policy, data.aws_iam_policy_document.cleanup_policy, aws_cloudfront_origin_access_identity.s3_identity]
provisioner "local-exec" {
when = create
command = "scripts/apply_bucket_policy.sh ${data.aws_s3_bucket.bucket.id} '${data.aws_iam_policy_document.append_policy.json}'"
}
provisioner "local-exec" {
when = destroy
command = "scripts/apply_bucket_policy.sh ${data.aws_s3_bucket.bucket.id} '${data.aws_iam_policy_document.cleanup_policy.json}'"
}
}
append_bucket_policy.sh
#!/bin/bash
set -eo pipefail
# To be called as an external data resource program by terraform
# Usage: append_bucket_policy.sh <bucket name> <principal to append>
#
# Retrieves the current principals in bucket policy for <bucket name>, appends <principal to append>, uniqs the output to remove any duplicates, concats the principals into a comma seperated string, and outputs a json blob that terraform can understand
# In terraform, the variable is split on commas to create a list, which is used in the "identifiers" of an iam policy document resource, and re-applied to the bucket
IFS='
'
# this likely won't work with more complex bucket policies, but it does the job for cloudfront origin access identities
CURRENT=$(aws --region us-west-2 s3api get-bucket-policy --bucket "${1}" | jq --raw-output '.Policy' | jq '.Statement[].Principal.AWS[]')
TEMP=$(for i in ${CURRENT} ; do
printf "%s\n" "${i},"
done
printf "\"%s\"" "${2},")
NEW=$(echo "${TEMP}" | sort | uniq | tr -d '\n')
jq -n --arg principals "${NEW}" '.principals = $principals' | sed 's/\\\"//g'
cleanup_bucket_policy.sh
#!/bin/bash
set -eo pipefail
IFS='
'
CURRENT=$(aws --region us-west-2 s3api get-bucket-policy --bucket "${1}" | jq --raw-output '.Policy' | jq '.Statement[].Principal.AWS[]')
TEMP=$(for i in ${CURRENT} ; do
printf "%s\n" "${i},"
done | egrep -v "${2}")
NEW=$(echo "${TEMP}" | sort | uniq | tr -d '\n')
jq -n --arg principals "${NEW}" '.principals = $principals' | sed 's/\\\"//g'
apply_bucket_policy.sh
#!/bin/bash
# apply bucket policy generated by append_bucket_policy.sh
set -eo pipefail
# may fail to apply if the origin access identity was just created, wait a few seconds
sleep 10
RESULT=$(aws --region us-west-2 s3api put-bucket-policy --bucket "${1}" --policy "${2}")
jq -n --arg result "${RESULT}" '.result = $result' | sed 's/\\\"//g'
我为此感到非常抱歉,也许不是100%合适,但过去它对我有用,并可能为您激发一些想法。我也欢迎一种更好的方法!