Skip to content

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.disableServiceAccountCreation

2. 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: deny

3. 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_ID

Policy 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_ID

Custom 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 correctly

Resolving 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 --recursive

Best 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-patternProblemSolution
Overly broad policiesBlocks legitimate useUse conditions, exceptions
Too many exceptionsPolicy becomes uselessRevisit who needs exceptions
No dry-run testingBreaks production workflowsTest before enforcement
No documentationTeam doesn't understand policiesDocument purpose, exceptions
Policies not reviewedOutdated constraintsSchedule quarterly review

References