Resource hierarchy & org policy
Org → folders → projects, and inherited guardrails.
A chain of shops works because head office writes a rule once and every new branch is bound by it on opening day. Nobody reads the rulebook aloud to each new clerk. Google Cloud runs on the same idea. Everything you own hangs off one root, and the security controls you set near the top roll downhill to everything under them without you lifting a finger. Get the shape of that tree right and a single policy covers a thousand projects. Get it wrong and you spend your career fixing settings one project at a time, missing half of them.
Four layers, and everything runs downhill
Four layers, from the top down. The organization is your company, pinned to a domain name like acme.com. Folders are the grouping layer, usually one per team or per environment. Projects are the real containers where resources and the bill actually live. At the bottom sit the resources themselves: a virtual machine (a VM, a computer you rent by the hour inside Google's data centre), a storage bucket, a database. Two things slide down this tree on their own. The first is IAM (Identity and Access Management, Google's system for deciding which identity may call which API). The second is organization policy, which decides what configuration is allowed to exist at all. Grant somebody a role on a folder and that grant lands on every project inside, with no extra typing. The project is also your blast-radius line. If an attacker gets into one, the damage stops at its edge. That is why prod, staging and shared tooling each get a project of their own.
gcloud organizations list# output:DISPLAY_NAME ID DIRECTORY_CUSTOMER_IDacme.com 843906890471 C03xk9zzp
That number is the anchor for everything else. You will paste it into folder and policy commands all day, so grab it first. The directory customer ID, the one starting with C, points at your Google Workspace or Cloud Identity account: the master staff directory for your company. It matters later, when you fence off sharing so only your own people can be added to anything.
Folders are your blast-radius buckets
gcloud resource-manager folders create \--display-name=production \--organization=843906890471# output:Waiting for [operations/cp.8891233707421] to finish...done.Created folder [451219876340].
Folders nest. Organization, then a production folder, then a payments sub-folder if one team wants its own boundary inside prod. Here is the payoff. Grant a break-glass admin role (the access nobody touches on a normal day, saved for a real emergency) or a read-only auditor role once on the production folder, and every project underneath picks it up. The ones that exist today, and the ones somebody spins up next March. No extra work either time. The same machinery bites back. One lazy, broad grant high in the tree leaks downhill just as quietly, and nobody spots it until an auditor does.
How you slice the folders is a genuine design decision, not paperwork. Environment first (prod, non-prod, sandbox) makes it easy to drop one strict guardrail across all of production in a single move, which is normally what the security team is asking for. Team first maps neatly onto who owns what and who pays the bill. Most grown-up estates do both: environment folders on top, team folders underneath. Remember what splitting costs you, though. Quotas, billing, and switching on each API a project is allowed to call (an API is a Google service your code talks to, like the storage service or the compute service) all live at the project level. More projects means more of that plumbing to look after, bought in exchange for tighter isolation.
Org policy: house rules even an Owner cannot break
IAM and organization policy answer two different questions, and people mix them up constantly. IAM is the guest list: which identity may call which API. Organization policy is the building code: what you are allowed to build in the first place, whoever you are. A project Owner has near-total power inside their project. Set a policy at head office saying no VM may carry a public IP address (an internet-facing address that lets strangers reach the machine directly) and that Owner still cannot attach one. The constraint sits above their pay grade. That is what makes organization policy the preventive backbone of a landing zone, the locked-down baseline every new project is born into. You turn off service-account keys (long-lived passwords that let software log in as a robot user), force OS Login (so every server login traces back to a real Google identity instead of shared keys drifting around in chat), block public IPs, and restrict sharing to your own domain. Every one of those limits holds for the whole subtree under the node where you set it.
# org policy v2 file, applied at the production foldername: folders/451219876340/policies/compute.vmExternalIpAccessspec:rules:- denyAll: true
gcloud org-policies set-policy no-external-ip.yaml# output:name: folders/451219876340/policies/compute.vmExternalIpAccessspec:etag: CMuk_r0GEND1w-4Crules:- denyAll: trueupdateTime: '2026-07-16T14:07:52.331046Z'
gcloud org-policies describe compute.vmExternalIpAccess \--project=payments-prod --effective# output (project never set this — it's inheriting the folder rule):name: projects/739104556201/policies/compute.vmExternalIpAccessspec:rules:- denyAll: true
Read that output again. payments-prod never set the policy. It is reading the folder's rule as its own effective policy, resolved from the top down at the moment the request is evaluated. The merge rules are worth learning now, so nothing surprises you at 2am. A boolean constraint (enforce, or do not) takes the most specific setting on the path, and a child can only loosen a parent by writing enforce:false at its own level. A list constraint (a set of allowed or denied values) either replaces the parent's list outright or, if you add inheritFromParent:true, extends it, with deny winning whenever the two disagree. Set reset:true and that node falls back to Google's platform default. When you have no idea what a new policy will break, write a dry-run spec first. It records the violations it would have caught in Cloud Audit Logs and blocks nothing at all, so you can watch the real blast radius for a week before you switch enforcement on.
gcloud compute routers create prod-nat-router \--network=payments-vpc --region=us-central1 --project=payments-prodgcloud compute routers nats create prod-nat-config \--router=prod-nat-router --region=us-central1 --project=payments-prod \--auto-allocate-nat-external-ips --nat-all-subnet-ip-ranges# output:Created [https://www.googleapis.com/compute/v1/projects/payments-prod/regions/us-central1/routers/prod-nat-router].Creating NAT [prod-nat-config] in router [prod-nat-router]...done.
One habit to build on day one. Whoever holds Organization Admin or Owner at the root controls every folder, every policy and every log sink underneath, so treat it like the master key to the whole building. Give it to a tiny break-glass group behind hardware MFA (multi-factor authentication, here a physical key you tap to prove it is really you), reach for it almost never, and fire an alert the second it is used. Everything you do on a normal Tuesday should run through narrow roles further down the tree.
A workable tree for a mid-size company looks roughly like this. Under the organization, one folder for shared platform services (networking, logging sinks, Security Command Center wiring), one folder per business unit or product line, and under each of those a pair of folders for non-prod and prod. Projects hang off those environment folders, never off the org root. That nesting is what lets a single org policy, a single IAM deny policy and a single logging sink cover every project anyone creates next year, with nobody re-applying anything by hand.
The two things flowing downhill behave differently, and the difference matters. An allow binding is additive: put it on a folder and it reaches every project underneath, stacked on top of whatever those projects already grant. An org policy is a ceiling: set it at the organization with inheritance on and it reaches every folder and project below unless a child writes a carefully scoped exception. That is why you almost never grant roles/owner at the org, and why an exception folder deserves the same treatment as a drum of hazardous waste. Every exception is a permanent hole somebody has to remember exists.
When a new team arrives, do not hand them a bare project under the org root. Hand them a folder under the right business unit, with the landing-zone scripts already stamping on the same org policies, log sinks and essential contacts the rest of the estate uses. The tree does the teaching. The runbook only adds another leaf.
Try this
In a scratch organization, or a folder you already own, list the tree and then list the org policies that already reach a project inside it. You are proving inheritance with your own eyes, not designing a new tree from nothing.
gcloud organizations listgcloud resource-manager folders list --organization=ORGANIZATION_IDgcloud projects describe payments-prod --format="yaml(name,parent,projectId)"gcloud org-policies list --project=payments-prod --format="table(constraint)"
DISPLAY_NAME: Acme CorpID: 123456789012DIRECTORY_CUSTOMER_ID: C012abcdfolders/111111111111 platformfolders/222222222222 workloadsname: projects/802451296328projectId: payments-prodparent:type: folderid: '222222222222'CONSTRAINTconstraints/compute.requireOsLoginconstraints/iam.disableServiceAccountKeyCreationconstraints/storage.uniformBucketLevelAccess
Takeaway
The tree is the control plane. Folders are blast-radius buckets, org policies are house rules a project Owner cannot peel off, and both get resolved from the root down at the moment a request is evaluated. Do the thinking about inheritance once, at the shape-of-the-tree stage, and you stop chasing settings project by project for the rest of the estate's life.
Next you tighten who can open which door inside that tree: IAM roles, conditions and deny policies. A beautifully shaped hierarchy with sloppy grants still leaks.