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