CRITICAL

Fix AWS S3 403 AccessDenied Error Blocking CI/CD Pipeline Artifact Upload

Quick Fix Summary

TL;DR

Verify 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

  • Insufficient IAM permissions on the pipeline execution role.
  • Restrictive S3 bucket policy or VPC Endpoint policy.
  • Denying Service Control Policy (SCP) in the AWS Organization.
  • Recovery Steps

    1

    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).

    bash
    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
    2

    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.

    bash
    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*
    3

    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.

    bash
    aws s3api get-bucket-policy --bucket BUCKET_NAME --query Policy --output text | python -m json.tool
    4

    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.

    bash
    aws s3api get-public-access-block --bucket BUCKET_NAME
    aws s3api get-bucket-ownership-controls --bucket BUCKET_NAME
    5

    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.

    bash
    aws ec2 describe-vpc-endpoints --vpc-endpoint-ids VPCE_ID --query 'VpcEndpoints[0].PolicyDocument' --output text | python -m json.tool
    6

    Step 6: Apply Corrective IAM Policy

    Attach a minimal, corrective policy to the CI/CD pipeline execution role. Replace placeholders.

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

    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.

    bash
    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.

    Related AWS Guides