GCP for DevOps — Organization Policies
Organization Policies
Organization policies are guardrails that restrict what actions can be performed across your GCP organization. While IAM controls who can do something, organization policies control what can be done — regardless of IAM permissions.
IAM vs Organization Policies
┌─────────────────────────────────────────────────────────┐
│ │
│ IAM: "Alice CAN create VMs in project-prod" │
│ │
│ Org Policy: "Nobody CAN create VMs in us-west2 │
│ regardless of IAM permissions" │
│ │
│ Result: Alice can create VMs, but NOT in us-west2 │
│ │
└─────────────────────────────────────────────────────────┘
| Feature | IAM | Organization Policies |
|---|---|---|
| Controls | Who can do what | What can be done |
| Applied to | Members (users, SAs) | Resources (org, folder, project) |
| Inheritance | Down the hierarchy | Down the hierarchy |
| Override | Cannot remove inherited allows | Can override at lower levels |
| Example | Grant roles/compute.admin |
Restrict VM locations to us-central1 |
Constraint Types
| Type | Description | Example |
|---|---|---|
| List constraint | Allow or deny specific values | Allowed VM locations |
| Boolean constraint | Enable or disable a behavior | Disable SA key creation |
Commonly Used Organization Policies for DevOps
| Constraint | ID | Type | Description |
|---|---|---|---|
| Restrict resource locations | gcp.resourceLocations |
List | Limit where resources can be created |
| Disable SA key creation | iam.disableServiceAccountKeyCreation |
Boolean | Prevent creating SA keys |
| Disable SA key upload | iam.disableServiceAccountKeyUpload |
Boolean | Prevent uploading SA keys |
| Require OS Login | compute.requireOsLogin |
Boolean | Enforce OS Login for SSH |
| Restrict VM external IPs | compute.vmExternalIpAccess |
List | Control which VMs get public IPs |
| Restrict Cloud SQL public IPs | sql.restrictPublicIp |
Boolean | Prevent public Cloud SQL instances |
| Restrict VPC peering | compute.restrictVpcPeering |
List | Control VPC peering targets |
| Uniform bucket-level access | storage.uniformBucketLevelAccess |
Boolean | Enforce uniform access on buckets |
Viewing Organization Policies
# List all available constraints
$ gcloud org-policies list-available-constraints \
--organization=123456789 \
--format="table(constraint,listPolicy,booleanPolicy)"
CONSTRAINT LIST_POLICY BOOLEAN_POLICY
gcp.resourceLocations True False
iam.disableServiceAccountKeyCreation False True
compute.requireOsLogin False True
compute.vmExternalIpAccess True False
...
# View the effective policy on a project
$ gcloud org-policies describe gcp.resourceLocations \
--project=my-project --effective
constraint: constraints/gcp.resourceLocations
listPolicy:
allowedValues:
- in:us-central1-locations
- in:us-east1-locations
Setting Organization Policies
# Restrict resource locations to US regions only
$ gcloud org-policies set-policy policy.yaml --organization=123456789
# policy.yaml:
constraint: constraints/gcp.resourceLocations
listPolicy:
allowedValues:
- in:us-locations
# Disable service account key creation (boolean)
$ gcloud org-policies set-policy disable-sa-keys.yaml --organization=123456789
# disable-sa-keys.yaml:
constraint: constraints/iam.disableServiceAccountKeyCreation
booleanPolicy:
enforced: true
# Require OS Login for all VMs
$ gcloud org-policies set-policy require-oslogin.yaml --organization=123456789
constraint: constraints/compute.requireOsLogin
booleanPolicy:
enforced: true
Overriding at Lower Levels
Organization policies can be overridden at the folder or project level — unless the parent policy is set with inheritFromParent: false.
# Override at project level — allow a specific region
$ gcloud org-policies set-policy override.yaml --project=dev-sandbox
# override.yaml:
constraint: constraints/gcp.resourceLocations
listPolicy:
allowedValues:
- in:us-central1-locations
- in:europe-west1-locations
inheritFromParent: true
Restrict VM External IPs
A common DevOps policy — prevent VMs from getting public IPs (force use of Cloud NAT or IAP).
# Deny all external IPs on VMs
$ gcloud org-policies set-policy no-external-ip.yaml --project=prod-project
constraint: constraints/compute.vmExternalIpAccess
listPolicy:
allValues: DENY
# Allow external IPs only for specific VMs
$ gcloud org-policies set-policy limited-external-ip.yaml --project=prod-project
constraint: constraints/compute.vmExternalIpAccess
listPolicy:
allowedValues:
- projects/prod-project/zones/us-central1-a/instances/bastion-host
Custom Organization Policy Constraints
You can create custom constraints for services that support them.
# Create a custom constraint
$ gcloud org-policies set-custom-constraint custom-constraint.yaml
name: organizations/123456789/customConstraints/custom.disableLargeVMs
resourceTypes:
- compute.googleapis.com/Instance
methodTypes:
- CREATE
- UPDATE
condition: "resource.machineType.contains('n2-highmem-128')"
actionType: DENY
displayName: "Deny very large VMs"
description: "Prevents creation of n2-highmem-128 instances"
Dry Run Mode
Test a policy before enforcing it:
# Set a dry-run policy (audit only, does not block)
$ gcloud org-policies set-policy dry-run.yaml --project=my-project
constraint: constraints/gcp.resourceLocations
listPolicy:
allowedValues:
- in:us-central1-locations
dryRunSpec: true
# View dry-run audit results in Cloud Logging
$ gcloud logging read 'resource.type="audited_resource" AND protoPayload.metadata.dryRunViolation=true' \
--project=my-project --limit=10
Policy Hierarchy Example
Organization: example.com
├── Policy: gcp.resourceLocations = [us-locations]
├── Policy: iam.disableServiceAccountKeyCreation = true
│
├── Folder: Production
│ ├── Inherits: gcp.resourceLocations = [us-locations] ✅
│ ├── Inherits: disableServiceAccountKeyCreation = true ✅
│ │
│ └── Project: prod-web
│ └── Cannot override parent policies (inherit)
│
└── Folder: Development
├── Override: gcp.resourceLocations += [europe-west1]
├── Inherits: disableServiceAccountKeyCreation = true ✅
│
└── Project: dev-sandbox
└── Override: disableServiceAccountKeyCreation = false
(only if parent allows override)
Common Mistakes
| Mistake | Why It’s Wrong | Fix |
|---|---|---|
| Setting org policy without dry-run | May break existing workloads | Use dry-run first |
| Restricting locations after resources exist | Existing resources are not affected | Audit existing resources separately |
| Not documenting policy exceptions | Hard to track why overrides exist | Add descriptions to policies |
Forgetting inheritFromParent |
Child policies may not include parent values | Set explicitly |
| Setting policies at org level too strictly | Blocks all projects including dev | Use folders to scope policies |
Quick Reference
| Command | Description |
|---|---|
gcloud org-policies list-available-constraints --organization=ORG |
List all constraints |
gcloud org-policies describe CONSTRAINT --project=PROJ --effective |
View effective policy |
gcloud org-policies set-policy FILE --organization=ORG |
Set org-level policy |
gcloud org-policies set-policy FILE --project=PROJ |
Override at project level |
gcloud org-policies reset CONSTRAINT --project=PROJ |
Reset to inherited policy |
gcloud org-policies set-custom-constraint FILE |
Create custom constraint |
Next → Practice Exercises