Deployment blocked by org policy with unnamed constraint: how to find it
For developers and platform engineers hitting a deployment failure where the platform says an organization policy blocked the change but does not name the constraint. This runbook gives you a fast decision path to identify the actual policy, confirm whether it is IAM, location, service, network, or image related, and apply the least risky fix.
TL;DR — If a deployment fails with a generic "blocked by organization policy" message and no constraint name, start by reproducing the exact API call with verbose output and then query effective org policies at the target project/folder/org. The most common root cause is a deny policy on location, public IP, service account usage, or allowed services/images; the fastest fix is usually to deploy into an allowed region or update the resource spec to comply, then verify with a dry run or no-op deploy. Reading time: ~6 min
The scenario
You push a routine Tuesday afternoon deploy to a cloud environment you have used for months. CI built cleanly, your IaC plan looked normal, but the apply or deploy step dies with a vague message like "request violated organization policy" and nothing else useful. The dashboard shows no obvious red banner, your service never rolls forward, and the only recent change was a new project, region, service account, or networking setting. You need to identify the exact policy without spending an hour clicking through every admin screen you do not control.
Symptoms
- Deployment or apply fails with a generic policy error, often one of:
ERROR: request is prohibited by organization's policy
PERMISSION_DENIED: The caller does not have permission
Operation denied by org policy. Constraint information is not available.
HTTP 403 Forbidden
rpc error: code = PermissionDenied desc = Request blocked by organization policy
- CI/CD exits non-zero after resource creation starts, commonly exit code
1from the deploy CLI or IaC wrapper. - Audit/activity logs show a denied write on the target resource, but the human-readable message is sparse.
- The same deploy works in one project or region and fails in another.
- A small config change flips behavior, especially:
- changing region/zone
- attaching a different service account
- enabling public ingress or public IP
- using a new container image registry or base image
- creating a load balancer, bucket, VM, or managed service with defaults
Likely causes
| Cause | How common | Quick check |
|---|---|---|
| Region/location constraint denies the selected region or multi-region | Very common | `gcloud resource-manager org-policies list --project "$PROJECT_ID" |
| Public IP / public ingress / external load balancer blocked by policy | Very common | `grep -RniE '0.0.0.0/0 |
Service account usage blocked (actAs, allowed SA list, key creation disabled) | Common | `gcloud projects get-iam-policy "$PROJECT_ID" --format=json |
| Service/API not allowed or not enabled in this org/project | Common | gcloud services list --enabled --project "$PROJECT_ID" |
| Allowed container registries/images or binary authorization policy blocks the image | Moderate | `grep -RniE 'image: |
| CMEK, storage, or network constraints require specific encryption/network attachment | Moderate | `grep -RniE 'kms |
| You are checking the wrong scope; the deny is inherited from folder or org, not project | Moderate | gcloud projects describe "$PROJECT_ID" --format='value(parent.type,parent.id)' |
Step-by-step diagnosis
-
Re-run the failing deploy with maximum request detail.
# Generic patterns; use the one matching your tool terraform apply -no-color 2>&1 | tee /tmp/deploy.log gcloud --verbosity=debug deploy ... 2>&1 | tee /tmp/deploy.log kubectl apply -f deploy.yaml -v=8 2>&1 | tee /tmp/deploy.logThis is your problem if you see
403,PERMISSION_DENIED,violated organization policy, orRequest blocked by organization policynear the final API call. If the error is404 API not enabledor401 unauthenticated, skip this runbook. -
Identify the exact resource and field being created when it fails.
grep -nEi 'POST |PATCH |create|insert|apply|permissiondenied|org policy|constraint|fieldViolations' /tmp/deploy.log | tail -50This is your problem if the failing request includes fields like
region,zone,serviceAccount,networkInterfaces[].accessConfigs,ingress,loadBalancer,kmsKey, orimage. Jump to the matching fix section. -
Check effective org policies at the project first.
gcloud resource-manager org-policies list --project "$PROJECT_ID"Look for policy names containing
location,vmExternalIpAccess,allowedPolicyMemberDomains,restrictNonCmekServices,disableServiceAccountKeyCreation,allowedIngress,allowedVpcPeering, or service-specific constraints. If you find a likely match, jump to that fix section. -
If the project list is sparse, check inheritance from folder or org.
PARENT_TYPE=$(gcloud projects describe "$PROJECT_ID" --format='value(parent.type)') PARENT_ID=$(gcloud projects describe "$PROJECT_ID" --format='value(parent.id)') echo "$PARENT_TYPE $PARENT_ID" gcloud resource-manager org-policies list --folder "$PARENT_ID" 2>/dev/null || true gcloud resource-manager org-policies list --organization "$ORG_ID" 2>/dev/null || trueThis is your problem if the project shows nothing useful but folder/org shows denies. Fix at the resource spec if possible; otherwise escalate to the policy owner with the exact inherited policy name.
-
Test the most common blockers directly from your config.
grep -RniE 'region:|location:|zone:|serviceAccount:|accessConfigs|0\.0\.0\.0/0|ingress|LoadBalancer|kms|image:' .Match findings to likely causes:
- region/zone set to a denied location → location fix
- external IP, public ingress, or
LoadBalancerservice → public access fix - custom service account → service account fix
- image from unapproved registry → image policy fix
- missing
kmsor private network fields where policy requires them → CMEK/network fix
-
Confirm whether the issue is API enablement versus policy.
gcloud services list --enabled --project "$PROJECT_ID" | sed 's/ */ /g' | head -50If the target service is absent and the deploy log mentions
API has not been usedorSERVICE_DISABLED, jump to the service/API fix. If the API is enabled but the call is still denied, continue. -
Inspect audit logs for the denied method.
gcloud logging read 'severity>=ERROR AND protoPayload.status.code=7 AND resource.labels.project_id="'"$PROJECT_ID"'"' --limit=20 --format=json | jq '.[].protoPayload | {methodName, resourceName, status, authorizationInfo}'This is your problem if
status.codeis7and themethodNamepoints to the exact resource type. Some providers omit the constraint name, but the denied method plus request fields is usually enough to map to the policy. -
If you still cannot map it, create the smallest compliant variant and compare.
⚠️ This can create billable resources or temporary exposure if you accidentally leave public networking enabled. Use a disposable project/environment and delete immediately after testing.
# Example strategy, not provider-specific: remove public IP, use default allowed region, use approved SA, add CMEK/private network.If the minimal private/internal deployment succeeds, the blocker is almost certainly one of location, public access, SA, image, or CMEK/network policy. Apply the corresponding fix to the real deployment.
Fixes
Region/location constraint denies the selected region or multi-region
Query the effective location policy, then change the deployment target.
gcloud resource-manager org-policies describe constraints/gcp.resourceLocations --project "$PROJECT_ID"
Typical output shape:
name: projects/123/policies/gcp.resourceLocations
spec:
rules:
- values:
allowedValues:
- in:us-locations
- us-central1
Update your config to an allowed region.
# Terraform example
sed -i.bak 's/europe-west1/us-central1/g' *.tf
terraform apply
# Kubernetes/Helm values example
region: us-central1
Verify it worked:
grep -RniE 'region|location' . && your-deploy-command
Public IP / public ingress / external load balancer blocked by policy
Remove external IPs, switch ingress to internal, or use a private load balancer pattern.
grep -RniE 'accessConfigs|LoadBalancer|ingress|0\.0\.0\.0/0|public' .
Concrete changes:
# VM/network config: remove accessConfigs to avoid external IP
networkInterfaces:
- subnetwork: projects/PROJECT/regions/us-central1/subnetworks/app-private
# no accessConfigs
# Kubernetes Service: avoid public LB
apiVersion: v1
kind: Service
spec:
type: ClusterIP
# App ingress setting
ingress: internal
Verify it worked:
your-deploy-command 2>&1 | tail -50
Service account usage blocked
There are two common cases: you cannot impersonate/attach the service account, or org policy forbids key creation and your pipeline still tries to create keys. Check IAM bindings:
gcloud iam service-accounts get-iam-policy "$SA_EMAIL" --format=json | jq .
gcloud projects get-iam-policy "$PROJECT_ID" --format=json | jq '.bindings[] | select(.role=="roles/iam.serviceAccountUser")'
If the deployer lacks roles/iam.serviceAccountUser on the target SA, grant it at the narrowest scope available:
gcloud iam service-accounts add-iam-policy-binding "$SA_EMAIL" --member="user:you@example.com" --role="roles/iam.serviceAccountUser"
If your pipeline creates JSON keys and org policy disables that, stop creating keys and use workload identity / OIDC federation instead. For CI, replace key-file auth with short-lived identity federation supported by your provider and runner. Verify it worked:
gcloud auth print-identity-token >/dev/null && your-deploy-command
Service/API not allowed or not enabled
If the API is simply disabled, enable it if your org permits it.
gcloud services enable compute.googleapis.com --project "$PROJECT_ID"
If enablement itself is denied, capture the exact service name and escalate to the org policy owner; this is usually controlled centrally. Typical failure shape:
ERROR: (gcloud.services.enable) PERMISSION_DENIED: Service usage is restricted by organization policy.
Verify it worked:
gcloud services list --enabled --project "$PROJECT_ID" | grep compute.googleapis.com
Allowed container registries/images or binary authorization blocks the image
Move the image to an approved registry or update the reference to an approved repository.
grep -RniE 'image:' .
Example change:
image: us-docker.pkg.dev/approved-project/platform/myapp:2026-08-05.1
If your org requires signed images, sign and attest the image using your existing signing flow before deploy; the exact command depends on your registry/signing stack, but the deploy must reference the signed digest, not a mutable tag, where policy enforces it. Verify it worked:
grep -Rni 'image:' . && your-deploy-command
CMEK, storage, or network constraints require specific encryption/network attachment
Add the required KMS key, subnet, or private networking fields explicitly instead of relying on provider defaults.
# Example resource fragment
kmsKeyName: projects/SECURITY-PROJECT/locations/us-central1/keyRings/app/cryptoKeys/runtime
network: projects/NET-PROJECT/global/networks/shared-vpc
subnetwork: projects/NET-PROJECT/regions/us-central1/subnetworks/app-private
If the policy requires private service access or no default network, remove any implicit default-network usage from templates. Verify it worked:
grep -RniE 'kmsKeyName|subnetwork|network' . && your-deploy-command
Wrong scope: deny inherited from folder or org
You cannot fix inheritance at the project if the parent denies it. Package the evidence and send a precise request to the policy owner. Use this bundle:
printf 'Project: %s\nParent: %s/%s\n' "$PROJECT_ID" "$PARENT_TYPE" "$PARENT_ID"
gcloud resource-manager org-policies list --project "$PROJECT_ID"
gcloud logging read 'severity>=ERROR AND protoPayload.status.code=7 AND resource.labels.project_id="'"$PROJECT_ID"'"' --limit=5 --format='value(protoPayload.methodName,protoPayload.resourceName,protoPayload.status.message)'
Ask for one of two actions only: add your region/service/account/image to the allowlist, or confirm the approved alternative you should use. Avoid asking for blanket exceptions. Verify it worked:
your-deploy-command --dry-run 2>&1 | tail -20
Prevention
- Add a preflight policy check in CI for region, public exposure, service account, image registry, and CMEK fields.
#!/usr/bin/env bash
set -euo pipefail
grep -RniE 'region: europe-|location: eu-|accessConfigs|type: LoadBalancer|0\.0\.0\.0/0' . && { echo 'Policy preflight failed'; exit 1; } || true
- Pin approved deployment defaults in code, not in human memory.
# values.yaml / shared module defaults
region: us-central1
ingress: internal
serviceAccount: deploy-runtime@approved-project.iam.gserviceaccount.com
imageRegistry: us-docker.pkg.dev/approved-project/platform
- Add a CI job that lists effective org policies for the target project and stores them as an artifact for each environment.
gcloud resource-manager org-policies list --project "$PROJECT_ID" > org-policies.txt
- Fail builds on mutable or unapproved image references.
grep -RniE 'image: .+:(latest|main)$' . && { echo 'Mutable image tag blocked'; exit 1; } || true
grep -RniE 'image: (?!us-docker\.pkg\.dev/approved-project/)' . && { echo 'Unapproved registry'; exit 1; } || true
- Emit denied-write alerts from audit logs so you see the blocked method immediately instead of reading raw deploy logs.
# Example filter to use in your logging/alerting system
severity>=ERROR AND protoPayload.status.code=7 AND ("org policy" OR "PermissionDenied")
- Keep one known-good minimal deployment template per environment. When a full deploy fails, diff against the baseline first instead of debugging every resource in the stack.
diff -ruN deploy/minimal/ deploy/current/
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI