Why preventive controls come first
Detection tools find a public bucket or a leaked service account key after it exists. Google Cloud Organization Policy constraints can refuse the action instead. A policy set at the organization or a folder is inherited by every project below it, so one change covers many projects. This guide covers four constraints worth checking in any GCP organization, and how to roll them out without breaking pipelines. Nothing here was tested in a live account; validate in a non-production folder first.
What new organizations get by default
Google’s secure-by-default page states that organizations created on or after 3 May 2024 have seven constraints enforced by default, including iam.managed.disableServiceAccountKeyCreation, iam.disableServiceAccountKeyUpload, iam.allowedPolicyMemberDomains and storage.uniformBucketLevelAccess. It adds that some organizations created between February and April 2024 may also have them.
Two consequences follow. Organizations created before that date may have none of these defaults. And storage.publicAccessPrevention is not in the default list we read, so even a new organization does not block public buckets unless you enforce it. Do not assume; check.
The constraints
| Constraint | Type | Effect (per Google’s constraint reference) |
|---|---|---|
iam.managed.disableServiceAccountKeyCreation (legacy: iam.disableServiceAccountKeyCreation) | Boolean | Blocks creation of service account keys and Cloud Storage HMAC keys |
iam.managed.disableServiceAccountKeyUpload (legacy: iam.disableServiceAccountKeyUpload) | Boolean | Blocks uploading public keys to service accounts |
storage.publicAccessPrevention | Boolean | Blocks ACLs and IAM bindings that grant access to allUsers and allAuthenticatedUsers; it overrides existing public permissions |
iam.allowedPolicyMemberDomains | List | Limits which organizations or Workspace customer IDs can appear in IAM policies |
Key creation enforcement is not retroactive, so existing keys keep working. A blocked attempt returns FAILED_PRECONDITION: Key creation is not allowed on this service account.
Audit what is in effect
Check the effective policy on the organization and on a sample project:
gcloud org-policies describe iam.managed.disableServiceAccountKeyCreation \
--effective --organization=ORGANIZATION_ID
gcloud org-policies describe storage.publicAccessPrevention \
--effective --project=PROJECT_ID
A policy that is enforced shows enforce: true under spec.rules. Then list the user-managed keys that already exist, since the constraint will not remove them:
gcloud iam service-accounts keys list \
--iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com \
--managed-by=user \
--created-before=2026-05-05T00:00:00Z # example date; use your own cutoff
Loop that over your service accounts and review anything old. For each key, find the workload that uses it and move it to Workload Identity Federation or short-lived credentials, both of which Google lists as alternatives.
Set the policy, dry run first
Policy file for a boolean constraint, following the format in Google’s documentation:
name: organizations/ORGANIZATION_ID/policies/storage.publicAccessPrevention
spec:
rules:
- enforce: true
Apply it with gcloud org-policies set-policy policy.yaml. The caller needs roles/orgpolicy.policyAdmin, and Google notes changes can take up to 15 minutes to take full effect.
Google also supports a dry-run mode, where violations are logged but not blocked. Add a dryRunSpec block to the file and apply it with --update-mask. One discrepancy: the usage guide shows --update-mask=dryRunSpec, while the set-policy command reference lists policy.dry_run_spec as the accepted value. Use the one your installed gcloud accepts, and check the help output. After a clean dry-run period, enforce with --update-mask=spec. Google’s guide also describes gcloud policy-intelligence simulate orgpolicy for previewing impact; we did not run it.
For a single bucket, public access prevention can be set directly:
gcloud storage buckets update gs://BUCKET_NAME --public-access-prevention
gcloud storage buckets describe gs://BUCKET_NAME \
--format="default(public_access_prevention)"
Google’s page says a bucket set to enforced keeps the protection even if a higher-level policy is later switched off. Our assessment: set it directly on buckets holding sensitive data as a second layer.
Rollout notes
- Enforce at a test folder first. Key creation blocks will break any deployment pipeline that still mints keys.
- Plan exceptions deliberately. Google documents tag conditions for service-account constraints generally and excludes only
iam.disableCrossProjectServiceAccountUsage, but it does not say specifically that they apply to the managed key-creation constraint. Test tag conditions before relying on them, or use a dedicated folder with its own policy. - Where keys are unavoidable, Google documents
iam.serviceAccountKeyExpiryHours; it warns that expiry can cause outages, so review it before enabling. - Alert on
SetOrgPolicychanges in Cloud Audit Logs (our assessment: an attacker with policy admin rights would loosen these first; we did not verify the exact log method name).
Checklist
- Run the effective-policy commands above on the organization and every top-level folder.
- Enforce key creation and key upload blocks; migrate remaining keys.
- Enforce
storage.publicAccessPreventionafter a dry run. - Limit
roles/orgpolicy.policyAdminto a small group.
References
- Google Cloud: Organization policy constraints
- Google Cloud: Secure by default organization resources
- Google Cloud: Restricting service account usage
- Google Cloud: Using constraints
- Google Cloud Storage: Use public access prevention
- gcloud org-policies set-policy
- gcloud iam service-accounts keys list