使用Terraform创建S3 Bucket ACL及Policy时遇Access Denied错误求助
问题场景
尝试使用Terraform创建S3 Bucket,执行terraform apply时持续出现Access Denied错误,涉及ACL和Bucket Policy的资源操作。
Terraform代码
resource "aws_s3_bucket" "prod_media" { bucket = var.prod_media_bucket acl = "public-read" } resource "aws_s3_bucket_cors_configuration" "prod_media" { bucket = aws_s3_bucket.prod_media.id cors_rule { allowed_headers = ["*"] allowed_methods = ["GET", "HEAD"] allowed_origins = ["*"] expose_headers = ["ETag"] max_age_seconds = 3000 } } resource "aws_s3_bucket_acl" "prod_media" { bucket = aws_s3_bucket.prod_media.id acl = "public-read" } resource "aws_iam_user" "prod_media_bucket" { name = "prod-media-bucket" } resource "aws_s3_bucket_policy" "prod_media_bucket" { bucket = aws_s3_bucket.prod_media.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Principal = "*" Action = [ "s3:*", ] Effect = "Allow" Resource = [ "arn:aws:s3:::${var.prod_media_bucket}", "arn:aws:s3:::${var.prod_media_bucket}/*" ] }, { Sid = "PublicReadGetObject" Principal = "*" Action = [ "s3:GetObject", ] Effect = "Allow" Resource = [ "arn:aws:s3:::${var.prod_media_bucket}", "arn:aws:s3:::${var.prod_media_bucket}/*" ] }, ] }) } resource "aws_iam_user_policy" "prod_media_bucket" { user = aws_iam_user.prod_media_bucket.name policy = aws_s3_bucket_policy.prod_media_bucket.id } resource "aws_iam_access_key" "prod_media_bucket" { user = aws_iam_user.prod_media_bucket.name }
错误信息
╷ │ Error: error creating S3 bucket ACL for prod-media-8675309: AccessDenied: Access Denied │ status code: 403, request id: XNW2R0KWFYB3KB9R, host id: CuBMdZSaJJgu+0Rprzlptt7oRsjMxBNNHJPhFq98ROGC9l9BUmfmv5YxYZuxf/V3GJBoiGJKJkg= │ │ with aws_s3_bucket_acl.prod_media, │ on s3.tf line 18, in resource "aws_s3_bucket_acl" "prod_media": │ 18: resource "aws_s3_bucket_acl" "prod_media" { │ ╵ ╷ │ Error: Error putting S3 policy: AccessDenied: Access Denied │ status code: 403, request id: XNW60T7SQXW1Y4SV, host id: AyGS46L37yIcI4JwddrjHo4GRF7T9JrnfD8TGNdUhpO5uLOWBbgY3+c4opoQTFc2jRdHtXwkqO8= │ │ with aws_s3_bucket_policy.prod_media_bucket, │ on s3.tf line 28, in resource "aws_s3_bucket_policy" "prod_media_bucket": │ 28: resource "aws_s3_bucket_policy" "prod_media_bucket" { │
运行Terraform的账号权限
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "*", "Resource": "*" } ] }
错误原因及修复方案
1. 重复设置ACL引发冲突
在aws_s3_bucket资源中已经指定了acl = "public-read",又单独创建aws_s3_bucket_acl资源重复设置相同ACL,这会导致Terraform执行时对同一资源的权限进行重复操作,触发S3的权限校验异常。
修复:删除单独的aws_s3_bucket_acl资源块,保留aws_s3_bucket内的ACL设置即可。
2. IAM用户策略配置错误
aws_iam_user_policy资源的policy字段需要传入JSON格式的策略内容,但代码中直接引用了aws_s3_bucket_policy.prod_media_bucket.id(Bucket Policy的ID),这是完全错误的配置,会导致IAM资源配置失败,同时干扰Terraform整体执行流程。
修复:为IAM用户单独定义合法的JSON策略,示例如下:
resource "aws_iam_user_policy" "prod_media_bucket" { user = aws_iam_user.prod_media_bucket.name policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = "s3:*" Resource = [ "arn:aws:s3:::${var.prod_media_bucket}", "arn:aws:s3:::${var.prod_media_bucket}/*" ] } ] }) }
3. S3公共访问阻止限制
即使账号拥有管理员权限,如果AWS账号级别或桶级别开启了S3公共访问阻止(Block Public Access),设置public-read ACL和允许Principal = "*"的Bucket Policy会被直接拒绝,返回403错误。
修复:
- 登录AWS控制台,检查账号或目标桶的公共访问阻止设置,根据业务需求关闭对应选项;
- 或在Terraform中显式配置公共访问阻止规则(仅当确认需要公开访问时使用):
resource "aws_s3_bucket_public_access_block" "prod_media" { bucket = aws_s3_bucket.prod_media.id block_public_acls = false block_public_policy = false ignore_public_acls = false restrict_public_buckets = false }
4. Bucket Policy冗余且资源范围错误
现有Bucket Policy包含两条冗余语句,第一条允许所有S3操作给所有用户,权限过宽且完全覆盖第二条的GetObject权限;同时GetObject操作的Resource包含了桶本身的ARN,这是不必要的(GetObject仅作用于桶内对象)。
修复:精简Policy,保留必要权限并修正资源范围:
resource "aws_s3_bucket_policy" "prod_media_bucket" { bucket = aws_s3_bucket.prod_media.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Sid = "PublicReadGetObject" Principal = "*" Action = ["s3:GetObject"] Effect = "Allow" Resource = "arn:aws:s3:::${var.prod_media_bucket}/*" } ] }) }
总结修复步骤
- 删除重复的
aws_s3_bucket_acl资源; - 修正
aws_iam_user_policy的配置,使用合法的JSON策略内容; - 检查并调整S3公共访问阻止设置(按需关闭);
- 精简Bucket Policy,修正资源范围。
内容的提问来源于stack exchange,提问作者Mark

