基于for_each的Terraform模块:为多个S3桶添加桶策略
解决Terraform S3模块添加强制HTTPS访问策略的问题
问题背景
现有可正常运行的Terraform模块用于创建带私有ACL的S3桶,根模块通过for_each遍历桶名集合调用该模块。需要为所有桶添加强制HTTPS访问的S3桶策略,尝试count方式无效,改用for_each时因模块内传入的是单个字符串而非集合遇到类型问题。
现有代码
S3模块代码
resource "aws_s3_bucket" "buckets" { bucket = var.s3_buckets } resource "aws_s3_bucket_acl" "buckets" { bucket = var.s3_buckets acl = "private" }
根模块调用代码
module "s3_buckets" { source = "./modules/s3" for_each = toset([ "bucket-test1-${var.my_env}", "bucket-test2-${var.my_env}", ]) s3_buckets = each.value }
无效的count尝试代码
data "aws_iam_policy_document" "buckets" { count = length(var.s3_buckets) statement { sid = "AllowSSlRequestsOnly" actions = ["s3:*"] effect = "Deny" condition { test = "Bool" variable = "aws:SecureTransport" values = ["false"] } principals { type = "*" identifiers = ["*"] } resources = ["arn:aws:s3:::${var.s3_buckets}"] } } resource "aws_s3_bucket_policy" "buckets" { bucket = var.s3_buckets policy = data.aws_iam_policy_document.buckets[count.index].json }
解决方案
方案一:调整模块变量为集合类型(推荐)
这种方式让模块直接处理桶名集合,内部用for_each批量创建资源和策略。
- 修改模块变量定义(新增
variables.tf或修改现有文件):
variable "s3_buckets" { type = set(string) description = "要创建的S3桶名称集合" }
- 更新模块内资源代码:
resource "aws_s3_bucket" "buckets" { for_each = var.s3_buckets bucket = each.value } resource "aws_s3_bucket_acl" "buckets" { for_each = var.s3_buckets bucket = each.value acl = "private" } data "aws_iam_policy_document" "bucket_https_policy" { for_each = var.s3_buckets statement { sid = "AllowSSlRequestsOnly" actions = ["s3:*"] effect = "Deny" condition { test = "Bool" variable = "aws:SecureTransport" values = ["false"] } principals { type = "*" identifiers = ["*"] } # 包含桶本身和桶内所有对象 resources = [ "arn:aws:s3:::${each.value}", "arn:aws:s3:::${each.value}/*" ] } } resource "aws_s3_bucket_policy" "buckets" { for_each = var.s3_buckets bucket = each.value policy = data.aws_iam_policy_document.bucket_https_policy[each.value].json }
- 修改根模块调用:
module "s3_buckets" { source = "./modules/s3" s3_buckets = toset([ "bucket-test1-${var.my_env}", "bucket-test2-${var.my_env}", ]) }
方案二:保持现有变量类型,单个桶实例添加策略
如果不想修改根模块调用方式,可直接在现有模块中为单个桶添加策略,利用根模块的for_each自动为每个桶实例创建策略。
更新模块代码:
resource "aws_s3_bucket" "buckets" { bucket = var.s3_buckets } resource "aws_s3_bucket_acl" "buckets" { bucket = var.s3_buckets acl = "private" } data "aws_iam_policy_document" "bucket_https_policy" { statement { sid = "AllowSSlRequestsOnly" actions = ["s3:*"] effect = "Deny" condition { test = "Bool" variable = "aws:SecureTransport" values = ["false"] } principals { type = "*" identifiers = ["*"] } resources = [ "arn:aws:s3:::${var.s3_buckets}", "arn:aws:s3:::${var.s3_buckets}/*" ] } } resource "aws_s3_bucket_policy" "buckets" { bucket = var.s3_buckets policy = data.aws_iam_policy_document.bucket_https_policy.json }
根模块调用保持原有代码不变即可,因为根模块通过for_each为每个桶名创建一个模块实例,每个实例都会独立创建桶、ACL和HTTPS策略。
原count方式无效原因
原代码中var.s3_buckets是单个字符串,length(var.s3_buckets)返回的是字符串的字符长度而非集合元素数量,导致count会创建与字符数相同数量的策略资源,完全不符合需求。
内容的提问来源于stack exchange,提问作者truesamurai
相关产品推荐
相关产品推荐

