Google Cloud Organization Policy constraints are available on every GCP organization from day one. They're free. They operate at the resource manager level, meaning they apply before any IAM policy evaluation for affected resource types. And in most GCP environments we look at, they're either completely unconfigured or only partially used — typically because someone enabled iam.disableServiceAccountKeyCreation after an audit finding and stopped there.
This is a missed opportunity. GCP's Organization Policy Service gives you a category of preventive control that operates differently from IAM — not as a permission boundary on principals, but as a constraint on resource configuration. Understanding that distinction is prerequisite to using it effectively.
Organization Policies vs. IAM: Different Layers, Different Enforcement
IAM in GCP controls who can do what. Organization Policy controls what configurations are permitted regardless of who is making the request. A user with roles/storage.admin on a bucket can normally set that bucket to public. An organization policy constraint on storage.publicAccessPrevention blocks that configuration change even for principals with full storage admin rights.
This is the key property: org constraints are not bypassed by elevated IAM permissions. An org admin with roles/owner in a project is still subject to org constraints applied at a parent node. The constraint operates at a different layer in the resource hierarchy evaluation stack.
The practical implication: if you rely solely on IAM to prevent misconfigurations, you're trusting that no principal with the necessary permissions will ever make the misconfiguration — either by accident, by compromise, or by a misunderstanding of the intended configuration. Org constraints remove that dependency. They make certain configurations structurally impossible, not just unauthorized.
The Constraint Taxonomy: Managed, Custom, and Default
GCP organization constraints fall into three categories that are worth distinguishing:
Managed constraints (recommended)
These are constraints defined and maintained by Google, identified by the constraints/ prefix (e.g., constraints/compute.disableSerialPortAccess). There are over 100 managed constraints covering Compute Engine, Cloud Storage, GKE, IAM, networking, and other services. They have documented semantics, they're tested by Google, and they respond predictably to policy evaluation.
Custom constraints
Introduced in GA in 2023, custom org policy constraints let you define your own constraint logic using Common Expression Language (CEL). Custom constraints operate on resource create and update operations and can inspect resource attributes that managed constraints don't cover. They look like this:
name: organizations/ORGANIZATION_ID/customConstraints/custom.restrictGKENodeVersion
resourceTypes:
- container.googleapis.com/NodePool
methodTypes:
- CREATE
- UPDATE
condition: "resource.config.nodeVersion.startsWith('1.28') || resource.config.nodeVersion.startsWith('1.29')"
actionType: ALLOW
displayName: Restrict GKE Node Pool to approved versions
description: Only allow node pools on approved GKE minor versions
Custom constraints are powerful but have important limitations: they only fire on create and update operations (not on existing resources), they don't retroactively remediate non-compliant existing resources, and the CEL evaluation is done against the resource representation in the API request body, not the final committed state.
Default constraint enforcement
Some constraints have a default enforcement state that applies unless overridden. constraints/iam.disableServiceAccountKeyCreation is off by default (service account keys are allowed). constraints/compute.requireOsLogin is off by default. Enabling these is a policy decision — you're opting in to a restriction. But checking which high-value constraints are currently not enforced in your org takes ten minutes and is worth doing.
The Constraints Most GCP Organizations Should Have Enabled
This isn't a comprehensive list — the right constraints depend on your workloads and threat model. These are the ones that provide high-value preventive coverage with low false-positive risk for most environments.
compute.disableSerialPortAccess
Disables interactive serial port access to Compute Engine VMs. Serial port access bypasses SSH key controls and can provide shell access to a VM even without valid credentials. It's a common misconfiguration that shows up in incident timelines. There's almost no legitimate use case for interactive serial port access in a production environment.
resource "google_org_policy_policy" "disable_serial_port" {
name = "organizations/${var.org_id}/policies/compute.disableSerialPortAccess"
parent = "organizations/${var.org_id}"
spec {
rules {
enforce = "TRUE"
}
}
}
iam.disableServiceAccountKeyCreation
Prevents creating user-managed service account keys. Service account keys are long-lived credentials that can be exported outside your organization. Workload Identity Federation is the correct replacement for most use cases. Enabling this constraint forces teams to move to short-lived credentials. It will break things — plan for migration. But the post-migration posture is substantially better.
storage.publicAccessPrevention
Prevents configuring Cloud Storage buckets or objects for public access. This is the GCP equivalent of AWS S3 Block Public Access at the account level. The managed version enforces this at the org or folder level, meaning it covers all projects in scope. There is a legitimate use case for public storage (static website hosting, public dataset distribution) — create a dedicated folder for those projects and set the constraint to inherited or override it at that folder node specifically.
compute.skipDefaultNetworkCreation
Prevents the auto-created default VPC network from being created when a new project is provisioned. The default network has permissive firewall rules and is a consistent source of accidental exposure. If your infrastructure provisioning creates explicit VPCs via Terraform, this constraint eliminates the default network before it can become a problem.
compute.vmExternalIpAccess
Restricts which VMs can have external IP addresses. You can supply a list of allowed VM instances (by self-link) or set it to deny all external IPs. For environments where workloads should communicate through load balancers or private service networking, this constraint prevents accidentally-configured external IPs from creating unexpected exposure.
Policy Inheritance and Override Mechanics
Org constraints inherit down the resource hierarchy: organization → folder → project. A constraint set at the org node applies to all folders and projects unless overridden. Whether a constraint can be overridden at a lower level depends on the constraint's inheritance mode:
- Deny all with no inheritance override: the constraint is enforced at the set scope and all descendants
- Allow override: child nodes can provide a more permissive policy — but only if the parent policy doesn't explicitly forbid this. By default, org-level constraints can be overridden by project-level policies unless the org policy specifically sets
inheritFromParent: falseand usesreset: true
This creates a subtle trap. If you set storage.publicAccessPrevention = enforced at the org level but a project owner has roles/orgpolicy.policyAdmin, they can override it at the project level. Preventing this requires also restricting who holds that role — or setting the constraint with spec.reset semantics that prevent lower-level override.
Check who has roles/orgpolicy.policyAdmin in your org. In most environments, this is a broader group than intended.
Custom Constraints for Service-Specific Configuration Control
Where managed constraints don't cover what you need, custom constraints fill the gap. A scenario we've seen more than once: a team wants to enforce that all Cloud Run services have a minimum number of instances configured to avoid cold starts affecting certain SLA tiers, but also wants to prevent any Cloud Run service from being deployed without VPC connector configuration. Managed constraints don't cover Cloud Run service configuration at this granularity.
Custom constraint for the VPC connector requirement:
name: organizations/ORGANIZATION_ID/customConstraints/custom.requireCloudRunVpcConnector
resourceTypes:
- run.googleapis.com/Service
methodTypes:
- CREATE
- UPDATE
condition: >
has(resource.spec.template.spec.containers) &&
has(resource.spec.template.metadata.annotations) &&
"run.googleapis.com/vpc-access-connector" in resource.spec.template.metadata.annotations
actionType: ALLOW
displayName: Require VPC connector on Cloud Run services
After defining the custom constraint, you attach it as an org policy:
resource "google_org_policy_policy" "require_cloudrun_vpc" {
name = "projects/${var.project_id}/policies/custom.requireCloudRunVpcConnector"
parent = "projects/${var.project_id}"
spec {
rules {
enforce = "TRUE"
}
}
}
The constraint fires on every Cloud Run service create or update in that project. If the annotation is absent, the operation is denied. This is a deploy-time enforcement mechanism that doesn't depend on post-deploy scanning to catch violations.
What Org Constraints Can't Do
Org constraints are not a complete security solution, and it's worth being specific about their limitations:
They don't remediate existing resources. A constraint set today doesn't change the configuration of buckets, VMs, or services that were created before the constraint existed. If storage.publicAccessPrevention is enabled today, buckets that were already public stay public — the constraint only blocks new public configurations. Existing resource remediation requires a separate mechanism (usually a script or Config Connector policy).
They don't cover all GCP services. Managed constraints exist for the major services (Compute, Storage, GKE, IAM, Cloud SQL, networking). Custom constraints cover services that expose resources through the Cloud Resource Manager API. Some services have limited or no org policy support.
They don't replace IAM. Org constraints and IAM are complementary. A constraint that prevents public storage doesn't prevent an authenticated user from exfiltrating data from a private bucket they have access to. The right model is both: IAM controls access, org constraints control configuration.
Treating org constraints as a secondary control — something to get to after "the real security work" is done — means leaving preventive coverage on the table that your GCP organization already ships. The policies exist in the API. Enabling them is a configuration decision, not a deployment. For the major misconfigurations that show up repeatedly in GCP incident reports, the org constraint layer is the place to enforce them at the infrastructure level, not the IAM level.