When managing multiple AWS accounts, centralizing CloudFront access logs in a single S3 bucket becomes crucial for streamlined monitoring and analysis. The key challenge lies in properly configuring cross-account permissions while maintaining security best practices.
To enable CloudFront from Account A to write logs to an S3 bucket in Account B, you need to establish these permissions:
Account A (CloudFront) → IAM Trust → Account B (S3 Bucket)
1. Configure the Destination Bucket Policy
In Account B, apply this bucket policy to your logging bucket (replace placeholders):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontLogDelivery",
"Effect": "Allow",
"Principal": {
"Service": "delivery.logs.amazonaws.com"
},
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": "arn:aws:s3:::your-log-bucket-name/AWSLogs/account-A-id/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
2. Set CloudFront Distribution Logging
In Account A, configure your CloudFront distribution with the cross-account bucket ARN:
aws cloudfront update-distribution \
--id EDFDVBD6EXAMPLE \
--distribution-config file://dist-config.json
Where dist-config.json contains:
{
"CallerReference": "my-distribution",
"Logging": {
"Enabled": true,
"IncludeCookies": false,
"Bucket": "your-log-bucket-name.s3.amazonaws.com",
"Prefix": "AWSLogs/account-A-id/"
}
}
After implementation:
- Trigger requests to your CloudFront distribution
- Check S3 bucket for new log files (may take 5-60 mins)
- Verify ACLs on delivered logs show proper ownership
403 Access Denied Errors
If logs fail to deliver, check:
- Bucket policy syntax and ARN formats
- Account ID in the resource path matches CloudFront account
- No S3 bucket ACLs are blocking the delivery service
Log Delivery Latency
Remember CloudFront logs are delivered asynchronously. Implement S3 event notifications if you need real-time processing:
aws s3api put-bucket-notification-configuration \
--bucket your-log-bucket-name \
--notification-configuration file://notification.json
When managing multiple AWS accounts, consolidating CloudFront access logs into a centralized S3 bucket presents unique permission challenges. The standard documentation doesn't clearly address this cross-account scenario, particularly regarding bucket ACLs and IAM policies.
For this setup, you'll need:
- A destination S3 bucket in the centralized logging account
- IAM permissions to modify both the bucket policy and CloudFront distributions
- The canonical ID of the CloudFront service account for your region
First, retrieve the CloudFront canonical ID for your region (this example uses us-east-1):
aws cloudfront list-cloud-front-origin-access-identities \ --query 'CloudFrontOriginAccessIdentityList.Items[?Comment==OAI-for-S3].Id' \ --output text
Apply this bucket policy to your centralized logging bucket:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowCloudFrontLogDelivery", "Effect": "Allow", "Principal": { "Service": "delivery.logs.amazonaws.com" }, "Action": [ "s3:GetBucketAcl", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::your-central-log-bucket", "arn:aws:s3:::your-central-log-bucket/*" ], "Condition": { "StringEquals": { "aws:SourceAccount": [ "source-account-id-1", "source-account-id-2" ] }, "ArnLike": { "aws:SourceArn": [ "arn:aws:cloudfront::source-account-id-1:distribution/*", "arn:aws:cloudfront::source-account-id-2:distribution/*" ] } } } ] }
Configure each distribution to log to the central bucket:
aws cloudfront update-distribution \ --id YOUR_DISTRIBUTION_ID \ --distribution-config file://dist-config.json
Where dist-config.json contains:
{ "Logging": { "Enabled": true, "IncludeCookies": false, "Bucket": "your-central-log-bucket.s3.amazonaws.com", "Prefix": "cloudfront-logs/account-id-1/" } }
Common issues include:
- Missing PutObject permission for the log delivery service
- Incorrect bucket policy conditions
- Bucket encryption conflicts
Check CloudTrail logs for permission errors if logs don't appear within 24 hours of configuration.
For infrastructure-as-code users, here's a Terraform example:
resource "aws_s3_bucket_policy" "cloudfront_logs" { bucket = aws_s3_bucket.central_logs.id policy = data.aws_iam_policy_document.cloudfront_log_delivery.json } data "aws_iam_policy_document" "cloudfront_log_delivery" { statement { principals { type = "Service" identifiers = ["delivery.logs.amazonaws.com"] } actions = [ "s3:GetBucketAcl", "s3:PutObject" ] resources = [ aws_s3_bucket.central_logs.arn, "${aws_s3_bucket.central_logs.arn}/*" ] condition { test = "StringEquals" variable = "aws:SourceAccount" values = var.source_account_ids } } }