You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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批量创建资源和策略。

  1. 修改模块变量定义(新增variables.tf或修改现有文件):
variable "s3_buckets" {
  type        = set(string)
  description = "要创建的S3桶名称集合"
}
  1. 更新模块内资源代码:
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
}
  1. 修改根模块调用:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 13:01:05