S3 Bucket ACL AccessControlListNotSupported Error in Terraform
Problem Statement
When creating aws_s3_bucket_acl
resources in Terraform after April 2023, you may encounter this error:
"Error: error creating S3 bucket ACL for bucket-name: AccessControlListNotSupported: The bucket does not allow ACLs │ status code: 400"
This occurs due to AWS security changes implemented in April 2023 that disable Access Control Lists (ACLs) by default for new S3 buckets. AWS now recommends bucket policies instead of ACLs for access management.
Terraform's default configuration conflict with this change because:
aws_s3_bucket_acl
resources implicitly require ACLs to be enabled- New buckets default to
ObjectOwnership = "BucketOwnerEnforced"
(which blocks ACLs) - Terraform creates ACL resources before ownership controls by default
Recommended Solutions
Solution 1: Enable ACLs with Ownership Controls (Quick Fix)
Add ownership controls to enable ACL support before creating the ACL resource:
# Ownership controls MUST be created first
resource "aws_s3_bucket_ownership_controls" "example" {
bucket = aws_s3_bucket.my_bucket.id
rule {
# Required for ACL support
object_ownership = "BucketOwnerPreferred"
}
}
resource "aws_s3_bucket_acl" "example" {
bucket = aws_s3_bucket.my_bucket.id
acl = "private"
# Critical dependency
depends_on = [aws_s3_bucket_ownership_controls.example]
}
Key Notes:
- Use
ObjectWriter
orBucketOwnerPreferred
ownership types depends_on
is mandatory to enforce creation order- Works for existing Terraform configurations with minimal changes
Solution 2: Using the Official S3 Bucket Module (Best Practice)
For new implementations, use HashiCorp's maintained S3 module with built-in ACL handling:
module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
version = "~> 3.0"
bucket = "my-bucket"
# These enable ACL support automatically
control_object_ownership = true
object_ownership = "BucketOwnerPreferred"
# Standard ACL declaration
acl = "private"
}
Advantages of this approach:
- Automatically handles creation order dependencies
- Implements AWS best practices
- Provides additional security features
- Maintained by HashiCorp/AWS experts
Solution 3: Migrate to Bucket Policies (AWS Recommended)
Where possible, replace ACLs with bucket policies (AWS preferred method):
resource "aws_s3_bucket_policy" "example" {
bucket = aws_s3_bucket.my_bucket.id
policy = jsonencode({
Version : "2012-10-17",
Statement : [
{
Effect : "Allow",
Principal : "*",
Action : "s3:GetObject",
Resource : "arn:aws:s3:::my-bucket/*"
}
]
})
}
Why This Error Occurs
AWS made two significant changes:
- New buckets default to ACL-disabled mode (
BucketOwnerEnforced
) - ACLs are being phased out as a legacy authorization method
Terraform's resource creation order exacerbates the problem:
Best Practices Summary
For existing Terraform configurations
Use Solution 1 with explicit ownership controls and dependenciesFor new infrastructure
Use Solution 2 with the official S3 bucket moduleLong-term strategy
Migrate to bucket policies (Solution 3) per AWS security recommendations
AWS ACL Deprecation Note
Bucket ACLs are being phased out - avoid them for new development. Use bucket policies or S3 Access Points instead.
Troubleshooting Tips
- Always apply ownership controls before ACL resources
- Verify ownership settings in AWS Console:
S3 → Bucket → Permissions → Object Ownership
- Enable access logs to diagnose permission issues
- Use
terraform apply -replace
to force resource recreation if stuck in bad state
Verifying Configuration
Check your ownership settings with AWS CLI:
aws s3api get-bucket-ownership-controls \
--bucket YOUR_BUCKET_NAME \
--query "OwnershipControls.Rules[0].ObjectOwnership"
Should return BucketOwnerPreferred
or ObjectWriter