Organization Policy Service
The Organization Policy Service restricts what any principal may configure, whatever IAM roles it holds. The GCP Enterprise Baseline sets 19 constraints at the organization node, so every folder and project inherits them and no project Owner can switch one off.
What it does
An organization policy applies a constraint to a node of the resource hierarchy. Boolean constraints switch a behavior on or off, such as "no service account keys". List constraints allow or deny values, such as the regions resources may be created in. A policy set at the organization is inherited by every folder and project; a lower node can override it only where a policy says so. Custom constraints express a rule in CEL (Common Expression Language) over a resource's fields, and any policy can run as a dry run that logs violations without blocking them.
How BuiltForProd uses it
The organizations unit sets every constraint at the organization node from two maps in modules/organizations/org-policies.tf:
locals {
boolean_constraints = {
"iam.disableServiceAccountKeyCreation" = true
"iam.disableServiceAccountKeyUpload" = true
"iam.automaticIamGrantsForDefaultServiceAccounts" = true
"storage.publicAccessPrevention" = true
"storage.uniformBucketLevelAccess" = true
"compute.skipDefaultNetworkCreation" = true
"compute.requireOsLogin" = true
"compute.requireShieldedVm" = true
"compute.disableSerialPortAccess" = true
"sql.restrictPublicIp" = true
"sql.restrictAuthorizedNetworks" = true
}
network_project = "${var.namespace}-core-network${var.project_id_suffix}"
list_constraints = {
"iam.allowedPolicyMemberDomains" = { allowed = var.allowed_member_customer_ids }
"iam.workloadIdentityPoolProviders" = { allowed = ["https://token.actions.githubusercontent.com"] }
"compute.vmExternalIpAccess" = { denied_all = true }
"compute.restrictVpcPeering" = { allowed = ["under:projects/${local.network_project}"] }
"compute.restrictSharedVpcHostProjects" = { allowed = ["under:projects/${local.network_project}"] }
"run.allowedIngress" = { allowed = ["internal-and-cloud-load-balancing"] }
"essentialcontacts.allowedContactDomains" = { allowed = ["@${var.org_domain}"] }
"gcp.resourceLocations" = { allowed = var.allowed_locations }
}
}
The eleven boolean constraints block service account key creation and upload, automatic Editor grants to default service accounts, public buckets and per-object ACLs, the default network, VMs without OS Login or Shielded VM, serial port access, and public IPs or authorized networks on Cloud SQL. The eight list constraints:
| Constraint | What it holds |
|---|---|
iam.allowedPolicyMemberDomains | IAM members only from the organization's Cloud Identity customer |
iam.workloadIdentityPoolProviders | Workload Identity Federation trusts the GitHub Actions issuer only |
compute.vmExternalIpAccess | No VM gets an external IP address |
compute.restrictVpcPeering | Peering only with networks of acme-core-network |
compute.restrictSharedVpcHostProjects | acme-core-network is the only Shared VPC host |
run.allowedIngress | Cloud Run accepts internal and load-balancer traffic only |
essentialcontacts.allowedContactDomains | Essential Contacts only on the organization's domain |
gcp.resourceLocations | The home region, every region with folders in the repository, and us |
The region list is not typed by hand: the unit derives it from the home region and every region folder under environments/, plus the us multi-region that Artifact Registry and dual-region buckets need. Adding a region folder opens that region.
Exceptions. acme-core-public serves intentionally public objects, so it carries two project-level overrides: public access prevention off, and domain-restricted sharing open (allUsers is not a member of the organization's customer). Each stage project also receives a project-level compute.restrictSharedVpcSubnetworks policy from the shared-vpc-service unit, which lets it use only its own stage subnets of the Shared VPC.
Custom label constraints. Three custom constraints, custom.requirePlatformLabels{Instance,Cluster,Bucket}, deny Compute Engine instances, GKE clusters and Cloud Storage buckets created or updated without the namespace, stage and managed_by labels. They run as a dry run (enforce_label_policy = false): violations are logged and nothing is blocked until the switch is turned on once the log is clean.
Organization policies cannot protect log sinks, so the audit trail is guarded by an IAM deny policy instead. A change to modules/ needs a review from the infrastructure admins and the platform leads (CODEOWNERS).
Terms you will see
| Term | Meaning |
|---|---|
| Constraint | A named restriction, such as iam.disableServiceAccountKeyCreation. |
| Boolean / list | The two built-in constraint types: on or off, or a set of allowed or denied values. |
| Custom constraint | A constraint written in CEL over a resource's fields, named custom.<name>. |
| Dry run | A policy spec that logs violations without enforcing them. |
| Override | A project-level policy that replaces the inherited one, as on acme-core-public. |
| Inheritance | A policy at the organization applying to every folder and project beneath it. |
Where to read more
- GCP Enterprise Baseline overview for the preventive guardrails as a whole.
- Resource Manager for the tree the policies apply to.
- Policy as code for keeping guardrails in reviewed code.