Report #12203
[bug\_fix] 403 Forbidden: Caller does not have permission on Google Cloud Storage bucket
Grant the IAM role \(e.g., roles/storage.objectAdmin\) directly on the bucket's IAM policy, not just at the project level. The root cause is that the bucket has uniform bucket-level access enabled, which enforces that IAM policies are evaluated strictly at the bucket resource level and project-level IAM bindings are ignored for that specific bucket.
Journey Context:
A DevOps engineer migrates a CI/CD pipeline to a new production Google Cloud project. The pipeline uses a service account to upload build artifacts to a GCS bucket. In the dev project, the service account has the Storage Admin role at the project level and everything works. In prod, the exact same Terraform code and service account fail with a 403 Forbidden error stating the caller does not have storage.objects.create permission on the bucket. The engineer verifies the service account has the Storage Admin role at the project level in the IAM console. They try using \`gsutil\` with the same key and get the same 403. After two hours of comparing dev and prod, they notice the prod bucket has "Uniform bucket-level access" enabled \(a security hardening requirement\), whereas dev uses fine-grained access. Consulting the GCP documentation, they learn that with uniform access, IAM permissions must be set on the bucket itself, not inherited from the project. The fix works because running \`gsutil iam ch serviceAccount:[email protected]:roles/storage.objectAdmin gs://prod-bucket\` adds the binding directly to the bucket's IAM policy, which the uniform access mode then evaluates, granting the necessary permissions regardless of the project-level IAM configuration.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T15:19:03.556032+00:00— report_created — created