AWS S3 sync AccessDenied before CloudFront invalidation in CI
The upload half of an S3-plus-CloudFront deploy failed: "AccessDenied ... PutObject" during aws s3 sync means the CI role cannot write objects to the bucket. The invalidation never runs because the sync exits non-zero first.
What this error means
aws s3 sync fails partway with "upload failed: ... An error occurred (AccessDenied) when calling the PutObject operation: Access Denied", and the pipeline stops before the CloudFront invalidation step.
upload failed: dist/app.js to s3://my-site-bucket/app.js
An error occurred (AccessDenied) when calling the PutObject operation: Access DeniedCommon causes
The role lacks s3:PutObject on the bucket
The deploy role can list or read but not write, or the bucket policy denies the principal, so each upload is denied.
A bucket policy or SSE/KMS condition blocks the write
A policy requiring a specific encryption header or a KMS key the role cannot use denies PutObject even when the S3 action is allowed.
How to fix it
Grant write and object listing on the bucket
- Add s3:PutObject, s3:DeleteObject, and s3:ListBucket for the bucket and its objects.
- If SSE-KMS is enforced, grant kms:GenerateDataKey on the key and pass the sse flag.
- Re-run the sync, then the invalidation.
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:DeleteObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::my-site-bucket",
"arn:aws:s3:::my-site-bucket/*"
]
}Order sync before invalidate and fail fast
Only invalidate after a clean sync so you never purge a cache for content that did not upload.
aws s3 sync ./dist "s3://my-site-bucket" --delete
aws cloudfront create-invalidation --distribution-id "$DISTRIBUTION_ID" --paths "/*"How to prevent it
- Grant least-privilege write on exactly the deploy bucket.
- Match KMS and SSE requirements when the bucket enforces encryption.
- Run the invalidation only after a successful sync.