Search code examples
amazon-web-servicesamazon-s3terraform

Deleting S3 Bucket objects not working with Terraform Access Denied


I'm attempting to rename an S3 bucket using Terraform, which will automatically delete the existing bucket and recreate a new one. I'm getting the following error:

│ Error: emptying S3 Bucket (my-bucket): 
|   deleting S3 Bucket (my-vuxkwr) objects: 1000 errors occurred:
│ * deleting: S3 object (AWSLogs/123456789/elasticloadbalancing/us-west-2/2024/03/15/123456789_elasticloadbalancing_us-west-2_app.my-api.log.gz) version (abcde_fghij.klmno): 
| AccessDenied: Access Denied
# omitted the rest of the AccessDenied errors

Here is my S3 bucket configuration:

data "aws_caller_identity" "current" {}
data "aws_elb_service_account" "main" {}

locals {
  bucket_arn  = "arn:aws:s3:::${var.bucket_name}"
}

resource "aws_s3_bucket" "main" {
  bucket = var.bucket_name

  force_destroy = true
  
  tags = var.tags
}

resource "aws_s3_bucket_logging" "main" {
  bucket = aws_s3_bucket.main.id

  target_bucket = var.s3_access_logs_bucket_name
  target_prefix = "${var.bucket_name}/"
}

resource "aws_s3_bucket_versioning" "main" {
  bucket = aws_s3_bucket.main.id

  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "main" {
  bucket = aws_s3_bucket.main.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "AES256"
    }
  }
}

resource "aws_s3_bucket_policy" "main" {
  bucket = aws_s3_bucket.main.id
  policy = data.aws_iam_policy_document.bucket_policy.json

  depends_on = [
    aws_s3_bucket.main
  ]
}

data "aws_iam_policy_document" "bucket_policy" {
  statement {
    sid = "RequireSSL"

    effect = "Deny"

    principals {
      type        = "*"
      identifiers = ["*"]
    }

    actions = ["s3:*"]

    resources = [
      local.bucket_arn,
      "${local.bucket_arn}/*",
    ]

    condition {
      test     = "Bool"
      variable = "aws:SecureTransport"
      values = [
        "false"
      ]
    }
  }

  statement {
    sid = "AwsElbServiceAccount"
    actions = [
      "s3:PutObject"
    ]
    resources = [
      "${aws_s3_bucket.main.arn}/*"
    ]
    principals {
      type        = "AWS"
      identifiers = [data.aws_elb_service_account.main.arn]
    }
  }
}

resource "aws_s3_bucket_ownership_controls" "main" {
  bucket = aws_s3_bucket.main.id

  rule {
    object_ownership = "BucketOwnerEnforced"
  }

  depends_on = [
    aws_s3_bucket.main
  ]
}

resource "aws_s3_bucket_public_access_block" "main" {
  bucket = aws_s3_bucket.main.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

Here are the relevant permissions for my current role:

data "aws_iam_policy_document" "provision_s3_bucket" {
  statement {
    sid       = "S3Bucket"
    actions   = [
        "s3:DeleteObject", 
        "s3:DeleteObjects",
      ]
    resources = [
        "arn:aws:s3:::*",
      ]
  }
}

Why might I be getting an AccessDenied error when I have permission to delete bucket objects on any S3 resource?


Solution

  • Solved: Add permission for s3:DeleteObjectVersion (or just s3:DeleteObject*)

    I'd been referencing the AWS S3 API actions to determine which permissions I needed, and the only relevant DeleteObject actions were the two I'd previously added.

    Apparently, the API actions and permissions don't always have a 1:1 correlation since an action (like DeleteObject) will sometimes have side effects (like DeleteObjectVersion).

    From the AWS API DeleteObject Docs page:

    s3:DeleteObject - To delete an object from a bucket, you must always have the s3:DeleteObject permission.

    s3:DeleteObjectVersion - To delete a specific version of an object from a versioning-enabled bucket, you must have the s3:DeleteObjectVersion permission.