Fix AWS S3 403 AccessDenied Error Blocking CI/CD Pipeline Artifact Upload
Quick Fix Summary
TL;DRVerify and correct the IAM role attached to your CI/CD pipeline's compute resource (e.g., CodeBuild, EC2).
A 403 AccessDenied error during S3 upload indicates the IAM principal (user/role) executing the pipeline lacks the necessary permissions (s3:PutObject) on the target bucket/prefix, or a bucket policy/SCP is explicitly denying the action.
Diagnosis & Causes
Recovery Steps
Step 1: Verify the Failing Request Details
Check AWS CloudTrail logs for the specific AccessDenied event to identify the exact IAM principal, requested action (e.g., s3:PutObject), and resource (bucket/key).
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=PutObject --start-time $(date -u -v-1H +'%Y-%m-%dT%H:%M:%SZ') --query 'Events[?contains(CloudTrailEvent, `"errorCode":"AccessDenied"`)]' --output text --max-items 5 Step 2: Audit IAM Permissions on the Pipeline Role
Simulate the policy evaluation for the specific S3 action and resource using the IAM policy simulator. Replace ROLE_NAME and BUCKET_NAME.
aws iam simulate-principal-policy --policy-source-arn arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME --action-names s3:PutObject --resource-arns arn:aws:s3:::BUCKET_NAME/path/to/artifact* Step 3: Check for Explicit Denies in Bucket Policy
Retrieve and inspect the S3 bucket policy for any 'Deny' statements that may affect the pipeline role.
aws s3api get-bucket-policy --bucket BUCKET_NAME --query Policy --output text | python -m json.tool Step 4: Verify S3 Block Public Access & Bucket Ownership Controls
If using ACLs or uploading to a bucket owned by another account, ensure settings allow for cross-account or specific principal writes. Disable legacy ACLs if possible.
aws s3api get-public-access-block --bucket BUCKET_NAME
aws s3api get-bucket-ownership-controls --bucket BUCKET_NAME Step 5: Inspect VPC Endpoint Policies (if applicable)
If the pipeline accesses S3 via a VPC Endpoint (Gateway or Interface), its resource policy must allow the action.
aws ec2 describe-vpc-endpoints --vpc-endpoint-ids VPCE_ID --query 'VpcEndpoints[0].PolicyDocument' --output text | python -m json.tool Step 6: Apply Corrective IAM Policy
Attach a minimal, corrective policy to the CI/CD pipeline execution role. Replace placeholders.
cat > corrective_policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:PutObjectAcl"],
"Resource": [
"arn:aws:s3:::BUCKET_NAME",
"arn:aws:s3:::BUCKET_NAME/path/to/artifact/*"
]
}]
}
EOF
aws iam put-role-policy --role-name PIPELINE_ROLE_NAME --policy-name S3ArtifactUploadFix --policy-document file://corrective_policy.json Step 7: Test the Fix
Run a test upload using the AWS CLI assuming the pipeline role's permissions, or trigger a manual pipeline execution.
aws sts assume-role --role-arn arn:aws:iam::ACCOUNT_ID:role/PIPELINE_ROLE_NAME --role-session-name TestUpload
# Use the returned credentials to test:
AWS_ACCESS_KEY_ID=ASIA... AWS_SECRET_ACCESS_KEY=... AWS_SESSION_TOKEN=... aws s3 cp ./test.file s3://BUCKET_NAME/path/to/artifact/test.file Architect's Pro Tip
"This often happens after a bucket policy update intended to lock down security, which inadvertently adds a blanket 'Deny' without an explicit 'Allow' for your CI/CD service principal. Always test bucket policy changes in a non-production environment first."
Frequently Asked Questions
The IAM simulation shows 'allowed', but the pipeline still gets 403. Why?
An explicit 'Deny' from a Service Control Policy (SCP), S3 Bucket Policy, VPC Endpoint Policy, or session policy is overriding the IAM allow. Check hierarchies above the IAM role.
Can KMS encryption cause a 403 on S3 upload?
Yes. If the bucket uses SSE-KMS, the IAM role also needs kms:GenerateDataKey and kms:Decrypt permissions on the specific KMS key. This fails with a 403, not a KMS-specific error.