How to Fix: GCP Cloud Build Permission Denied

GCP403High severity

The Scenario

You’re pushing a commit to a repo on a Friday afternoon, triggering a Cloud Build trigger. The build starts, fetches the source, then immediately fails when trying to pull a private Docker image from Artifact Registry or push a build artifact to a Cloud Storage bucket. The step logs show a terse “Permission denied” with no further context.

Symptoms

Root Cause

Cloud Build triggers run under a Cloud Build service account (<project-number>@cloudbuild.gserviceaccount.com), not your user credentials. This service account lacks the necessary IAM roles (e.g., roles/artifactregistry.reader, roles/storage.objectAdmin) on the target resources. The error is not a network issue or misconfiguration—it’s purely an IAM permission gap.

Resolution (Step-by-Step)

  1. Identify the Cloud Build service account email:

    PROJECT_ID=$(gcloud config get-value project)
    PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
    echo "${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com"
  2. Grant the necessary role on the specific resource (e.g., Artifact Registry repository):

    # For pulling images from Artifact Registry
    gcloud artifacts repositories add-iam-policy-binding <repo-name> \
      --location=<region> \
      --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
      --role="roles/artifactregistry.reader"
    
    # For pushing to Cloud Storage
    gsutil iam ch serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com:objectAdmin \
      gs://<bucket-name>
  3. Alternatively, grant a project-level role (less granular, use with caution):

    gcloud projects add-iam-policy-binding $PROJECT_ID \
      --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
      --role="roles/artifactregistry.reader"
  4. Wait for IAM propagation (up to 60 seconds) and retrigger the build.

Why This Sometimes Doesn’t Work

If you’re using a custom service account specified in the Cloud Build trigger (under “Service account” in the trigger settings), the default @cloudbuild.gserviceaccount.com is not used. You must grant permissions to the custom service account instead. Check the trigger’s YAML or UI for serviceAccount field. Also, if the error involves a cross-project resource (e.g., pulling from an Artifact Registry in another project), you need to grant permissions in that project’s resource, not the build’s project.

Verification

Rerun the build and check the logs for the specific step that failed. If the step now succeeds, the fix worked. For a quick test, impersonate the service account locally:

gcloud builds submit --impersonate-service-account=${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com

Common Follow-up Questions

Prevent This in the Future

Proactively monitor Cloud Build failures for IAM-related errors using structured logging and alerting. Tools like Better Stack can ingest Cloud Build logs and trigger alerts when “Permission denied” appears, letting you catch permission gaps before they block deployments. Set up uptime monitoring and alerts with Better Stack