amazon-web-servicesamazon-s3terraformterraform-provider-aws

Access Denied when creating S3 Bucket ACL & S3 Policy using Terraform


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.


Solution

  • 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]
    }