4

I'm trying to create an S3 bucket using Terraform, but keep getting Access Denied errors.

I have the following Terraform code:

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
}

Whenever I run terraform apply I get the following error:

╷
│ 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" {
│

The account running the Terraform has Administrator Access to all resources.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "*",
            "Resource": "*"
        }
    ]
}

Please help identify what is causing this error.

Mark
  • 1,455
  • 3
  • 28
  • 51

1 Answers1

11

There are few issues in your code:

  1. acl attribute of aws_s3_bucket is deprecated and shouldn't be used.
  2. You don't have aws_s3_bucket_ownership_controls
  3. You don't have aws_s3_bucket_public_access_block
  4. You are missing relevant depends_on
  5. aws_iam_user_policy can't use aws_s3_bucket_policy.prod_media_bucket.id (its not even clear what do you want to accomplish here, so I removed it from the code below).

The working code is:


resource "aws_s3_bucket" "prod_media" {
  bucket = var.prod_media_bucket
}

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"
    depends_on = [aws_s3_bucket_ownership_controls.s3_bucket_acl_ownership]
}

resource "aws_s3_bucket_ownership_controls" "s3_bucket_acl_ownership" {
  bucket = aws_s3_bucket.prod_media.id
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
  depends_on = [aws_s3_bucket_public_access_block.example]
}

resource "aws_iam_user" "prod_media_bucket" {
  name = "prod-media-bucket"
}

resource "aws_s3_bucket_public_access_block" "example" {
  bucket = aws_s3_bucket.prod_media.id

  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

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}/*"
        ]
      },
    ]
  })
  
  depends_on = [aws_s3_bucket_public_access_block.example]
}
Marcin
  • 215,873
  • 14
  • 235
  • 294
  • 3
    Thank you @Marcin, I have implemented other requirements but suggested `depends_on = [ aws_s3_bucket_public_access_block.example ]` saved me time. – Vab Jun 08 '23 at 11:42
  • 1
    Very clean and nice +1 – bucky Jul 14 '23 at 08:49