Organization Policies: Constraint Enforcement & Compliance
Organization Policies Overview
Organization Policies enforce constraints on resources: "Developer cannot delete VMs", "Storage must be encrypted"
Organization Policy:
"Developer cannot delete Compute Engine instances"
↓
Applied to: Org → Folder → Project
↓
Enforced: No developer can delete instances (override: specific service accounts)Constraint Types
1. Managed Constraints
Pre-built by Google, ready to use:
Common managed constraints:
- compute.skipDefaultNetworkCreation
- compute.requireOsLogin
- compute.requireShieldedVm
- storage.uniformBucketLevelAccess
- iam.disableServiceAccountCreation2. Custom Constraints
User-defined using CEL (Common Expression Language):
yaml
# Example: Require labels on all resources
constraint:
name: "require-resource-labels"
display_name: "Require resource labels"
description: "All resources must have 'environment' and 'team' labels"
condition: |
resource.labels.environment != null
AND resource.labels.team != null
action_type: deny3. Legacy Constraints (Deprecated)
Old format, being phased out. Use managed/custom instead.
Setting up Organization Policies
bash
# 1. List available managed constraints
gcloud resource-manager org-policies list-constraints
# 2. Get constraint details
gcloud resource-manager org-policies describe \
constraints/compute.requireOsLogin
# 3. Create policy (enforce constraint)
gcloud resource-manager org-policies create \
--constraint=constraints/compute.requireOsLogin \
--enforce \
--location=projects/PROJECT_ID
# 4. Set exceptions (allow specific service accounts)
gcloud resource-manager org-policies create \
--constraint=constraints/compute.requireOsLogin \
--enforce \
--exemptions=serviceAccount:sa@project.iam.gserviceaccount.com \
--location=projects/PROJECT_IDPolicy Inheritance
Organization
├── Policy: "Require OS Login"
│ ├── Inherited by all folders
│ └── Inherited by all projects
│
├── Folder: "Production"
│ ├── Inherits: "Require OS Login"
│ ├── Policy: "Require CMEK"
│ │ ├── Inherited by child projects
│ │ └── Exception: backup-sa@...
│ │
│ └── Project: "prod-api"
│ ├── Inherits: "Require OS Login"
│ ├── Inherits: "Require CMEK"
│ └── Policy: "Disable SSH keys"Conflict Resolution
What if policies conflict?
Organization: "Enforce CMEK" (must be true)
Folder: Override "Allow unencrypted" (must be false)
Result: CMEK is enforced
(cannot relax inherited policies at lower levels)Conditional Policies with Tags
yaml
# Enforce CMEK only for production resources
policy:
name: "encrypt-production"
constraint: "compute.cmekResources"
rules:
- enforce: true
condition:
expression: "resource.matchTag('environment', 'production')"
description: "CMEK required for production"
- enforce: false
condition:
expression: "resource.matchTag('environment', 'dev')"
description: "Dev can use default encryption"Dry-Run Mode
Test policies before enforcing:
bash
# Create policy in dry-run mode
gcloud resource-manager org-policies create \
--constraint=constraints/compute.requireOsLogin \
--dry-run \
--location=projects/PROJECT_ID
# Monitor audit logs to see what would be blocked
gcloud logging read \
'protoPayload.status.code=7' \ # Permission denied
--format=json
# Once satisfied, move to enforcement
gcloud resource-manager org-policies update \
--constraint=constraints/compute.requireOsLogin \
--enforce \
--location=projects/PROJECT_IDCustom Constraints with CEL
python
from google.cloud import resource_manager_v3
def create_custom_constraint():
"""Create custom constraint: Require labels"""
client = resource_manager_v3.OrgPolicyClient()
# Define constraint with CEL expression
constraint = resource_manager_v3.CustomConstraint(
display_name="Require environment label",
description="All resources must have environment label",
condition="resource.labels.environment != null",
action_type=resource_manager_v3.CustomConstraint.ActionType.DENY,
resource_types=["compute.googleapis.com/Instance"]
)
# Create constraint
operation = client.create_custom_constraint(
parent=f"organizations/ORG_ID",
custom_constraint=constraint
)
return operation.result()CEL Examples
yaml
# Example 1: Require specific machine type
resource.type == "compute.googleapis.com/Instance"
AND resource.machineType NOT IN ["n1-standard-1", "n1-standard-2"]
# Example 2: Require labels
resource.labels.environment == null OR resource.labels.team == null
# Example 3: Restrict by zone
resource.zone NOT IN ["us-central1-a", "us-central1-b"]
# Example 4: Enforce encryption
resource.encryptionKey == null
# Example 5: Complex condition with tags
resource.matchTag("environment", "production")
AND resource.labels.encryption != "cmek"Terraform Organization Policies
hcl
# Enforce policy via Terraform
resource "google_org_policy_policy" "require_os_login" {
name = "organizations/ORG_ID/policies/compute.requireOsLogin"
parent = "organizations/ORG_ID"
rules {
enforce = true
}
}
# Policy with exceptions
resource "google_org_policy_policy" "require_cmek" {
name = "projects/PROJECT_ID/policies/compute.cmekResources"
parent = "projects/PROJECT_ID"
rules {
enforce = true
# Exception for service account
condition {
expression = "resource.matchTag('backup', 'true')"
}
}
}
# Custom constraint
resource "google_org_policy_custom_constraint" "require_labels" {
name = "organizations/ORG_ID/customConstraints/require.labels"
display_name = "Require labels"
description = "All resources must have labels"
action_type = "DENY"
condition = "resource.labels.environment != null"
resource_types = ["compute.googleapis.com/Instance"]
}Troubleshooting
Policy not enforcing
bash
# Check if policy is actually applied
gcloud resource-manager org-policies list \
--location=projects/PROJECT_ID
# Check audit logs
gcloud logging read \
'protoPayload.resourceName="projects/PROJECT_ID"' \
--limit=50 \
--format=json
# Test by attempting blocked action
# If blocked → Policy is enforced
# If allowed → Policy may not be configured correctlyResolving policy conflicts
bash
# If multiple policies apply, most restrictive wins
# Example:
Organization: Allow
Folder: Deny
Project: Allow
Result: Deny (most restrictive)
# Solution: Check hierarchy for conflicting policies
gcloud resource-manager org-policies list --recursiveBest Practices
yaml
1. Start with dry-run
- Understand impact before enforcement
- Monitor logs for what would be blocked
2. Use tags for conditional policies
- Apply different rules per environment
- Cleaner than multiple policies
3. Document exceptions
- Why is this service account exempt?
- Keep audit trail
4. Review regularly
- Policies may become outdated
- Technology changes (e.g., new encryption methods)
5. Test recovery procedures
- Can you temporarily disable policy if needed?
- What's the approval process?Anti-Patterns
| Anti-pattern | Problem | Solution |
|---|---|---|
| Overly broad policies | Blocks legitimate use | Use conditions, exceptions |
| Too many exceptions | Policy becomes useless | Revisit who needs exceptions |
| No dry-run testing | Breaks production workflows | Test before enforcement |
| No documentation | Team doesn't understand policies | Document purpose, exceptions |
| Policies not reviewed | Outdated constraints | Schedule quarterly review |