02 · IAM & Security Basics¶
Identity and Access Management (IAM) controls who (identity) can do what (role/permissions) on which resource. Every action you've taken so far — creating a project, enabling an API — was already authorized by IAM behind the scenes. This module makes that explicit so you can grant access safely instead of by trial and error.
The three pieces of every IAM policy¶
An IAM binding is always: member + role + resource.
- Member — a Google account, service account, Google group, or
Workspace/Cloud Identity domain. Members are identified by an email-like
string, e.g.
user:you@example.comorserviceAccount:my-sa@project.iam.gserviceaccount.com. - Role — a named bundle of permissions, e.g.
roles/storage.objectViewerbundlesstorage.objects.getandstorage.objects.list. - Resource — the project, folder, organization, or individual resource (like one bucket) the binding applies to. IAM policies are inherited downward: a role granted at the project level applies to every resource inside it.
# Grant a user the "Viewer" role on the whole project
gcloud projects add-iam-policy-binding gcp-mastery-path-123 \
--member="user:teammate@example.com" \
--role="roles/viewer"
# See the current policy
gcloud projects get-iam-policy gcp-mastery-path-123
Role types¶
| Type | Example | Notes |
|---|---|---|
| Basic | roles/owner, roles/editor, roles/viewer |
Broad, project-wide. Convenient but coarse — avoid in anything beyond a personal sandbox. |
| Predefined | roles/storage.objectViewer, roles/compute.instanceAdmin.v1 |
Scoped to one service, curated by Google. The default choice for real work. |
| Custom | roles/myCustomDeployer |
You define the exact permission list. Use when predefined roles are too broad or too narrow. |
List roles and inspect what a role actually grants:
gcloud iam roles list --filter="name:roles/storage"
gcloud iam roles describe roles/storage.objectViewer
# includedPermissions:
# - storage.objects.get
# - storage.objects.list
Principle of least privilege¶
Grant the narrowest role, on the narrowest resource, for the shortest time that gets the job done. In practice:
- Prefer a predefined role scoped to one service over
roles/editor. - Prefer binding at the resource level (one bucket, one instance) over the project level when the access is genuinely that narrow.
- Prefer granting to a group over individual users when more than one or two people need the same access — you manage membership once instead of editing IAM policy per person.
- Review who has
roles/ownerperiodically — it includes the ability to change IAM policy itself, so it should be held by very few identities.
# Narrow: viewer on just one bucket, not the whole project
gsutil iam ch user:teammate@example.com:objectViewer \
gs://gcp-mastery-path-123-assets
Service accounts¶
A service account is an identity for workloads, not humans — your Compute Engine VM, Cloud Function, or a CI pipeline authenticates as one instead of as a person's Google login. Every project gets a default Compute Engine service account, but creating dedicated, narrowly-scoped ones per workload is the recommended practice.
# Create a dedicated service account for an app
gcloud iam service-accounts create app-runtime \
--display-name="App Runtime Service Account"
# Grant it only what it needs
gcloud projects add-iam-policy-binding gcp-mastery-path-123 \
--member="serviceAccount:app-runtime@gcp-mastery-path-123.iam.gserviceaccount.com" \
--role="roles/storage.objectViewer"
# Generate a key file for local/offsite use (avoid this when a
# metadata-server-based identity is available, e.g. on Compute Engine, Cloud
# Run, or GKE workload identity -- keys are a standing secret to manage)
gcloud iam service-accounts keys create key.json \
--iam-account=app-runtime@gcp-mastery-path-123.iam.gserviceaccount.com
Service account keys are long-lived secrets
A downloaded JSON key works until you explicitly revoke it — there's no
built-in expiry. Prefer attaching a service account directly to a
Compute Engine VM, Cloud Run service, or Cloud Function (no key file
needed at all) whenever the workload runs on GCP itself. Only create key
files for genuinely external systems, and rotate/delete them when done:
gcloud iam service-accounts keys delete <KEY_ID> --iam-account=....
Checking "who can do what"¶
Two directions matter: what can this identity do, and who can do this thing.
# What roles does a member have, project-wide?
gcloud projects get-iam-policy gcp-mastery-path-123 \
--flatten="bindings[].members" \
--filter="bindings.members:teammate@example.com" \
--format="table(bindings.role)"
# Would a specific member be allowed a specific permission? (Policy Simulator
# in the console gives a fuller picture; this checks granted roles directly.)
gcloud iam roles describe roles/storage.objectViewer --format="value(includedPermissions)"
Cheat sheet¶
| Command | Purpose |
|---|---|
gcloud projects add-iam-policy-binding |
Grant a role to a member on a project. |
gcloud projects remove-iam-policy-binding |
Revoke a role from a member. |
gcloud projects get-iam-policy |
View the current IAM policy. |
gcloud iam roles list |
List available (predefined/custom) roles. |
gcloud iam roles describe <role> |
See exactly what permissions a role grants. |
gcloud iam service-accounts create |
Create a new service account. |
gcloud iam service-accounts keys create |
Generate a key file for a service account (use sparingly). |
gcloud iam service-accounts keys delete |
Revoke a service account key. |
How It Actually Works¶
Every IAM check GCP performs evaluates the same abstraction: a policy
is a list of bindings, each pairing a role (a bundle of permissions) with
a set of members, attached to a resource. Resources form a hierarchy —
Organization → Folder → Project → individual resource — and policies are
additive down the tree: a role granted at the Organization node is
inherited by every folder, project, and resource beneath it, and there is
no way to revoke an inherited grant lower down (only Deny policies, a
separate mechanism, can override an inherited Allow). This is why the
"who can do what" question is really a union: gcloud projects
get-iam-policy only shows bindings set at that node, not the inherited
ones from folders/org above it, which is the single most common cause of
"but I never granted that role" surprises. Under the hood, every API call
hits a common Checker service that walks the resource hierarchy
upward, unions every applicable binding at every ancestor node, and grants
access if the union contains the requested permission anywhere in the
chain — deterministic, but hierarchy-wide, not resource-local.
Exercise¶
Create a service account named reporting-bot. Grant it
roles/storage.objectViewer scoped to a single bucket you own (not the
whole project) using gsutil iam ch. Then run
gcloud projects get-iam-policy and confirm reporting-bot does not
appear with any project-wide role — only the bucket-level grant should
exist. Finally, list and then delete any key you created for it.