Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
.claude/skills/implementing-gcp-organization-policy-constraints/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | — | — |
| case-18 | ✗→✓ | ▲ Improved | — | — |
| case-07 | ✗→✓ | ▲ Improved | — | — |
| case-12 | ✗→✓ | ▲ Improved | — | — |
| case-06 | ✗→✓ | ▲ Improved | — | — |
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.
gcloud CLI configured and authenticatedroles/orgpolicy.policyAdmin)Policies inherit from the lowest ancestor with an enforced policy. If no ancestor has a policy, Google's managed default behavior applies.
bash# Deny external IP addresses on all VMs gcloud resource-manager org-policies set-policy \ --organization=ORGANIZATION_ID \ policy.yaml
policy.yaml:
yamlconstraint: constraints/compute.vmExternalIpAccess listPolicy: allValues: DENY
bashgcloud org-policies set-policy \ --organization=ORGANIZATION_ID \ location-policy.yaml
location-policy.yaml:
yamlconstraint: constraints/gcp.resourceLocations listPolicy: allowedValues: - "in:us-locations" - "in:eu-locations"
yamlconstraint: constraints/iam.automaticIamGrantsForDefaultServiceAccounts booleanPolicy: enforced: true
yamlconstraint: constraints/compute.requireOsLogin booleanPolicy: enforced: true
yamlconstraint: constraints/compute.disableSerialPortAccess booleanPolicy: enforced: true
yamlconstraint: constraints/storage.uniformBucketLevelAccess booleanPolicy: enforced: true
yamlconstraint: constraints/sql.restrictPublicIp booleanPolicy: enforced: true
yamlconstraint: constraints/iam.disableServiceAccountKeyCreation booleanPolicy: enforced: true
hclresource "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"] } } }
Use Policy Intelligence tools to test changes before enforcement:
bash# Create a dry-run policy to monitor impact gcloud org-policies set-policy \ --organization=ORGANIZATION_ID \ dry-run-policy.yaml
dry-run-policy.yaml:
yamlconstraint: constraints/compute.vmExternalIpAccess listPolicy: allValues: DENY dryRunSpec: true
bash# Check violations against dry-run policy gcloud org-policies list-custom-constraints \ --organization=ORGANIZATION_ID
yaml# 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
bashgcloud org-policies set-custom-constraint custom-constraint.yaml
bashgcloud org-policies list --organization=ORGANIZATION_ID
bashgcloud org-policies describe constraints/compute.vmExternalIpAccess \ --organization=ORGANIZATION_ID
bashgcloud asset search-all-resources \ --scope=organizations/ORGANIZATION_ID \ --query="policy:constraints/compute.vmExternalIpAccess"
| Constraint | Type | Scope | Purpose | |-----------|------|-------|---------| | compute.vmExternalIpAccess | List/Deny | Org | Prevent public VM IPs | | gcp.resourceLocations | List/Allow | Org | Restrict to approved regions | | iam.disableServiceAccountKeyCreation | Boolean | Org | Force Workload Identity | | compute.requireOsLogin | Boolean | Org | Mandate OS Login for SSH | | storage.uniformBucketLevelAccess | Boolean | Org | Enforce uniform bucket access | | sql.restrictPublicIp | Boolean | Org | No public Cloud SQL | | compute.disableSerialPortAccess | Boolean | Org | Disable serial port | | compute.disableNestedVirtualization | Boolean | Org | No nested VMs |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-23 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted, and 22 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +39 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.