GCP Cloud Storage: Fix 403 Forbidden Errors Caused by IAM Policy Misconfiguration in Production
Quick Fix Summary
TL;DRGrant the `roles/storage.objectViewer` role to the service account or user principal on the affected bucket.
A 403 Forbidden error indicates the authenticated principal (user or service account) lacks the necessary IAM permissions to perform the requested operation on the Cloud Storage resource.
Diagnosis & Causes
Recovery Steps
Step 1: Diagnose the Principal and Operation
Identify the exact service account or user and the API operation that is failing. Check Cloud Audit Logs in the project containing the bucket.
gcloud logging read 'resource.type=gcs_bucket AND protoPayload.status.code=7' --project=PROJECT_ID --limit=5 --format="table(protoPayload.authenticationInfo.principalEmail, protoPayload.methodName, resource.labels.bucket_name)" Step 2: Check Effective IAM Permissions on the Bucket
Use the Policy Troubleshooter to see if the principal has permission for the specific operation on the bucket. Replace placeholders.
gcloud policy-troubleshooter gcp storage.buckets.get --principal-email=SERVICE_ACCOUNT_EMAIL --bucket-name=BUCKET_NAME --permission=storage.buckets.get
gcloud policy-troubleshooter gcp storage.objects.get --principal-email=SERVICE_ACCOUNT_EMAIL --bucket-name=BUCKET_NAME --object-name=OBJECT_NAME --permission=storage.objects.get Step 3: List Current IAM Policy for the Bucket
Examine the IAM bindings directly on the affected bucket to confirm the principal's role.
gcloud storage buckets get-iam-policy gs://BUCKET_NAME --format=json Step 4: Grant Missing Permissions (Immediate Fix)
Bind the appropriate Storage role to the principal at the bucket level. Use `objectAdmin` for write/delete, `objectViewer` for read.
gcloud storage buckets add-iam-policy-binding gs://BUCKET_NAME --member='serviceAccount:SERVICE_ACCOUNT_EMAIL' --role='roles/storage.objectViewer' Step 5: Check for VPC Service Controls (VPC-SC)
If VPC-SC is enabled, the request might be blocked by a perimeter violation. Check the perimeter's access level and ingress/egress rules.
gcloud access-context-manager perimeters list --policy=POLICY_NAME --format="table(name,title,status.resources)" Step 6: Check Organization/Folder Policy Inheritance
A Deny policy at the organization or folder level can override bucket-level grants. List effective policies.
gcloud asset analyze-iam-policy --project=PROJECT_ID --full-resource-name=//storage.googleapis.com/projects/_/buckets/BUCKET_NAME --identity='serviceAccount:SERVICE_ACCOUNT_EMAIL' Step 7: Validate Service Account Impersonation & Credentials
Ensure the application is using the intended service account credentials and has not exceeded the default 1-hour lifetime for short-lived credentials.
# For a Compute Engine VM:
gcloud compute instances describe INSTANCE_NAME --zone=ZONE --format="value(serviceAccounts[].email)"
# For a GKE workload:
kubectl get pod POD_NAME -o jsonpath='{.spec.containers[*].env[?(@.name=="GOOGLE_APPLICATION_CREDENTIALS")]}' Architect's Pro Tip
"This often happens after migrating workloads or during CI/CD pipeline changes where the runtime service account differs from the one used in development. Always check the principal email in the audit log, not just your assumed identity."
Frequently Asked Questions
I granted the role but still get 403. What's wrong?
IAM policy changes can take up to 60 seconds to propagate. More likely, a Deny policy from a parent resource (Organization, Folder) or a VPC-SC perimeter is overriding the allow grant. Use the Policy Troubleshooter (Step 2) and check inheritance (Step 6).
Should I grant permissions at the Project or Bucket level?
For production security, follow the principle of least privilege. Grant permissions at the bucket level (`gs://my-bucket`) unless the principal legitimately needs access to all buckets in the project. Project-level grants (`roles/storage.admin` on the project) are overly permissive.
How do I troubleshoot 403s from a Google Cloud Function or Cloud Run service?
The process is identical. The key is identifying the default compute service account or the configured user-managed service account for the service. Use Step 1's audit log query, then run the Policy Troubleshooter for that principal.