Part 1 of 1218 min read · 9 diagramsAI-assisted

Fundamentals & Project Structure

Table of Contents#

  1. Why GCP Gets Its Own Dedicated Series
  2. How GCP's Geography Differs From AWS's
  3. The Project — GCP's Real Unit of Isolation
  4. Why a Single Project for Everything Is Still a Trap
  5. The Resource Hierarchy: Organization, Folders, Projects
  6. Folders — Grouping Projects by Purpose
  7. Organization Policies — GCP's Guardrails
  8. A Realistic Multi-Project Resource Hierarchy
  9. Policy Inheritance — How Rules Flow Down the Hierarchy
  10. Google Cloud's Well-Architected Equivalent: The Architecture Framework
  11. The Shared Responsibility Model on GCP
  12. Billing Accounts — Decoupled From Projects
  13. The gcloud CLI and Client Libraries
  14. Labels — GCP's Version of Tags
  15. Resource Manager Tags — A Newer, More Powerful Alternative
  16. A Full Worked Landing Zone Bootstrap
  17. Part 1 CLI Cheat Sheet
  18. APIs and Services — Explicit Enablement Required
  19. Quotas — GCP's Version of Service Quotas
  20. Essential Contacts — Making Sure Google Can Reach You
  21. Common Mistakes
  22. Worked Practice Problems
  23. Summary and What's Next

Why GCP Gets Its Own Dedicated Series#

The AWS Cloud Architecture series already established the core framing this entire series reuses: a cloud provider is a specific, concrete implementation of principles already covered in depth elsewhere in this course — replication (Databases series), least privilege (DevSecOps series), CAP theorem (Reliability & Architecture Patterns series). GCP is genuinely worth its own series rather than a diff against AWS for a real reason: Google built GCP around its own internal infrastructure lineage (Borg, the system that inspired Kubernetes; Spanner, the globally-consistent database; the same global network backbone GCP's own products run on) — this produces real, substantive architectural differences from AWS, not just renamed services. Where the two providers ARE conceptually identical, this series says so explicitly and moves on; where GCP does something genuinely different, that difference gets the same explicit, worked-through treatment AWS's series gave its own distinctive features.


How GCP's Geography Differs From AWS's#

The Region/Availability Zone model itself is essentially identical to AWS's (already covered in depth in the AWS series, Part 1) — but GCP's NETWORK layer underneath it is genuinely different in a way worth understanding immediately, since it shapes several later parts of this series.

Diagram

Why this matters concretely, worth stating explicitly, and directly previewing Part 4's VPC discussion: AWS's VPC is inherently REGIONAL — a subnet lives in one region, and connecting VPCs across regions requires explicit peering or Transit Gateway (AWS series, Part 4). GCP's VPC is GLOBAL by default — a single VPC can have subnets in EVERY region worldwide, with traffic between them routed over Google's own private backbone, with no peering or gateway needed at all. This is the single most consequential geography-level difference between the two providers, and it changes how multi-region architecture is approached throughout this entire series.


The Project — GCP's Real Unit of Isolation#

A GCP Project is the direct analog of an AWS Account (AWS series, Part 1) — the real, meaningful isolation boundary for resources, billing, and IAM.

Diagram
# Create a project (the Project ID must be GLOBALLY unique)
gcloud projects create my-app-prod-4f21 --name="My App - Production"

# Link it to a billing account — a project with NO linked
# billing account cannot use most paid services at all
gcloud billing projects link my-app-prod-4f21 --billing-account=012345-6789AB-CDEF01

Why the Project ID being permanently, globally unique and IMMUTABLE is worth knowing precisely, a genuinely common real gotcha: unlike a project's display NAME (which can be changed anytime), the Project ID is set once at creation and can never be changed — and once released (if a project is deleted), that exact ID may become available for anyone else to claim, which is why many organizations adopt a deliberate ID-naming convention (e.g. company-env-service-randomsuffix) up front rather than improvising one during creation.


Why a Single Project for Everything Is Still a Trap#

Directly the same lesson already established in the AWS series (Part 1), worth restating precisely for GCP's specific terminology.

Diagram

Why this is worth stating with just as much force as the AWS series did, worth stating explicitly: the fix — a genuine multi-project structure — is dramatically easier to adopt at the very start of a GCP footprint than to untangle later, after hundreds of resources and IAM bindings already exist tangled together inside one project.


The Resource Hierarchy: Organization, Folders, Projects#

GCP's version of the multi-account landing zone already covered in the AWS series (Part 1) — structurally similar, with GCP-specific names.

Diagram

A direct vocabulary mapping worth stating explicitly for anyone coming from the AWS series: GCP's Organization ≈ AWS's Organization (root); GCP's Folder ≈ AWS's Organizational Unit (OU); GCP's Project ≈ AWS's Account — same underlying hierarchy shape, different vendor-specific names.

gcloud organizations list
gcloud resource-manager folders create --display-name="Production" --organization=123456789012
gcloud projects create prod-payments-7f3a --folder=987654321098

Folders — Grouping Projects by Purpose#

Directly the same purpose as AWS OUs (AWS series, Part 1) — applying IAM bindings and Organization Policies (next section) to a whole GROUP of projects at once.

gcloud resource-manager folders list --organization=123456789012

# Move an existing project into a folder
gcloud projects move prod-payments-7f3a --folder=555566667777

Organization Policies — GCP's Guardrails#

The direct GCP analog of AWS Service Control Policies (AWS series, Part 1) — a genuinely important, precise parallel worth drawing.

Diagram
# A constraint restricting which regions resources can be
# created in — directly the GCP equivalent of the region-lock
# SCP already shown in the AWS series (Part 1)
cat <<'POLICY'
constraint: constraints/gcp.resourceLocations
listPolicy:
  allowedValues:
    - "in:us-locations"
    - "in:europe-west1-locations"
POLICY

gcloud resource-manager org-policies set-policy policy.yaml --project=prod-payments-7f3a

Why the SAME "guardrail, not a grant" precision already established for SCPs in the AWS series applies identically here, worth stating explicitly: an Organization Policy constraint restricting WHERE resources can be created doesn't grant anyone the ability to create resources at all — IAM still has to separately grant that permission; the constraint only bounds what's possible once IAM has already granted something.


A Realistic Multi-Project Resource Hierarchy#

Bringing the last several sections together, directly mirroring the AWS series' worked landing zone (Part 1).

Diagram

Why centralizing logs in a separate project matters just as much on GCP, worth restating precisely, directly reinforcing the same reasoning already established in the AWS series: a compromised workload project's own local logs could be tampered with by an attacker with sufficient access — shipping logs continuously to a separate project the workload project has no delete permission on guarantees a tamper-evident audit trail survives even a full project compromise.


Policy Inheritance — How Rules Flow Down the Hierarchy#

Worth a precise, dedicated explanation, since GCP's inheritance model has one genuinely important nuance AWS's SCP model doesn't share in quite the same way.

Diagram

Why IAM bindings are ADDITIVE down the hierarchy (never restrictive), worth stating precisely, a genuinely important, frequently-tested nuance: an IAM role granted at the Organization level applies to EVERY project underneath it automatically — there is no way for a project to "opt out" of an inherited IAM grant the way an SCP-style Deny could override something at a lower level. Only Organization Policy CONSTRAINTS (the previous section) can restrict what's possible at a lower level; IAM grants only ever ADD access as you move down the hierarchy, never remove it.


Google Cloud's Well-Architected Equivalent: The Architecture Framework#

The direct GCP analog of the AWS Well-Architected Framework's six pillars (AWS series, Part 1).

Diagram

Worth stating the same synthesizing point already made in the AWS series, applied here: every one of these six pillars maps onto a concept already covered elsewhere in this course — Reliability is the Reliability & Architecture Patterns and Disaster Recovery series; Security, Privacy, and Compliance is the DevSecOps series; Operational Excellence is SRE Fundamentals and Incident Management. Recognizing this instantly is exactly as strong an interview move for GCP as it was for AWS.


The Shared Responsibility Model on GCP#

Diagram

The identical shape already established in the AWS series (Part 1) — the line moves up the stack the more managed a service is (Compute Engine requires you to patch the guest OS; Cloud Run or Cloud Functions, Part 7, hand that responsibility entirely to Google) — worth stating that this pattern is provider-agnostic, not an AWS-specific quirk.


Billing Accounts — Decoupled From Projects#

A genuinely useful GCP-specific structural detail worth knowing precisely: a Billing Account is a SEPARATE resource from the Organization/Folder/Project hierarchy — one billing account can be linked to MANY projects across DIFFERENT parts of the resource hierarchy.

gcloud billing accounts list
gcloud billing projects link staging-project --billing-account=012345-6789AB-CDEF01

# Set a BUDGET with alerts — directly the GCP equivalent of
# AWS Budgets already covered in the AWS series (Part 12)
gcloud billing budgets create \
  --billing-account=012345-6789AB-CDEF01 \
  --display-name="Monthly Production Budget" \
  --budget-amount=50000USD \
  --threshold-rule=percent=0.8

Why this decoupling matters, worth stating explicitly: a large organization can maintain ONE central billing account for consolidated invoicing while still organizing projects freely across whatever Folder structure best fits their operational needs — billing structure and resource-organization structure are independent decisions on GCP, unlike AWS where consolidated billing is tied more directly to the Organizations hierarchy itself.


The gcloud CLI and Client Libraries#

# Configure the CLI (interactive)
gcloud init

# Verify current identity — the GCP equivalent of AWS's
# `aws sts get-caller-identity`, worth running before any
# risky command, exactly the same habit already established
# in the AWS series
gcloud auth list
gcloud config get-value project

# Every gcloud command follows roughly the same shape:
# gcloud <service> <resource> <verb>
gcloud compute instances list
gcloud storage ls

Worth stating the identical point already made in the AWS series: the Console, gcloud CLI, and client libraries are just different clients calling the SAME underlying GCP APIs — nothing the Console can do is fundamentally different from what Terraform's GCP provider (Part 11) can do, since both are just making the same API calls under the hood.


Labels — GCP's Version of Tags#

gcloud compute instances add-labels app-server-1 \
  --labels=environment=production,team=payments,cost-center=cc-1042

Worth stating the identical tagging discipline already established in the AWS series (Part 1), with GCP's own terminology: labels are the foundational input every cost-allocation report and blast-radius-aware automation depends on — skipping this "for now" is just as costly to retrofit later on GCP as it is on AWS.


Resource Manager Tags — A Newer, More Powerful Alternative#

Worth knowing as a genuinely distinct, newer GCP capability beyond simple labels: Resource Manager Tags are key-value pairs that — unlike labels — can be directly referenced in IAM conditions and Organization Policies, enabling attribute-based access control.

gcloud resource-manager tags keys create environment --parent=organizations/123456789012
gcloud resource-manager tags values create production --parent=123456789012/environment

gcloud resource-manager tags bindings create \
  --tag-value=123456789012/environment/production \
  --parent=//cloudresourcemanager.googleapis.com/projects/prod-payments-7f3a

Why this is worth distinguishing precisely from labels, a genuinely important nuance: labels are purely descriptive metadata (useful for cost reports and Resource Manager filtering) — Resource Manager Tags can be referenced directly INSIDE IAM Conditions and Organization Policies, enabling the same tag-based/attribute-based access control pattern already covered as ABAC in the AWS series (Part 2), now with GCP's own native mechanism.


A Full Worked Landing Zone Bootstrap#

# 1. Confirm the organization exists (created automatically
#    when Cloud Identity/Workspace is set up)
gcloud organizations list

# 2. Create the core folder structure
gcloud resource-manager folders create --display-name="Security" --organization=123456789012
gcloud resource-manager folders create --display-name="Infrastructure" --organization=123456789012
gcloud resource-manager folders create --display-name="Workloads" --organization=123456789012
gcloud resource-manager folders create --display-name="Sandbox" --organization=123456789012

# 3. Create the foundational projects
gcloud projects create log-archive-9f2a --folder=<security-folder-id>
gcloud projects create security-audit-3c1b --folder=<security-folder-id>
gcloud projects create shared-networking-8e4d --folder=<infra-folder-id>

# 4. Set a baseline Organization Policy at the ROOT
gcloud resource-manager org-policies set-policy region-lock.yaml --organization=123456789012

# 5. Enable Cloud Audit Logs org-wide, routed to the
#    log-archive project (Part 9 covers this in full depth)
gcloud logging sinks create org-audit-sink \
  storage.googleapis.com/log-archive-bucket \
  --organization=123456789012 --include-children

Why establishing guardrails and centralized logging BEFORE workload projects are populated matters just as much here as it did in the AWS series, worth stating explicitly: retrofitting governance after resources already exist is dramatically more painful than building it into the very first project created.


Part 1 CLI Cheat Sheet#

AreaCommandPurpose
Identitygcloud auth list / config get-value projectConfirm current identity and active project
Projectsgcloud projects createCreate a new project
Projectsgcloud projects moveMove a project into a folder
Foldersgcloud resource-manager folders createCreate a folder
Org Policygcloud resource-manager org-policies set-policyApply a constraint
Billinggcloud billing projects linkLink a project to a billing account
Billinggcloud billing budgets createSet a budget with alerts
Labelsgcloud <resource> add-labelsTag a resource
Tagsgcloud resource-manager tags bindings createBind an IAM-referenceable tag

APIs and Services — Explicit Enablement Required#

A genuinely important, GCP-specific operational detail worth knowing precisely: unlike AWS, where every service is always "available" the moment you have IAM permission to use it, every GCP API/service must be explicitly ENABLED on a per-project basis before it can be used at all — even with full IAM permissions granted.

# Enable the Compute Engine API for a project — without this,
# EVERY gcloud compute command fails with a clear
# "API not enabled" error, regardless of IAM permissions
gcloud services enable compute.googleapis.com --project=prod-payments-7f3a

# List currently enabled APIs
gcloud services list --enabled --project=prod-payments-7f3a

# Disable an API no longer needed — directly reduces the
# project's overall attack surface
gcloud services disable unused-api.googleapis.com --project=prod-payments-7f3a

Why this is worth treating as a genuinely useful SECURITY control, not just an operational speed bump, worth stating explicitly: a new project starts with almost nothing enabled — meaning even a fully-privileged IAM principal cannot accidentally (or maliciously) spin up a resource type the project owner never intended to allow, simply because the underlying API was never turned on. This is a real, structural layer of defense-in-depth that AWS's "everything is always available if IAM allows it" model doesn't have an equivalent for.


Quotas — GCP's Version of Service Quotas#

Directly the same concept already covered for AWS Service Quotas (AWS series, Part 1) — every GCP project has default limits on nearly everything, and hitting one unexpectedly is just as real and avoidable a failure mode here as it was there.

# Check current quota usage for a specific service/region
gcloud compute regions describe us-central1 --project=prod-payments-7f3a \
  --format="table(quotas.metric,quotas.limit,quotas.usage)"

# Request a quota increase proactively, before a launch —
# exactly the same pre-launch capacity-planning discipline
# already established in the AWS series
gcloud alpha services quota update \
  --service=compute.googleapis.com \
  --consumer=projects/prod-payments-7f3a \
  --metric=compute.googleapis.com/cpus \
  --value=500 --unit=1/min/{project}

Why this deserves the same weight already given to Service Quotas in the AWS series, worth stating explicitly: a sudden autoscaling event (Part 3) hitting a project's CPU quota fails silently from the application's perspective — new instances simply don't launch — producing a symptom that looks identical to an unrelated outage until someone specifically checks quota usage.


Essential Contacts — Making Sure Google Can Reach You#

A small, easy-to-overlook GCP feature worth knowing about: Essential Contacts lets an organization specify WHO Google should notify for specific categories of communication (security, billing, technical issues) — separate from whoever happens to be the project owner at any given moment.

gcloud essential-contacts create \
  --email=security-team@example.com \
  --notification-categories=SECURITY \
  --organization=123456789012

Why this matters, worth stating explicitly, directly connecting to the on-call/escalation discipline from the Incident Management series: without it, a critical security notification from Google (e.g. about a compromised credential detected on Google's end) might land in the inbox of an individual who left the company months ago, rather than reaching an actual, current, monitored team distribution list.


Common Mistakes#

MistakeWhy It's WrongFix
Running every workload in a single GCP projectNo real isolation — identical risk already established for AWS's single-account trapAdopt a multi-project structure via the resource hierarchy from the start
Assuming an Organization Policy constraint grants a permissionConstraints only set a ceiling — IAM still has to separately grant the actual permissionAlways pair a constraint with an actual IAM binding
Assuming IAM bindings can be "opted out of" at a lower folder/project levelIAM grants are strictly additive down the hierarchy — only Org Policy constraints can restrictGrant broad IAM only at the level genuinely appropriate; don't rely on lower levels restricting it
Improvising a Project ID at creation timeProject IDs are permanent and globally unique — a poor choice can't be renamed laterAdopt a deliberate ID-naming convention before creating projects
Skipping label/tag discipline "for now"Makes cost attribution and IAM-condition-based access control impossible to retrofit cleanly laterEnforce a minimum label set from the first resource created

Worked Practice Problems#

Problem 1: A platform team grants a broad IAM role (roles/editor) at the Organization level, intending it only for a specific sandbox folder's experimental projects. Months later, a security review finds this same broad role effectively applies to every production project in the organization too. What's the root cause, and what should have been done instead?

Answer: The root cause is a misunderstanding of GCP's IAM inheritance model — IAM bindings are strictly additive down the entire resource hierarchy, meaning a role granted at the Organization level automatically applies to EVERY folder and project beneath it, including production, with no way for a lower level to "opt out" of an inherited grant. The correct approach is granting the broad role at the SPECIFIC folder level it was actually intended for (the sandbox folder), not at the Organization root — IAM should always be granted at the narrowest level that satisfies the actual need, precisely because there's no restrictive override available further down the hierarchy the way an SCP-style Deny would provide on AWS.

Problem 2: A finance team wants centralized, single-invoice billing across an organization's 30 GCP projects, but different teams want to organize those same projects into folders based on their own operational structure, which doesn't align with any billing-related grouping. Is this achievable on GCP, and how?

Answer: Yes — this is precisely what GCP's decoupling of Billing Accounts from the resource hierarchy enables. A single Billing Account can be linked to all 30 projects for consolidated invoicing, completely independent of how those same projects are organized into folders for IAM and operational purposes. Each team can structure their folder hierarchy however best fits their operational needs, while finance still gets one centralized bill — the two concerns (billing consolidation and resource organization) are genuinely independent decisions on GCP, unlike AWS where consolidated billing is more directly tied to the Organizations hierarchy itself.

Problem 3: A security team wants to grant a specific automation service account access to any resource tagged environment=production, without needing to update the IAM policy every time a new production resource is created. Would GCP labels or Resource Manager Tags be the right tool here, and why?

Answer: Resource Manager Tags, not labels. Labels are purely descriptive metadata — useful for cost reports and resource filtering, but they cannot be referenced inside an IAM Condition, meaning an IAM policy cannot dynamically grant access based on a label's value. Resource Manager Tags CAN be referenced directly inside IAM Conditions, enabling exactly the attribute-based access control pattern this scenario needs — a single IAM binding conditioned on the environment=production tag automatically applies to any current OR future resource carrying that tag, with zero need to update the policy as new production resources are created, directly the same tag-based ABAC benefit already established generically in the AWS series (Part 2).

Problem 4: A developer with a fully-privileged IAM role on a brand-new GCP project reports they cannot launch a Compute Engine instance, receiving an error unrelated to permissions. What's the most likely cause, and how does this differ from the equivalent situation on AWS?

Answer: The most likely cause is that the Compute Engine API has never been explicitly enabled on this new project — GCP requires every API/service to be explicitly turned on per-project before it can be used at all, regardless of how privileged the calling principal's IAM role is. This is a genuine structural difference from AWS, where an IAM-permitted principal can call any AWS service the moment permission is granted, with no separate "enable this service for the project" step required. The fix is enabling the Compute Engine API (gcloud services enable compute.googleapis.com) — and worth noting as a positive: this per-project enablement requirement is itself a real, additional layer of defense-in-depth, since even a fully-privileged principal can't provision a resource type the project owner never intentionally enabled.


Summary and What's Next#

  • GCP's VPC is global by default, unlike AWS's inherently regional VPC — a foundational geography difference that shapes Part 4's networking discussion.
  • The Project is GCP's real isolation boundary, directly analogous to an AWS Account — a single shared project for everything is just as much a trap as it is on AWS.
  • The resource hierarchy (Organization → Folders → Projects) directly mirrors AWS's Organization → OUs → Accounts, with Organization Policies as the precise GCP analog of SCPs — a guardrail, never a grant.
  • IAM bindings are strictly additive down the hierarchy — a genuinely important nuance without a direct SCP-style restrictive override at lower levels.
  • Billing Accounts are decoupled from the resource hierarchy — one billing account can span many projects organized however best fits operational needs.
  • Labels are GCP's tagging equivalent; Resource Manager Tags are a newer, IAM-Condition-referenceable alternative enabling genuine attribute-based access control.

Continue to Part 2 (02-iam-and-identity.md) for a deep dive into GCP IAM — the permission system governing everything inside a project, including its genuinely distinctive service account model.