cloud security

Implementing GCP Organization Policy Constraints

Implement GCP Organization Policy constraints to enforce security guardrails across the entire resource hierarchy, restricting risky configurations and ensuring compliance at organization, folder, and project levels.

cloud-securitycomplianceconstraintsgcpgovernanceorganization-policyresource-manager
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

The GCP Organization Policy Service provides centralized and programmatic control over cloud resources. Organization policies configure constraints that restrict one or more Google Cloud services, enforced at organization, folder, or project levels. They improve security by blocking external IPs, requiring encryption, and minimizing unauthorized access. Changes can take up to 15 minutes to propagate.

When to Use

  • When deploying or configuring implementing gcp organization policy constraints capabilities in your environment
  • When establishing security controls aligned to compliance requirements
  • When building or improving security architecture for this domain
  • When conducting security assessments that require this implementation

Prerequisites

  • GCP Organization with Organization Administrator role
  • gcloud CLI configured and authenticated
  • Terraform or gcloud for policy management
  • Organization Policy Administrator IAM role (roles/orgpolicy.policyAdmin)

Core Concepts

Constraint Types

  1. List Constraints: Allow or deny specific values (e.g., allowed regions)
  2. Boolean Constraints: Enable or disable a capability (e.g., disable serial port access)
  3. Custom Constraints: User-defined rules targeting specific resource fields (Preview)

Policy Inheritance

Policies inherit from the lowest ancestor with an enforced policy. If no ancestor has a policy, Google's managed default behavior applies.

Essential Security Constraints

Restrict VM External IP Addresses

# Deny external IP addresses on all VMs
gcloud resource-manager org-policies set-policy \
  --organization=ORGANIZATION_ID \
  policy.yaml

policy.yaml:

constraint: constraints/compute.vmExternalIpAccess
listPolicy:
  allValues: DENY

Restrict Resource Locations

gcloud org-policies set-policy \
  --organization=ORGANIZATION_ID \
  location-policy.yaml

location-policy.yaml:

constraint: constraints/gcp.resourceLocations
listPolicy:
  allowedValues:
    - "in:us-locations"
    - "in:eu-locations"

Disable Default Service Account Creation

constraint: constraints/iam.automaticIamGrantsForDefaultServiceAccounts
booleanPolicy:
  enforced: true

Require OS Login for SSH

constraint: constraints/compute.requireOsLogin
booleanPolicy:
  enforced: true

Disable Serial Port Access

constraint: constraints/compute.disableSerialPortAccess
booleanPolicy:
  enforced: true

Enforce Uniform Bucket-Level Access

constraint: constraints/storage.uniformBucketLevelAccess
booleanPolicy:
  enforced: true

Restrict Public IP on Cloud SQL

constraint: constraints/sql.restrictPublicIp
booleanPolicy:
  enforced: true

Disable Service Account Key Creation

constraint: constraints/iam.disableServiceAccountKeyCreation
booleanPolicy:
  enforced: true

Terraform Implementation

resource "google_organization_policy" "restrict_vm_external_ip" {
  org_id     = var.org_id
  constraint = "constraints/compute.vmExternalIpAccess"
 
  list_policy {
    deny {
      all = true
    }
  }
}
 
resource "google_organization_policy" "restrict_locations" {
  org_id     = var.org_id
  constraint = "constraints/gcp.resourceLocations"
 
  list_policy {
    allow {
      values = ["in:us-locations", "in:eu-locations"]
    }
  }
}
 
resource "google_organization_policy" "require_os_login" {
  org_id     = var.org_id
  constraint = "constraints/compute.requireOsLogin"
 
  boolean_policy {
    enforced = true
  }
}
 
resource "google_folder_organization_policy" "dev_folder_external_ip" {
  folder     = google_folder.dev.name
  constraint = "constraints/compute.vmExternalIpAccess"
 
  list_policy {
    allow {
      values = ["projects/dev-project/zones/us-central1-a/instances/bastion-host"]
    }
  }
}

Dry-Run Testing

Use Policy Intelligence tools to test changes before enforcement:

# Create a dry-run policy to monitor impact
gcloud org-policies set-policy \
  --organization=ORGANIZATION_ID \
  dry-run-policy.yaml

dry-run-policy.yaml:

constraint: constraints/compute.vmExternalIpAccess
listPolicy:
  allValues: DENY
dryRunSpec: true
# Check violations against dry-run policy
gcloud org-policies list-custom-constraints \
  --organization=ORGANIZATION_ID

Custom Constraints

# custom-constraint.yaml
name: organizations/ORGANIZATION_ID/customConstraints/custom.disableGKEAutoUpgrade
resourceTypes:
  - container.googleapis.com/NodePool
methodTypes:
  - CREATE
  - UPDATE
condition: "resource.management.autoUpgrade == true"
actionType: DENY
displayName: Deny GKE auto-upgrade on node pools
description: Prevents enabling auto-upgrade on GKE node pools for controlled upgrades
gcloud org-policies set-custom-constraint custom-constraint.yaml

Monitoring and Compliance

List active policies

gcloud org-policies list --organization=ORGANIZATION_ID

Describe a specific policy

gcloud org-policies describe constraints/compute.vmExternalIpAccess \
  --organization=ORGANIZATION_ID

Audit policy violations with Cloud Asset Inventory

gcloud asset search-all-resources \
  --scope=organizations/ORGANIZATION_ID \
  --query="policy:constraints/compute.vmExternalIpAccess"

References

Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md2.0 KB

API Reference: Implementing GCP Organization Policy Constraints

gcloud CLI Commands

# List all org policies
gcloud org-policies list --organization=ORG_ID
 
# Describe specific constraint
gcloud org-policies describe constraints/compute.vmExternalIpAccess --organization=ORG_ID
 
# Set policy from YAML
gcloud resource-manager org-policies set-policy policy.yaml --organization=ORG_ID
 
# Set custom constraint
gcloud org-policies set-custom-constraint custom-constraint.yaml
 
# Check effective policy on project
gcloud org-policies list --project=PROJECT_ID

Baseline Security Constraints

Constraint Type Purpose
compute.vmExternalIpAccess List/Deny Block public VM IPs
compute.requireOsLogin Boolean Mandate OS Login for SSH
compute.disableSerialPortAccess Boolean Disable serial port
storage.uniformBucketLevelAccess Boolean Uniform bucket ACLs
sql.restrictPublicIp Boolean No public Cloud SQL
iam.disableServiceAccountKeyCreation Boolean Force Workload Identity
gcp.resourceLocations List/Allow Restrict to approved regions

Policy YAML Formats

Boolean Policy

constraint: constraints/compute.requireOsLogin
booleanPolicy:
  enforced: true

List Policy (Deny All)

constraint: constraints/compute.vmExternalIpAccess
listPolicy:
  allValues: DENY

List Policy (Allow Specific)

constraint: constraints/gcp.resourceLocations
listPolicy:
  allowedValues:
    - "in:us-locations"
    - "in:eu-locations"

Terraform Resource

resource "google_organization_policy" "example" {
  org_id     = var.org_id
  constraint = "constraints/compute.requireOsLogin"
  boolean_policy { enforced = true }
}

References

standards.md1.1 KB

Standards and References - GCP Organization Policy Constraints

CIS GCP Foundations Benchmark v3.0

Section Control Constraint
1.4 Ensure user-managed service account keys are rotated within 90 days iam.disableServiceAccountKeyCreation
3.10 Ensure VPC Flow Logs are enabled N/A (use custom constraint)
4.4 Ensure OS Login is enabled for a project compute.requireOsLogin
4.5 Ensure serial port access is disabled compute.disableSerialPortAccess
6.2 Ensure Cloud SQL instances are not publicly accessible sql.restrictPublicIp
5.1 Ensure uniform bucket-level access is enabled storage.uniformBucketLevelAccess

NIST 800-53 Controls Mapping

  • AC-3: Access Enforcement
  • AC-6: Least Privilege
  • CM-7: Least Functionality
  • SC-7: Boundary Protection
  • SC-12: Cryptographic Key Establishment

Google Cloud Security Best Practices

  • Principle of least privilege for organization policies
  • Hierarchical policy inheritance model
  • Dry-run testing before enforcement
  • Separate exception management from baseline policies
workflows.md1.6 KB

Workflows - GCP Organization Policy Constraints

Implementation Workflow

1. Inventory Phase
   ├── List all existing organization policies
   ├── Identify current resource configurations
   ├── Map compliance requirements to constraints
   └── Document exceptions needed per team/project
 
2. Design Phase
   ├── Select constraints for baseline enforcement
   ├── Define exception policies for specific folders/projects
   ├── Plan hierarchy (Org → Folder → Project overrides)
   └── Document policy inheritance chain
 
3. Testing Phase
   ├── Deploy constraints in dry-run mode
   ├── Monitor violation logs for 2-4 weeks
   ├── Identify legitimate use cases requiring exceptions
   └── Refine policies based on dry-run results
 
4. Enforcement Phase
   ├── Convert dry-run policies to enforced mode
   ├── Apply exceptions at appropriate hierarchy level
   ├── Communicate changes to engineering teams
   └── Monitor for new violations
 
5. Ongoing Governance
   ├── Review policies quarterly
   ├── Audit exception requests
   ├── Update constraints for new GCP services
   └── Integrate with change management process

Exception Management Workflow

1. Request → Developer requests exception for specific constraint
2. Review → Security team evaluates risk and business justification
3. Approve → Exception approved with scope (project/folder) and duration
4. Implement → Policy override applied at lowest necessary scope
5. Audit → Regular review of active exceptions
6. Expire → Time-bound exceptions automatically revert

Scripts 2

agent.py7.7 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for auditing and managing GCP Organization Policy constraints."""

import json
import argparse
import subprocess
from datetime import datetime


BASELINE_BOOLEAN_CONSTRAINTS = {
    "constraints/compute.vmExternalIpAccess": {"type": "list", "expected": "DENY_ALL"},
    "constraints/compute.requireOsLogin": {"type": "boolean", "expected": True},
    "constraints/compute.disableSerialPortAccess": {"type": "boolean", "expected": True},
    "constraints/compute.disableNestedVirtualization": {"type": "boolean", "expected": True},
    "constraints/storage.uniformBucketLevelAccess": {"type": "boolean", "expected": True},
    "constraints/sql.restrictPublicIp": {"type": "boolean", "expected": True},
    "constraints/iam.disableServiceAccountKeyCreation": {"type": "boolean", "expected": True},
    "constraints/iam.automaticIamGrantsForDefaultServiceAccounts": {"type": "boolean", "expected": True},
}


def run_gcloud(args_list):
    """Run a gcloud command and return parsed JSON."""
    cmd = ["gcloud"] + args_list + ["--format=json"]
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
    if result.returncode != 0:
        return {"error": result.stderr.strip()}
    try:
        return json.loads(result.stdout) if result.stdout.strip() else {}
    except json.JSONDecodeError:
        return {"raw": result.stdout.strip()}


def list_org_policies(org_id):
    """List all active organization policies."""
    return run_gcloud(["org-policies", "list", f"--organization={org_id}"])


def describe_policy(org_id, constraint):
    """Get details of a specific org policy constraint."""
    return run_gcloud(["org-policies", "describe", constraint,
                       f"--organization={org_id}"])


def audit_baseline_compliance(org_id):
    """Audit organization against security baseline constraints."""
    findings = []
    for constraint, expected in BASELINE_BOOLEAN_CONSTRAINTS.items():
        policy = describe_policy(org_id, constraint)
        if isinstance(policy, dict) and "error" in policy:
            findings.append({
                "constraint": constraint,
                "status": "NOT_SET",
                "severity": "HIGH",
                "recommendation": f"Enable {constraint}",
            })
            continue
        if expected["type"] == "boolean":
            enforced = False
            if isinstance(policy, dict):
                bp = policy.get("booleanPolicy", {})
                enforced = bp.get("enforced", False)
            findings.append({
                "constraint": constraint,
                "status": "COMPLIANT" if enforced else "NON_COMPLIANT",
                "severity": "INFO" if enforced else "HIGH",
                "current": enforced,
                "expected": expected["expected"],
            })
        elif expected["type"] == "list":
            lp = policy.get("listPolicy", {}) if isinstance(policy, dict) else {}
            all_denied = lp.get("allValues") == "DENY" or lp.get("deniedValues") == ["*"]
            findings.append({
                "constraint": constraint,
                "status": "COMPLIANT" if all_denied else "NON_COMPLIANT",
                "severity": "INFO" if all_denied else "HIGH",
            })
    return findings


def audit_project_policies(project_id):
    """Audit organization policies effective on a specific project."""
    policies = run_gcloud(["org-policies", "list", f"--project={project_id}"])
    if isinstance(policies, dict) and "error" in policies:
        return [{"error": policies["error"]}]
    return policies if isinstance(policies, list) else []


def check_resource_location_constraint(org_id):
    """Check if resource location constraint is configured."""
    policy = describe_policy(org_id, "constraints/gcp.resourceLocations")
    if isinstance(policy, dict) and "error" not in policy:
        lp = policy.get("listPolicy", {})
        allowed = lp.get("allowedValues", [])
        if allowed:
            return {"status": "CONFIGURED", "allowed_locations": allowed}
    return {"status": "NOT_CONFIGURED", "severity": "MEDIUM",
            "recommendation": "Restrict resource locations to approved regions"}


def generate_terraform_policies(org_id, constraints=None):
    """Generate Terraform HCL for baseline org policies."""
    constraints = constraints or list(BASELINE_BOOLEAN_CONSTRAINTS.keys())
    tf_blocks = []
    for c in constraints:
        info = BASELINE_BOOLEAN_CONSTRAINTS.get(c, {"type": "boolean", "expected": True})
        resource_name = c.split("/")[1].replace(".", "_")
        if info["type"] == "boolean":
            tf_blocks.append(f'''resource "google_organization_policy" "{resource_name}" {{
  org_id     = "{org_id}"
  constraint = "{c}"
  boolean_policy {{
    enforced = true
  }}
}}''')
        elif info["type"] == "list":
            tf_blocks.append(f'''resource "google_organization_policy" "{resource_name}" {{
  org_id     = "{org_id}"
  constraint = "{c}"
  list_policy {{
    deny {{
      all = true
    }}
  }}
}}''')
    return "\n\n".join(tf_blocks)


def generate_compliance_report(findings):
    """Generate a compliance summary from audit findings."""
    total = len(findings)
    compliant = sum(1 for f in findings if f.get("status") == "COMPLIANT")
    non_compliant = sum(1 for f in findings if f.get("status") == "NON_COMPLIANT")
    not_set = sum(1 for f in findings if f.get("status") == "NOT_SET")
    return {
        "total_constraints": total,
        "compliant": compliant,
        "non_compliant": non_compliant,
        "not_set": not_set,
        "compliance_percentage": round(compliant / total * 100, 1) if total else 0,
        "high_severity_gaps": [f for f in findings if f.get("severity") == "HIGH"],
    }


def main():
    parser = argparse.ArgumentParser(description="GCP Organization Policy Agent")
    parser.add_argument("--org-id", help="GCP organization ID")
    parser.add_argument("--project", help="GCP project ID for project-level audit")
    parser.add_argument("--action", choices=["audit", "list", "terraform", "locations"],
                        default="audit")
    parser.add_argument("--output", default="gcp_orgpolicy_report.json")
    args = parser.parse_args()

    report = {"generated_at": datetime.utcnow().isoformat(), "results": {}}

    if args.action == "audit" and args.org_id:
        findings = audit_baseline_compliance(args.org_id)
        summary = generate_compliance_report(findings)
        report["results"]["findings"] = findings
        report["results"]["summary"] = summary
        print(f"[+] Compliance: {summary['compliance_percentage']}%"
              f" ({summary['compliant']}/{summary['total_constraints']})")

    elif args.action == "list" and args.org_id:
        policies = list_org_policies(args.org_id)
        report["results"]["policies"] = policies
        count = len(policies) if isinstance(policies, list) else 0
        print(f"[+] Active policies: {count}")

    elif args.action == "terraform" and args.org_id:
        tf = generate_terraform_policies(args.org_id)
        report["results"]["terraform"] = tf
        print("[+] Terraform configuration generated")

    elif args.action == "locations" and args.org_id:
        result = check_resource_location_constraint(args.org_id)
        report["results"]["location_constraint"] = result
        print(f"[+] Location constraint: {result['status']}")

    if args.project:
        project_policies = audit_project_policies(args.project)
        report["results"]["project_policies"] = project_policies
        print(f"[+] Project {args.project}: {len(project_policies)} policies")

    with open(args.output, "w") as f:
        json.dump(report, f, indent=2, default=str)
    print(f"[+] Report saved to {args.output}")


if __name__ == "__main__":
    main()
process.py7.7 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
GCP Organization Policy Constraints Management Script

Automates auditing, deploying, and monitoring organization policies
across a GCP organization hierarchy.
"""

import json
import subprocess
import sys
from datetime import datetime


SECURITY_CONSTRAINTS = {
    "compute.vmExternalIpAccess": {
        "type": "list",
        "action": "deny_all",
        "description": "Deny external IP addresses on VMs"
    },
    "compute.requireOsLogin": {
        "type": "boolean",
        "enforced": True,
        "description": "Require OS Login for SSH access"
    },
    "compute.disableSerialPortAccess": {
        "type": "boolean",
        "enforced": True,
        "description": "Disable serial port access on VMs"
    },
    "iam.disableServiceAccountKeyCreation": {
        "type": "boolean",
        "enforced": True,
        "description": "Disable service account key creation"
    },
    "storage.uniformBucketLevelAccess": {
        "type": "boolean",
        "enforced": True,
        "description": "Enforce uniform bucket-level access"
    },
    "sql.restrictPublicIp": {
        "type": "boolean",
        "enforced": True,
        "description": "Restrict public IP on Cloud SQL"
    },
    "gcp.resourceLocations": {
        "type": "list",
        "action": "allow",
        "values": ["in:us-locations", "in:eu-locations"],
        "description": "Restrict resource locations to US and EU"
    },
    "compute.disableNestedVirtualization": {
        "type": "boolean",
        "enforced": True,
        "description": "Disable nested virtualization"
    }
}


def run_gcloud(args):
    """Execute a gcloud command and return output."""
    cmd = ["gcloud"] + args + ["--format=json"]
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        return None, result.stderr
    try:
        return json.loads(result.stdout) if result.stdout.strip() else {}, None
    except json.JSONDecodeError:
        return result.stdout, None


def audit_org_policies(org_id):
    """Audit current organization policies and identify gaps."""
    print(f"[*] Auditing organization policies for org: {org_id}")

    results, err = run_gcloud([
        "org-policies", "list",
        f"--organization={org_id}"
    ])

    if err:
        print(f"[!] Error listing policies: {err}")
        return None

    active_constraints = set()
    if isinstance(results, list):
        for policy in results:
            constraint = policy.get("constraint", "")
            active_constraints.add(constraint.replace("constraints/", ""))

    print(f"\n[+] Active organization policies: {len(active_constraints)}")

    # Check against recommended baseline
    missing = []
    present = []
    for constraint in SECURITY_CONSTRAINTS:
        if constraint in active_constraints:
            present.append(constraint)
            print(f"  [OK] {constraint}: {SECURITY_CONSTRAINTS[constraint]['description']}")
        else:
            missing.append(constraint)
            print(f"  [MISSING] {constraint}: {SECURITY_CONSTRAINTS[constraint]['description']}")

    return {
        "active": list(active_constraints),
        "baseline_present": present,
        "baseline_missing": missing,
        "compliance_score": len(present) / len(SECURITY_CONSTRAINTS) * 100
    }


def generate_policy_yaml(constraint_name, config, dry_run=False):
    """Generate YAML policy definition for a constraint."""
    import yaml

    policy = {"constraint": f"constraints/{constraint_name}"}

    if config["type"] == "boolean":
        policy["booleanPolicy"] = {"enforced": config["enforced"]}
    elif config["type"] == "list":
        if config.get("action") == "deny_all":
            policy["listPolicy"] = {"allValues": "DENY"}
        elif config.get("action") == "allow":
            policy["listPolicy"] = {"allowedValues": config["values"]}

    if dry_run:
        policy["dryRunSpec"] = True

    return yaml.dump(policy, default_flow_style=False)


def deploy_baseline_policies(org_id, dry_run=True):
    """Deploy baseline organization policies."""
    mode = "dry-run" if dry_run else "enforced"
    print(f"\n[*] Deploying baseline policies in {mode} mode for org: {org_id}")

    for constraint, config in SECURITY_CONSTRAINTS.items():
        print(f"\n  Deploying: {constraint}")
        print(f"  Description: {config['description']}")

        # Generate policy file
        import tempfile
        policy_yaml = generate_policy_yaml(constraint, config, dry_run)

        with tempfile.NamedTemporaryFile(
            mode="w", suffix=".yaml", delete=False, prefix=f"policy_{constraint}_"
        ) as f:
            f.write(policy_yaml)
            policy_file = f.name

        # Apply policy
        _, err = run_gcloud([
            "org-policies", "set-policy",
            f"--organization={org_id}",
            policy_file
        ])

        if err:
            print(f"  [FAIL] Error applying {constraint}: {err}")
        else:
            print(f"  [OK] Successfully applied {constraint}")


def check_policy_violations(org_id, constraint_name):
    """Check for resources violating a specific policy."""
    print(f"\n[*] Checking violations for: {constraint_name}")

    results, err = run_gcloud([
        "asset", "search-all-resources",
        f"--scope=organizations/{org_id}",
        f"--query=policy:constraints/{constraint_name}"
    ])

    if err:
        print(f"[!] Error checking violations: {err}")
        return []

    if isinstance(results, list):
        print(f"[+] Found {len(results)} potentially affected resources")
        for resource in results[:10]:
            print(f"  - {resource.get('name', 'unknown')}")
        return results

    return []


def generate_compliance_report(audit_results, org_id):
    """Generate a compliance report for organization policies."""
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    report = f"""
{'='*60}
GCP Organization Policy Compliance Report
Organization: {org_id}
Generated: {timestamp}
{'='*60}

Compliance Score: {audit_results['compliance_score']:.1f}%

Baseline Policies Present ({len(audit_results['baseline_present'])}):
"""
    for p in audit_results['baseline_present']:
        report += f"  [PASS] {p}\n"

    report += f"\nBaseline Policies Missing ({len(audit_results['baseline_missing'])}):\n"
    for m in audit_results['baseline_missing']:
        report += f"  [FAIL] {m} - {SECURITY_CONSTRAINTS[m]['description']}\n"

    report += f"\nTotal Active Policies: {len(audit_results['active'])}\n"
    report += f"\nRecommendation: {'Compliant' if audit_results['compliance_score'] >= 80 else 'Remediation Required'}\n"

    return report


if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description="GCP Organization Policy Management")
    parser.add_argument("--org-id", required=True, help="GCP Organization ID")
    parser.add_argument("--audit", action="store_true", help="Audit existing policies")
    parser.add_argument("--deploy", action="store_true", help="Deploy baseline policies")
    parser.add_argument("--dry-run", action="store_true", default=True, help="Deploy in dry-run mode")
    parser.add_argument("--enforce", action="store_true", help="Deploy in enforced mode")
    parser.add_argument("--check-violations", type=str, help="Check violations for a constraint")

    args = parser.parse_args()

    if args.audit:
        results = audit_org_policies(args.org_id)
        if results:
            report = generate_compliance_report(results, args.org_id)
            print(report)

    if args.deploy:
        dry_run = not args.enforce
        deploy_baseline_policies(args.org_id, dry_run=dry_run)

    if args.check_violations:
        check_policy_violations(args.org_id, args.check_violations)

Assets 1

template.mdtext/markdown · 1.1 KB
Keep exploring