# GCP Fundamentals

> Google Cloud essentials: projects as the unit of organization, the core compute and storage services, and IAM - with a quick map from AWS terms.


---

# GCP Fundamentals

You opened the Google Cloud console and the left-hand menu kept scrolling. A hundred products, half of them with names that tell you nothing - Dataflow, Pub/Sub, Spanner, Anthos. You wanted a server and a place to put files, and now you're staring at a sidebar that looks like an airport departure board. The fear is real: pick wrong, get a surprise bill, or wire up something you can't undo.

Here's the relief. You don't need a hundred products. You need one organizing idea - the **project** - and about six services. Once those click, the rest of the console is more of the same shape, and you can ignore it until you actually need it. This guide gives you the mental model first, then the everyday services, then the parts that bite people in production.

## How to read this

Read the phases in order. Phase 1 is the map - the project, what it is, and why everything hangs off it. Don't skip it; the whole console makes sense or doesn't make sense based on this one idea. Phase 2 is the working core: the services you'll actually touch and how they fit together. Phase 3 is the reality check - IAM done right, billing surprises, and the AWS-to-GCP translation table for when your brain keeps reaching for the term it already knows.

If you've used another cloud before, the term map in Phase 3 will save you the most time. If you're brand new, start clean at Phase 1 and let the AWS comparisons wash over you.

For the bigger picture of how the major clouds compare and which one to reach for, see /guides/cloud-platforms-explained.

## The phases

1. [The project is the unit of everything](01-the-project-is-the-unit.md) - the mental model: resource hierarchy, projects, and why GCP organizes the way it does.
2. [The services you'll actually use](02-the-services-you-use.md) - Compute Engine, Cloud Storage, Cloud SQL, Cloud Run, Cloud Functions, and BigQuery.
3. [IAM, billing, and the AWS map](03-iam-billing-and-the-aws-map.md) - roles and service accounts done safely, where the bills come from, and the term-for-term translation.


---

# The project is the unit of everything

Before you spin up a single server, there's one idea that decides whether GCP feels organized or chaotic: the **project**. Every virtual machine, every storage bucket, every database, every API key, every dollar of billing - all of it lives inside a project. There is no "loose" resource floating outside one. Get this, and the console stops being scary.

Think of a project as a labeled box. Everything you create goes in a box. Boxes have their own bill, their own access rules, and their own enabled features. You can have many boxes - one for your side project, one for a client, one for "experiments I'll delete next week" - and they don't leak into each other. Delete the box, and everything inside it goes too. That last part is a feature: a project is the cleanest delete button in the cloud.

## A project has three names, and that trips everyone up

When you create a project, GCP asks for a display name and generates an ID. People conflate them and then can't figure out why a command fails. There are three identifiers:

```text
Project name:    "My Side Project"      ← human label, you can change it
Project ID:      my-side-project-481923 ← globally unique, permanent, used in commands
Project number:  738201947562           ← auto-assigned integer, used by some APIs
```

*What just happened:* you saw the three IDs side by side. The **Project ID** is the one you'll type all day - it's globally unique across all of Google Cloud, so GCP often appends random digits to keep it unique, and you can never change it after creation. Pick it deliberately.

When a command or config asks for a project, it almost always wants the **ID**, not the name:

```bash
# Set the active project for every gcloud command that follows
gcloud config set project my-side-project-481923

# Confirm what you're pointed at
gcloud config get-value project
# → my-side-project-481923
```

*What just happened:* you told the `gcloud` CLI which box to operate in. Every resource you create from here lands in that project until you change it. Running a `create` command without knowing your active project is how people accidentally build things in the wrong box.

## Why projects? Because the alternative is a mess

Other clouds let you pile resources into one big account and sort them out later with tags. That works until it doesn't - your dev experiment and your production database share a billing total, share permission boundaries, and one fat-fingered delete can hit the wrong thing.

GCP made the boundary structural instead of optional. Because billing, IAM, and enabled APIs all attach to the project, isolation is the default, not something you remember to configure. Want to give a contractor access to one app and nothing else? Put that app in its own project and grant them access to that project. Done - there's no way for them to wander into your other work.

## The hierarchy above the project

Projects don't float in a vacuum. In a company, they sit inside a tree:

```text
Organization (your-company.com)
└── Folder: Engineering
    ├── Folder: Production
    │   ├── Project: web-prod
    │   └── Project: data-prod
    └── Folder: Staging
        └── Project: web-staging
```

*What just happened:* you saw the resource hierarchy. The **Organization** maps to your company's domain. **Folders** group projects (by team, environment, whatever). **Projects** hold the actual resources. The point of the tree: permissions and policies set high up flow **downward**. Grant someone a role at the Engineering folder, and it applies to every project beneath it. Set a policy on the Organization, and it covers everything.

If you're an individual with a personal Google account, you skip the Organization and Folders entirely - you have projects directly under your account. That's completely normal and everything in this guide still applies.

> **The big idea:** A resource inherits permissions from its project, its folder, and its organization, top to bottom. When you're confused about why someone can (or can't) access something, walk up the tree. The grant is almost always at a level above where you were looking.

## For builders

The practical habit that pays off forever: **one project per environment**, never one project for everything. A `myapp-dev`, a `myapp-prod`, and maybe a `myapp-staging`. It costs nothing to have extra projects, and it means a mistake in dev can't physically reach prod - different box, different billing line, different access list. When you eventually automate deploys, your scripts target a project ID, and swapping `-dev` for `-prod` is the whole difference between environments.

```quiz
[
  {
    "q": "Which project identifier is globally unique, permanent, and what most gcloud commands expect?",
    "choices": ["Project name", "Project ID", "Project number", "Billing account ID"],
    "answer": 1,
    "explain": "The Project ID is globally unique and cannot be changed after creation; the name is a mutable human label and the number is an auto-assigned integer."
  },
  {
    "q": "In the GCP resource hierarchy, how do permissions and policies flow?",
    "choices": ["Upward, from project to organization", "Downward, from organization/folder to project", "Sideways between sibling projects", "They don't inherit; each resource is independent"],
    "answer": 1,
    "explain": "Roles and policies set at the organization or folder level are inherited by everything beneath them, so isolation and access are managed top-down."
  },
  {
    "q": "What is the recommended way to isolate a development environment from production in GCP?",
    "choices": ["Use tags on resources in one shared project", "Put each environment in its own project", "Use one project but separate regions", "Use one project with different service accounts"],
    "answer": 1,
    "explain": "Because billing, IAM, and enabled APIs attach to the project, one project per environment gives structural isolation a mistake in dev cannot cross."
  }
]
```


---

# The services you'll actually use

The console lists a hundred products. You need six. Everything else is a specialized variation you can learn the day a real need shows up. This phase walks the core six in the order most people meet them: a server, a place for files, a managed database, two ways to run code without managing a server, and the data warehouse that GCP is quietly famous for.

The shape to hold in your head: GCP gives you a **ladder of how much you manage**. At the bottom, you run a whole virtual machine and own everything on it. At the top, you hand Google a function and never think about servers at all. Pick the rung that matches how much control you actually need - lower is more control and more chores, higher is less of both.

## Compute Engine - a virtual machine you own

This is the bottom rung: a plain virtual server. You pick the CPU, RAM, and operating system, and you get a Linux or Windows box you SSH into and treat like any other machine.

```bash
# Create a small Linux VM in a specific zone
gcloud compute instances create web-1 \
  --zone=us-central1-a \
  --machine-type=e2-small \
  --image-family=debian-12 \
  --image-project=debian-cloud

# SSH straight in - gcloud handles the keys for you
gcloud compute ssh web-1 --zone=us-central1-a
```

*What just happened:* you created a VM named `web-1` and connected to it. Note the **zone** (`us-central1-a`): GCP splits the world into regions (a geographic area) and zones (an isolated data center within a region). A VM lives in exactly one zone. The `e2-small` is a **machine type** - a preset bundle of CPU and memory. The catch with Compute Engine: it bills while it's on whether or not anyone's using it, and patching, scaling, and uptime are your job.

Reach for Compute Engine when you need a full OS, custom software that won't run in a container, or a lift-and-shift of something that already expects a normal server.

## Cloud Storage - a place for files (buckets)

Object storage. You put files ("objects") into **buckets**, and Google keeps them durable and reachable over HTTP. This is where images, backups, build artifacts, and static website files go. It is not a filesystem and not a disk - it's a giant, addressable key-value store for blobs.

```bash
# Create a bucket (the name must be globally unique across all of GCP)
gcloud storage buckets create gs://my-app-uploads-481923 \
  --location=us-central1

# Copy a file up, then list what's in the bucket
gcloud storage cp ./logo.png gs://my-app-uploads-481923/
gcloud storage ls gs://my-app-uploads-481923/
# → gs://my-app-uploads-481923/logo.png
```

*What just happened:* you created a bucket and uploaded a file to it. The `gs://` prefix is how every tool refers to Cloud Storage paths. Bucket names share one global namespace - like project IDs, yours must be unique across the entire planet, which is why people append a project number or random suffix. You pay for what you store plus what you download out ("egress"); uploads are free.

## Cloud SQL - a managed relational database

You want PostgreSQL or MySQL, but you don't want to be the one applying security patches, configuring backups, and babysitting replication at 3am. Cloud SQL runs the database engine for you. You still get a normal Postgres or MySQL you connect to with normal tools - Google handles the operations underneath.

```bash
# Create a small PostgreSQL instance
gcloud sql instances create app-db \
  --database-version=POSTGRES_16 \
  --tier=db-f1-micro \
  --region=us-central1
```

*What just happened:* you stood up a managed Postgres instance. Backups, patching, and failover are now Google's responsibility. The trade: less control over the exact server config, and it's pricier than running the same database yourself on a raw VM - you're paying for not having to do the operations work. For most teams that's the right trade, because database operations is exactly the work that quietly eats weekends.

## Cloud Run - your container, no server to manage

Now we climb the ladder. You have an app in a container. You don't want to manage a VM to run it. Cloud Run takes your container image, runs it on demand, scales it up when traffic arrives, and scales it to **zero** when nobody's calling - so an idle service costs nothing.

```bash
# Deploy a container image straight to a public URL
gcloud run deploy my-api \
  --image=us-docker.pkg.dev/my-project/repo/my-api:latest \
  --region=us-central1 \
  --allow-unauthenticated
# → Service URL: https://my-api-xxxxxxxxxx-uc.a.run.app
```

*What just happened:* you deployed a container and got back a live HTTPS URL, with autoscaling and TLS handled for you. The key behavior is **scale-to-zero**: if your service gets no traffic, you run no instances and pay nothing for compute. The first request after idle has to start a container ("cold start"), which adds a little latency. Cloud Run is the sweet spot for most web APIs and backends today - container flexibility without VM chores.

## Cloud Functions - a single function on a trigger

The top rung. You don't even bring a container - you bring one function, and Google runs it in response to an event: an HTTP request, a file landing in a bucket, a message on a queue. Pure event-driven glue.

```bash
# Deploy an HTTP-triggered function from the current directory
gcloud functions deploy hello \
  --runtime=python312 \
  --trigger-http \
  --region=us-central1 \
  --entry-point=hello
```

*What just happened:* you deployed a single function reachable over HTTP, with no server and no container to think about. Cloud Functions shines for small, single-purpose reactions - "when a file is uploaded, generate a thumbnail" or "when this webhook fires, write a row." For anything with real structure, you'll usually outgrow it and move to Cloud Run; the line between them is mostly "one function" versus "a whole app."

## BigQuery - the data warehouse GCP is known for

This one is different in kind, and it's the service that made Google Cloud's reputation. BigQuery is a **serverless data warehouse**: you load enormous tables - billions of rows - and run plain SQL across all of them in seconds, with no servers, no indexes, no cluster to size. You don't provision anything; you query, and Google throws as much compute at it as the query needs.

```sql
-- Query a public dataset: top 5 names in the US name records
SELECT name, SUM(number) AS total
FROM `bigquery-public-data.usa_names.usa_1910_current`
GROUP BY name
ORDER BY total DESC
LIMIT 5;
```

*What just happened:* you ran standard SQL over a multi-billion-row public dataset and got an answer fast, without managing a single machine. BigQuery bills mainly by **how much data your query scans**, not by time - so `SELECT *` over a huge table is the expensive mistake; selecting only the columns you need is the cheap habit. This pay-per-scan model is why analytics teams gravitate to GCP.

## How they fit together

```mermaid
graph TD
  U[User] --> R[Cloud Run API]
  U --> F[Cloud Function: thumbnailer]
  R --> DB[(Cloud SQL)]
  R --> GCS[Cloud Storage bucket]
  F --> GCS
  GCS --> BQ[(BigQuery: analytics)]
  R -.heavy compute.-> CE[Compute Engine VM]
```

*What just happened:* a realistic small system. Users hit a Cloud Run API that reads and writes Cloud SQL and stores uploads in a bucket; a Cloud Function reacts to those uploads; data flows into BigQuery for analytics; and a Compute Engine VM stands ready for any workload that needs a full machine. You rarely use one service alone - they compose.

## In the wild

The instinct for newcomers is to default to Compute Engine because a VM feels familiar. Resist it. Each VM is a machine you now have to patch, monitor, and keep alive. The lazy-in-the-good-sense move is to climb as high up the ladder as your workload allows: a stateless web service goes on Cloud Run, a one-shot reaction goes in a Cloud Function, and you only drop down to a VM when something genuinely needs a full operating system. Less to manage means less that can page you.

```quiz
[
  {
    "q": "Which service runs your container, scales automatically, and scales to zero so an idle service costs nothing for compute?",
    "choices": ["Compute Engine", "Cloud Run", "Cloud SQL", "BigQuery"],
    "answer": 1,
    "explain": "Cloud Run runs container images on demand, autoscales with traffic, and scales to zero when idle so you pay nothing for compute while no requests arrive."
  },
  {
    "q": "BigQuery's main cost driver for a query is:",
    "choices": ["How long the query runs", "How many servers you provisioned", "How much data the query scans", "The number of rows returned"],
    "answer": 2,
    "explain": "BigQuery bills mainly by bytes scanned, which is why selecting only needed columns is far cheaper than SELECT * over a large table."
  },
  {
    "q": "You need to run legacy software that expects a full Linux operating system. Which service fits best?",
    "choices": ["Cloud Functions", "Cloud Run", "Compute Engine", "Cloud Storage"],
    "answer": 2,
    "explain": "Compute Engine gives you a full virtual machine with an OS you control, which is what lift-and-shift of legacy software needs."
  }
]
```


---

# IAM, billing, and the AWS map

This is the phase where theory meets the parts that actually hurt: who can do what (IAM), where the money goes (billing), and the term-for-term translation if your brain already speaks AWS. These are the three things that surprise people in production - an over-permissioned service account, a bill nobody predicted, and a word that means something different than you assumed.

## IAM: who can do what, on which resource

GCP's access model is one sentence: **a member has a role on a resource.** That's the whole grammar. Pin those three words down and IAM stops being mysterious.

- **Member** - the *who*. A user (`alice@example.com`), a group, or a **service account** (a robot identity for code).
- **Role** - the *what*. A bundle of permissions, like "can read storage" or "can administer Cloud SQL."
- **Resource** - the *where*. A project, a folder, the whole org, or sometimes a single bucket.

```bash
# Grant Alice the role to view (not change) everything in a project
gcloud projects add-iam-policy-binding my-project \
  --member="user:alice@example.com" \
  --role="roles/viewer"
```

*What just happened:* you bound a member to a role on a resource. That triple - member, role, resource - is called a **policy binding**, and it's the atomic unit of all GCP access. Note this grant was at the **project** level, so it covers every resource in the project. Remember Phase 1: grant it on a folder instead, and it would flow down to every project inside.

### The three flavors of role

```text
Basic roles:       Owner / Editor / Viewer   ← broad, blunt, project-wide
Predefined roles:  roles/storage.objectAdmin ← curated per-service, scoped
Custom roles:      you pick the exact permissions ← when predefined is too much
```

*What just happened:* you saw the role ladder from blunt to precise. **Basic** roles (Owner/Editor/Viewer) are the tempting default and the classic mistake - `Editor` can change almost anything in the project. **Predefined** roles are the right daily choice: hundreds of them, each scoped to one service and one job. **Custom** roles exist for when even predefined is broader than you want. The rule that keeps you safe: grant the narrowest role that gets the job done, never `Owner` "to keep things simple."

### Service accounts: the part people get wrong

A **service account** is an identity for code, not a person - your Cloud Run service, your VM, your CI pipeline all act *as* a service account. Two habits separate the safe from the breached:

1. **Give each workload its own service account with only the roles it needs.** If your thumbnail function only reads and writes one bucket, it gets exactly that - not `Editor`. When something is compromised, the blast radius is whatever that one account could do.
2. **Avoid downloadable service-account key files.** A JSON key is a long-lived password that doesn't expire and gets leaked into git history. On GCP, your code usually doesn't need one: a Cloud Run service or VM automatically *is* its attached service account, no key file involved.

> **The mistake that ends up in the postmortem:** giving a workload `Owner` or `Editor` because it was faster than figuring out the right predefined role, then exporting a key file "for local testing." Least privilege plus no key files is the difference between an incident and a non-event.

## Billing: where the money actually goes

Billing in GCP attaches to a **billing account**, which is linked to one or more projects. The bill is the project's; the payment method is the billing account's. Two things catch newcomers:

```text
Most common surprise charges:
  • Network egress  - data leaving GCP costs money; data in is usually free
  • Idle VMs        - Compute Engine bills while running, used or not
  • BigQuery scans  - SELECT * over a huge table can cost real money per run
  • Forgotten resources - a load balancer or static IP you stopped using
```

*What just happened:* you saw the usual culprits. The pattern behind all of them: **GCP bills for what's allocated, not only what's actively serving traffic.** A stopped-but-not-deleted VM with a disk still costs. The defense is cheap and built in - set a **budget alert** so an email lands when spend crosses a threshold you choose:

```bash
# List your billing accounts to find the one to attach a budget to
gcloud billing accounts list
# → ACCOUNT_ID            NAME                OPEN
#   0X0X0X-0X0X0X-0X0X0X  My Billing Account  True
```

*What just happened:* you found your billing account ID. From there you create a budget (in the console or `gcloud billing budgets`) that emails you at, say, 50% and 100% of a monthly cap. A budget alert doesn't stop spending - it warns you - but that warning is what turns a four-figure surprise into a same-day fix.

## The AWS → GCP term map

If you came from AWS, half your confusion is vocabulary. Here's the translation for the services and concepts in this guide:

```text
AWS                              GCP
------------------------------   --------------------------------
Account                          Project
Organizations / OUs              Organization / Folders
IAM user / role                  Member / Role (+ Service Account)
EC2                              Compute Engine
S3                              Cloud Storage (buckets)
RDS                             Cloud SQL
Lambda                          Cloud Functions
Fargate / App Runner            Cloud Run
Redshift / Athena              BigQuery
Availability Zone               Zone
Region                          Region
VPC                             VPC
CloudWatch                      Cloud Monitoring / Logging
```

*What just happened:* you got the cheat sheet. The one that reshapes your thinking is the first row: an **AWS account ≈ a GCP project**. In AWS, an account is a heavyweight thing you create sparingly; in GCP, projects are cheap and disposable, so you make many. That's why the GCP advice is "one project per environment" where the AWS instinct was "one account, separated by tags." Same isolation goal, different default-sized unit.

The other classic stumble: in AWS, **Redshift** and **Athena** are two different products (a cluster you size versus serverless query-on-S3). In GCP, **BigQuery** covers both jobs - it's serverless like Athena but a full warehouse like Redshift. If you're hunting the GCP "Redshift," stop; it's BigQuery.

## In the wild

When you land on a new GCP project, three questions tell you almost everything about its health: Who has Owner or Editor on it (run `gcloud projects get-iam-policy`)? Are there budget alerts? And are workloads using attached service accounts or leaked key files? Those three checks catch the overwhelming majority of "how did this happen" incidents - over-broad access, runaway spend, and stale credentials. For a wider comparison of GCP against the other major clouds and how to choose between them, see /guides/cloud-platforms-explained.

```quiz
[
  {
    "q": "What is the single-sentence grammar of GCP IAM?",
    "choices": ["A resource owns a member's role", "A member has a role on a resource", "A role contains members and resources", "A project grants policies to roles"],
    "answer": 1,
    "explain": "Every IAM grant is a binding of a member (who) to a role (what) on a resource (where) - that triple is the atomic unit of access."
  },
  {
    "q": "Which practice best limits damage if a workload's identity is compromised?",
    "choices": ["Give every workload the Editor role for convenience", "Use one shared service account across all workloads", "Give each workload its own service account with only the roles it needs", "Download a JSON key file for every workload"],
    "answer": 2,
    "explain": "Per-workload service accounts with least-privilege roles keep the blast radius to what that one account could do; avoiding key files removes a long-lived leakable credential."
  },
  {
    "q": "An engineer from AWS is looking for GCP's equivalent of an EC2 instance and an S3 bucket. Which pair is correct?",
    "choices": ["Cloud Run and BigQuery", "Compute Engine and Cloud Storage", "Cloud Functions and Cloud SQL", "Cloud SQL and Cloud Storage"],
    "answer": 1,
    "explain": "EC2 maps to Compute Engine (virtual machines) and S3 maps to Cloud Storage (object buckets)."
  }
]
```
