Skip to content

CloudFront log delivery to S3 works via Console but fails with CLI and Terraform (AccessDenied)

0

When I configure the logging manually using the AWS Management Console, everything works as expected. But when I try to do the same using the AWS CLI or Terraform, I get the following error:

An error occurred (AccessDenied) when calling the UpdateDistribution operation: You don't have permission to access the S3 bucket for CloudFront logs: <your-bucket-name>.s3.amazonaws.com If you're using IAM, you need s3:GetBucketAcl and s3:PutBucketAcl permissions to create a distribution or to update log settings for an existing distribution. In addition, the S3 ACL for the bucket must grant you FULL_CONTROL.

I'm using an IAM role with full admin permissions. And here’s the S3 bucket policy: { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowCloudFrontWriteLogs", "Effect": "Allow", "Principal": { "Service": "cloudfront.amazonaws.com" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::<bucket-name>/AWSLogs/<account-id>/*", "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } } } ] }

Terraform snippet for CloudFront logging: dynamic "logging_config" { for_each = var.cloudfront_logging_enabled ? [1] : []

content { bucket = "<bucket-name>.s3.amazonaws.com" include_cookies = var.cloudfront_logging_config.include_cookies prefix = var.cloudfront_logging_config.prefix } }

I’ve already verified that:

  • The bucket exists and the name matches the distribution config.
  • The bucket has Block Public Access enabled.
  • The IAM role has AdministratorAccess.
  • Logging works in the Console but fails via CLI/Terraform

Is there anything missing in the bucket policy or logging_config that the Console automatically handles? Do I need to explicitly set the ACL on the bucket to make programmatic updates work?

Thanks in advance for your help 🙏

4 Answers
0

The issue you're experiencing is related to how CloudFront handles permissions for log delivery to S3 buckets when configured programmatically versus through the console.

When you enable CloudFront logging through the console, CloudFront automatically updates the bucket's ACL to give the awslogsdelivery account FULL_CONTROL permission to write logs to your bucket. However, when using CLI or Terraform, you need to ensure that:

  1. Your IAM role has the necessary permissions to modify the bucket ACL (s3:GetBucketAcl and s3:PutBucketAcl).

  2. The S3 bucket must have ACLs enabled. If you're using the "Bucket owner enforced" setting for S3 Object Ownership (which disables ACLs), CloudFront cannot write logs to the bucket.

CloudFront uses a different permissions model than other AWS services that send logs to S3. While your bucket policy correctly grants the cloudfront.amazonaws.com service principal the s3:PutObject permission, CloudFront specifically requires ACL permissions rather than just bucket policy permissions.

To fix this issue:

  1. Make sure ACLs are enabled on your S3 bucket by checking the Object Ownership setting (it should be "Bucket owner preferred" or "Object writer" instead of "Bucket owner enforced").

  2. Ensure your IAM role has the s3:GetBucketAcl and s3:PutBucketAcl permissions.

  3. If you're using standard logging (v2), consider following the cross-account logging setup which uses the delivery.logs.amazonaws.com service principal instead.

The console handles these ACL modifications automatically, which is why it works there but fails when using CLI or Terraform.
Sources
Access control in Amazon S3 - Amazon Simple Storage Service
Configure standard logging (legacy) - Amazon CloudFront
Logs sent to Amazon S3 - Amazon CloudWatch Logs
Send CloudFront logs to an S3 bucket in another account | AWS re:Post

answered 6 months ago
  • I am not using cross-account logging.

    I am using an IAM role with full Administrator permissions, which includes s3:GetBucketAcl and s3:PutBucketAcl.

    The S3 bucket is configured with Object Ownership = Bucket owner preferred, so ACLs are enabled.

    Additionally, I have explicitly set the bucket ACL for CloudFront log delivery: resource "aws_s3_bucket_acl" "cloudfront_logs_acl" { bucket = aws_s3_bucket.cloudfront_logs.id acl = "log-delivery-write" }

    Despite all this, enabling CloudFront logging via CLI or Terraform fails with AccessDenied, while it works via the AWS Console.

    Could this behavior be caused by CloudFront requiring something additional beyond standard admin permissions and ACL settings when configured programmatically?

0

hi,

This is a complex scenario, while you have done configuration for both side terraform and s3 bucket policy right, as per the docs, I see.

as accessdiend is basically policies and configuration issues.

here in your situation access denied issue is

An error occurred (AccessDenied) when calling the UpdateDistribution operation: You don't have permission to access the S3 bucket for CloudFront logs: <your-bucket-name>.s3.amazonaws.com If you're using IAM, you need s3:GetBucketAcl and s3:PutBucketAcl permissions to create a distribution or to update log settings for an existing distribution. In addition, the S3 ACL for the bucket must grant you FULL_CONTROL.

as error says "You don't have permission to access the S3 bucket for CloudFront logs"

so, in my understanding S3 bucket policy is denying,

Your original bucket policy with the s3:x-amz-acl condition is technically correct, **but **if you can temporarily remove this condition, and rely on the ACLs and Object Ownership (which are necessary anyway) to enforce the delivery, thereby satisfying the API's less restrictive check during the update operation.

temporarily removing the condition and see if it works?

Best,

answered 6 months ago
  • Hi, Thanks for your reply! I tried removing the "s3:x-amz-acl" condition from the bucket policy, but unfortunately I’m still getting the same error.

    Just to clarify, this is my rest Terraform configuration for the S3 bucket that CloudFront should write logs to: resource "aws_s3_bucket" "cloudfront_logs" { bucket ="bucket-name"

    tags = { Name = "bucket-name" } }

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

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

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

    rule { object_ownership = "BucketOwnerPreferred" } }

    resource "aws_s3_bucket_acl" "cloudfront_logs_acl" { bucket = aws_s3_bucket.cloudfront_logs.id acl = "log-delivery-write"

    depends_on = [aws_s3_bucket_ownership_controls.cloudfront_logs] }

    resource "aws_s3_bucket_lifecycle_configuration" "cloudfront_logs" { bucket = aws_s3_bucket.cloudfront_logs.id rule { id = "log" status = "Enabled" filter { prefix = "/" } expiration { days = 1 } } }

    I’m using: Object ownership = BucketOwnerPreferred ACL = log-delivery-write Admin permissions on my IAM role (including s3:GetBucketAcl and s3:PutBucketAcl)

    what you think i can do?

0

Hi again,

As per my understanding from the docs

When using: object_ownership = "BucketOwnerPreferred"

Terraform applies the ACL log-delivery-write, which grants access to the S3 Log Delivery group, not to the CloudFront canonical user.

However, CloudFront requires permissions for its own canonical user ID, which is different from the “log-delivery-write” group.

[https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_log_delivery_canonical_user_id]()

OR

switching to BucketOwnerEnforced solves the issue without needing the CloudFront canonical user ID, since ACLs are disabled entirely. and remove the condition "s3:x-amz-acl": "bucket-owner-full-control" because ACLs are disabled in this mode.

This is my current understanding I faced a similar issue using CloudFormation last year

make sure the bucket name matches the actual S3 bucket name (not ARN)

Best

answered 6 months ago
  • Thanks for the detailed explanation Just to clarify a few things based on what I tested on my side: Using only the bucket name in the logging_config does not work, cloudFront returns this error unless I provide the ARN: Error: updating CloudFront Distribution: InvalidArgument: The S3 bucket that you specified for CloudFront logs does not enable ACL access: aws-cloudfront-logs-sandbox-digitalidf-il.s3.amazonaws.com When I switch to the ARN — the validation passes.

    I tried your first recommendation (BucketOwnerEnforced) CloudFront fails with the ACL-based error, because CloudFront still expects ACLs for log delivery: InvalidArgument: The S3 bucket that you specified for CloudFront logs does not enable ACL access So ACL-disabled mode is incompatible with CloudFront logging at this moment.

    I tried the approach using aws_canonical_user_id Still ends with: AccessDenied: You don't have permission to access the S3 bucket for CloudFront logs If you're using IAM, you need s3:GetBucketAcl and s3:PutBucketAcl permissions... and the S3 ACL for the bucket must grant you FULL_CONTROL. Which is the same main issue I’ve been stuck on.

    So: The bucket name alone is rejected — ARN is required. BucketOwnerEnforced mode fails because CloudFront needs ACL access. Granting canonical user permissions still leads to the same AccessDenied error.

    Let me know if you've seen a Terraform-specific workaround, or if there's something else I'm missing in the policy/ACL setup.

0

thanks for sharing your testing scenarios.

Here is a scenario I used with my existing CloudFront distribution, sending logs to a newly created S3 bucket using Terraform. I also included my CLI output for verification.

google docs created for this scenario: https://docs.google.com/document/d/e/2PACX-1vT81wh3algLi8vRWxzINZUGYIrMWiR-7UorjkcOooqsEUj4ehIrn0Hrso7UiTdG3sWvK3LOzo5Cq_eA/pub

Enter image description here
Enter image description here

After running the CLI linking command, I waited a few minutes for the logs to populate, and it worked. Output shows:

malini@Malini:~/projects/cloudfront-logging-demo$ aws cloudfront get-distribution --id EO9YJD19H7YWS --query 'Distribution.DistributionConfig.Logging'
{
    "Enabled": true,
    "IncludeCookies": false,
    "Bucket": "malini-cloudfront-logs-demo.s3.amazonaws.com",
    "Prefix": "cf-logs/"
}

Conclusion:-> CloudFront logging requires ACL-based delivery. Using BucketOwnerPreferred with ACLs enabled and granting CloudFront bucket-owner-full-control resolves AccessDenied errors. This setup works across any region

Best,

answered 6 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.