Open a terminal, type gcloud, and Google Cloud’s entire surface area is suddenly one command away — every VM, bucket, database, IAM grant and billing report, all reachable without ever opening a browser. That is the promise. The reality, for most newcomers, is a wall: hundreds of subcommands, a sprawling man page, cryptic flags, and an authentication model that fails in ways the error message doesn’t explain. People learn three commands by copy-paste, never understand why they work, and stay slow forever.
This quickstart fixes that. The secret nobody tells you is that gcloud is not hundreds of commands — it’s one predictable sentence pattern with a small, reusable vocabulary. Once you see the pattern (gcloud GROUP SUBGROUP VERB NAME --flags), every command in the tool becomes guessable, and the twenty or so you actually use every day fit on a single screen. We will lock down that mental model first, then walk the daily-driver commands one verb at a time, untangle the auth-and-config setup that causes 90% of “it worked yesterday” tickets, and finish with a hands-on lab where you spin up a real (free-tier) server and tear it down — all from the command line.
By the end you will not be memorising commands; you will be composing them — you will know the difference between gcloud auth login and gcloud auth application-default login (and why mixing them up wastes an afternoon), how named configurations flip you between projects in one keystroke, and how --format and --filter quietly turn gcloud into a data-extraction tool. This is the cheat sheet I wish someone had handed me on day one.
What problem this solves
The Console is wonderful for learning and one-off clicks, but it has a hard ceiling: it is not repeatable, not scriptable, not reviewable. The moment a task happens more than once — create five VMs, grant the same role across ten projects, wire a step into CI — clicking becomes a liability. You cannot diff a click, paste a screenshot into a runbook, or put a mouse movement in version control.
What breaks without CLI fluency is speed and correctness at scale. A Console-only engineer spends ten minutes navigating menus to do what gcloud does in one line, makes inconsistent changes (this VM got the right tag, that one didn’t), and produces nothing anyone can audit. And when they do reach for the CLI under pressure, they hit the authentication wall — “your default credentials were not found,” or a command runs against the wrong project — and retreat to the Console, reinforcing the slowness. It bites everyone who operates GCP beyond a toy project, hardest on people from a pure-Console background, anyone juggling multiple projects (where “wrong project” is a daily hazard), and first-time scripters (where the human-login model silently doesn’t apply). The fix is not “memorise more commands” — it is “understand the four-part sentence, the two logins, and the configuration that pins your context,” after which the commands write themselves. A confident user keeps a tiny, finite set in their head: one command shape, ~6 verbs, ~6 service groups, ~5 global flags, and the two logins — that is genuinely enough to cover the day, with --help filling the rest.
Learning objectives
By the end of this article you can:
- Read and write any gcloud command from its four-part anatomy — group, subgroup, verb, and flags — instead of memorising it.
- Install or open the Cloud SDK (locally or in Cloud Shell), run
gcloud init, and confirm your active account, project and region in one command. - Use the daily-driver verbs (
create,list,describe,update,delete,set) fluently across thecompute,storage,iamandprojectsgroups. - Distinguish
gcloud auth login(your human identity) fromgcloud auth application-default login(credentials for code), and pick the right one every time. - Manage named configurations so you can switch between projects, regions and identities without ever passing
--projectby hand. - Bend output to your will with
--format(table, value, json, csv) and--filter, turning gcloud into a scriptable data tool. - Run a complete free-tier lab — create a VM, SSH in, attach a firewall rule, then delete everything — entirely from the command line, with validation at each step.
Prerequisites & where this fits
You need a Google account, a browser, and one Google Cloud project you can treat as a sandbox (with billing enabled — the lab stays inside the Always Free tier, but a billing account must be attached). You do not need any prior command-line experience; every command here is explained and every one of them runs unmodified inside Cloud Shell with nothing installed locally. Comfort with the idea of a “terminal” — a place where you type a command and read text back — is the only soft prerequisite.
This sits at the very start of the hands-on Google Cloud journey. It assumes you grasp the shape of the platform — that resources live inside projects, which live inside the GCP resource hierarchy of organization, folders and projects — and that permissions come through Google Cloud IAM. Everything downstream is operated through the commands you learn here, making this the highest-leverage hour in the curriculum. For the wider tour of every interface (SDK install paths, client libraries, ADC internals), the companion lesson Google Cloud Hands-On First Steps: Console, gcloud CLI, Cloud Shell & SDKs goes broad; this article goes fast and practical — the 90% you reach for daily.
A quick map of where each interface earns its place, so you reach for the right one:
| Interface | What it is | Reach for it when | Don’t use it for |
|---|---|---|---|
| Cloud Console | The web UI at console.cloud.google.com |
Exploring a new service, dashboards, billing, one-off changes | Anything you’ll do twice; anything that must be auditable |
gcloud CLI |
The command-line tool in the Cloud SDK | Fast repeatable changes, scripts, CI, copy-paste runbooks | Visual exploration of an unfamiliar service |
| Cloud Shell | A browser terminal with the SDK pre-installed and pre-authenticated | Running gcloud with zero install; the lab below | Long-running jobs (it idles out) or > 5 GB of files |
| Terraform / IaC | Declarative infra-as-code | Reproducible, reviewed, version-controlled infrastructure | A quick one-line lookup or ad-hoc fix |
Core concepts
Five ideas make the entire tool click. Internalise these and you stop searching Stack Overflow for commands you can simply derive.
A gcloud command is a sentence with a fixed grammar. Every command reads gcloud GROUP [SUBGROUP] VERB [NAME] [--flags]: the group is the service area (compute), an optional subgroup narrows it (compute instances), the verb is the action (create), the name is the resource, and flags supply the details. So gcloud compute instances create web-1 --zone us-central1-a is just “in compute/instances, create one named web-1, in this zone.” The whole tool is this one sentence with different nouns — you don’t memorise it, you speak it.
The same verbs repeat everywhere. GCP services share a common CRUD shape, so the verbs you learn for VMs work almost identically for buckets, IAM and DNS: create makes a thing, list shows many, describe shows one in full, update changes a thing, delete removes it, set configures a property. Learn the six once and only the nouns change after that.
gcloud is self-documenting — --help is a feature, not a fallback. Append --help to any point in a command and gcloud tells you what comes next: gcloud compute --help lists every subgroup, gcloud compute instances create --help every flag. Because the grammar is regular, you drill from “I want to do X” to the exact command without leaving the terminal.
Two completely different “logins” exist, and confusing them is the #1 newcomer trap. gcloud auth login authenticates you, the human, so the gcloud commands you type work. gcloud auth application-default login writes a separate credential file — Application Default Credentials (ADC) — that your code, Terraform and client libraries read. Run the wrong one and you get the maddening “your default credentials were not found” while gcloud itself works perfectly. Two logins, two purposes — keep them straight and a whole class of errors disappears.
Your “active context” is set once, then assumed. Rather than typing --project and --region on every command, gcloud holds an active configuration — a named bundle of properties (account, project, default region/zone) that commands inherit silently. gcloud config set project my-proj pins it; juggling two clients means two configurations you switch with one command. This is what makes “wrong project” mistakes avoidable instead of routine.
The vocabulary in one table
Before the daily commands, pin down every moving part. The glossary at the end repeats these for lookup; this is the mental model side by side:
| Term | One-line definition | Why it matters day to day |
|---|---|---|
| Cloud SDK | The package that contains gcloud (and gsutil, bq) |
What you install; what Cloud Shell pre-installs |
| Component | An add-on bundled with the SDK (gke-gcloud-auth-plugin, beta, alpha) |
Installed with gcloud components install |
| Group / subgroup | The service area in a command (compute, compute instances) |
The first thing you type; how you find the verb |
| Verb | The action (create, list, describe, delete, update) |
The same six cover most of your day |
| Property | A stored setting (project, compute/zone, account) |
Set once; inherited by every command |
| Configuration | A named bundle of properties | Switch projects/identities in one command |
gcloud auth login |
Logs in you (the human) for the CLI | Makes the commands you type work |
ADC (application-default login) |
Credentials for code / Terraform / libraries | Makes your programs authenticate |
| Impersonation | Borrowing a service account’s permissions for one command | Run as a robot identity without downloading a key |
--format / --filter |
Reshape and narrow command output | Turn gcloud into a scriptable data tool |
Anatomy of a gcloud command
Everything starts here. Spend two minutes on this section and the rest of the tool stops being a memory test. A gcloud command has up to five parts, left to right:
gcloud compute instances create web-1 --zone=us-central1-a --machine-type=e2-micro
# ^ ^ ^ ^ ^ ^
# CLI group subgroup verb name flags
Read it as plain English: “gcloud — in the compute service, the instances area — create one called web-1, in zone us-central1-a, of machine type e2-micro.” That’s it. Now map each slot to what it does and how you discover its options:
| Slot | What it is | Example values | How to discover options |
|---|---|---|---|
gcloud |
The CLI itself | always gcloud |
— |
| Group | The service area | compute, storage, iam, projects, config, dns, run |
gcloud --help lists all groups |
| Subgroup | A narrower area inside the group | instances, disks, firewall-rules, service-accounts |
gcloud compute --help |
| Verb | The action | create, list, describe, update, delete, set, add-iam-policy-binding |
gcloud compute instances --help |
| Name | The specific resource | web-1, my-bucket, [email protected] |
you choose it (or list to find it) |
| Flags | Per-command details | --zone, --machine-type, --format, --project |
gcloud compute instances create --help |
The habit that makes this anatomy pay off: list before you describe, describe before you delete — list finds the exact name, describe confirms it’s the right resource, and only then do you act. That three-step rhythm prevents the most common destructive mistake: deleting the wrong thing because you guessed its name. The next section walks each verb with real commands.
The daily-driver commands, one verb at a time
This is the 90%. Almost every day on Google Cloud is some combination of the six verbs, and the pattern repeats — list/describe/create/delete look nearly identical whether the noun is a VM, a bucket, or an IAM grant. Here they are end to end against one VM, in the order you actually use them:
# list — read-only; your most-typed, safest command. "What do I have?"
gcloud compute instances list
# describe — full config of ONE resource (YAML); confirm before you change
gcloud compute instances describe web-1 --zone=us-central1-a
# create — NAME + flags; anything you omit takes a default (describe to check)
gcloud compute instances create web-1 \
--zone=us-central1-a --machine-type=e2-micro \
--image-family=debian-12 --image-project=debian-cloud
# update / set / add-* — change an existing resource
gcloud compute instances add-tags web-1 --zone=us-central1-a --tags=http-server
# delete — prompts for confirmation (suppress with --quiet only when certain)
gcloud compute instances delete web-1 --zone=us-central1-a
The single most useful cross-service verb is add-iam-policy-binding (with its mirror remove-iam-policy-binding), which grants a role on any resource via a rigid --member + --role shape:
gcloud projects add-iam-policy-binding my-project-id \
--member="user:[email protected]" --role="roles/storage.objectViewer"
Now the payoff: the same verbs across the services you touch most. This single matrix table is most of your job — swap the noun, keep the verb:
| Task | Compute (compute) |
Storage (storage) |
IAM (iam / projects) |
|---|---|---|---|
| List | gcloud compute instances list |
gcloud storage buckets list |
gcloud iam service-accounts list |
| Describe one | gcloud compute instances describe NAME --zone=Z |
gcloud storage buckets describe gs://B |
gcloud iam service-accounts describe SA_EMAIL |
| Create | gcloud compute instances create NAME --zone=Z |
gcloud storage buckets create gs://B --location=L |
gcloud iam service-accounts create NAME |
| Change | gcloud compute instances add-tags NAME --tags=T |
gcloud storage buckets update gs://B --flag |
gcloud projects add-iam-policy-binding P --member=M --role=R |
| Delete | gcloud compute instances delete NAME --zone=Z |
gcloud storage rm --recursive gs://B |
gcloud iam service-accounts delete SA_EMAIL |
| Connect/act | gcloud compute ssh NAME --zone=Z |
gcloud storage cp FILE gs://B |
gcloud iam service-accounts keys list ... |
And the handful of “everyday” commands outside the CRUD verbs that you will reach for constantly:
| Command | What it does | When you use it |
|---|---|---|
gcloud init |
Interactive first-time setup (login + project + region) | Once per machine / new identity |
gcloud config list |
Show active account, project and region | Every time you’re unsure where you are |
gcloud auth login |
Log in your human identity for the CLI | When the CLI says you’re not authenticated |
gcloud auth application-default login |
Write ADC for code/Terraform | Before running scripts or Terraform locally |
gcloud components update |
Update the SDK and components | Every few weeks; when a command is missing |
gcloud compute ssh NAME |
SSH into a VM (handles keys for you) | To get a shell on a Compute Engine VM |
gcloud info |
Dump environment, paths and config for debugging | When something is mysteriously broken |
Authentication and configuration: the setup that trips everyone
This is the section that saves you the most hours. Two distinct mechanisms — who you are and what context you’re in — cause the vast majority of “it worked yesterday” tickets.
The two logins, finally untangled
There are two separate credential stores, for two separate purposes, and they are not interchangeable. gcloud auth login authenticates you, the human typing commands (it’s what gcloud init runs); after it, your terminal’s gcloud commands succeed. gcloud auth application-default login writes a separate Application Default Credentials (ADC) file your programs read — the GCP client libraries, Terraform, any SDK looking for “ambient” credentials. The trap: you run auth login, your gcloud commands work, then a Python script or terraform plan errors with “your default credentials were not found” — the script isn’t broken, it’s looking for ADC you never created. The fix is to also run gcloud auth application-default login. The clean comparison to keep:
| Aspect | gcloud auth login |
gcloud auth application-default login |
|---|---|---|
| Authenticates | You (the human) for the CLI | Your code / Terraform / client libraries |
| Used by | gcloud commands you type |
Programs that call the GCP client libraries |
| Stored where | gcloud’s own credential store | An ADC JSON file (e.g. ~/.config/gcloud/application_default_credentials.json) |
| Symptom if missing | gcloud: “you do not currently have an active account” | Code: “your default credentials were not found” |
| You run it when | Setting up the CLI on a new machine | Before running scripts/Terraform locally |
On Cloud Shell and on GCP VMs you usually need neither for basic work — Cloud Shell logs you in automatically, and a VM has an attached service account whose credentials the libraries find on their own. The two-login dance is mostly a local laptop concern.
Where the laptop gets its identity — and where you should never go
A related hazard is the old habit of downloading a service-account JSON key file and pointing GOOGLE_APPLICATION_CREDENTIALS at it. It works, but a long-lived key on a laptop is a standing credential an attacker can use until someone revokes it. The safer path for “run as a robot identity” is impersonation: stay logged in as yourself and add --impersonate-service-account to borrow the SA’s permissions for that one command, no key on disk. The ways code finds its credentials, best to worst:
| How credentials are provided | Where it’s used | Safety | Notes |
|---|---|---|---|
| Attached service account (metadata) | GCP VMs, Cloud Run, Cloud Shell | Best | No key exists; platform supplies short-lived tokens |
Impersonation (--impersonate-service-account) |
Your laptop, acting as a robot | Strong | You keep your identity; no downloaded key |
| Workload Identity Federation | CI/CD outside GCP (GitHub Actions, etc.) | Strong | Keyless; external identity trades for short-lived tokens |
ADC user login (application-default login) |
Local dev with the libraries | Good | Your own identity; fine for development |
Downloaded JSON key + GOOGLE_APPLICATION_CREDENTIALS |
Legacy / last resort | Weak | Long-lived secret on disk; avoid; rotate/disable if used |
Configurations: your context, named and switchable
A configuration is a named bundle of properties — at minimum your active account, your project, and your default compute/region and compute/zone. Commands inherit these silently, which is why a correctly-set config means you almost never type --project. Set the basics:
gcloud config set project my-project-id
gcloud config set compute/region us-central1
gcloud config set compute/zone us-central1-a
gcloud config list # confirm what's active
The power move is multiple named configurations — one per client, environment or identity — and switching between them in a single command:
gcloud config configurations create client-acme
gcloud config set project acme-prod
gcloud config set account [email protected]
# ...later, flip back to your default context:
gcloud config configurations activate default
gcloud config configurations list # see them all, * marks active
This is the antidote to “wrong project” disasters: instead of remembering to pass --project acme-prod and forgetting it on the one destructive command, you activate the acme configuration and every command targets the right place. The properties worth knowing and where each one bites:
| Property | What it sets | Set with | If unset, the symptom |
|---|---|---|---|
project |
The active project for all commands | gcloud config set project ID |
“project is required” or wrong-project changes |
account |
Which logged-in identity is active | gcloud config set account EMAIL |
Commands run as the wrong user |
compute/region |
Default region for regional resources | gcloud config set compute/region R |
gcloud prompts you for a region each time |
compute/zone |
Default zone for zonal resources (VMs, disks) | gcloud config set compute/zone Z |
You must pass --zone on every VM command |
core/disable_prompts |
Suppress interactive prompts globally | gcloud config set disable_prompts true |
Scripts hang waiting for a Y/N answer |
A clean separation rule of thumb: pick your default region/zone once based on where your users and data live (see GCP regions and zones explained), set it in your configuration, and only override with a per-command --zone when you have a specific reason.
Shaping output: --format and --filter (the superpower)
Most newcomers treat gcloud output as fixed text to read with their eyes; professionals treat it as data. --filter narrows which rows you get using simple field expressions; --format controls how the result is rendered — a human table, a bare value for a script, JSON for a program, CSV for a spreadsheet. Together they turn any list/describe into a precise query:
# Only running VMs, showing just name and zone, as a clean table
gcloud compute instances list \
--filter="status=RUNNING" \
--format="table(name, zone, machineType.basename())"
# Just one value — the external IP of a VM — perfect to capture in a script
gcloud compute instances describe web-1 --zone=us-central1-a \
--format="value(networkInterfaces[0].accessConfigs[0].natIP)"
# Every bucket as JSON, for a program to consume
gcloud storage buckets list --format=json
# Projects whose ID starts with "prod", as CSV
gcloud projects list --filter="projectId:prod*" --format="csv(projectId, name)"
The second example is the one you’ll use constantly: --format="value(...)" returns a bare string with no header, so you can assign it to a shell variable (IP=$(gcloud ... --format='value(...)')) and build real automation. The format types and when to choose each:
--format value |
Output shape | Use it for | Example |
|---|---|---|---|
table(...) |
Aligned columns (the default-ish view) | Human reading; pick exactly the columns you want | --format="table(name, status)" |
value(...) |
Bare value(s), no header | Capturing a single field in a script | --format="value(name)" |
json |
Full JSON document | Feeding another program / jq |
--format=json |
yaml |
YAML (the describe default) |
Reading a full resource by eye | --format=yaml |
csv(...) |
Comma-separated rows | Importing into a spreadsheet | --format="csv(name, zone)" |
flattened |
One key: value per line |
Grepping deeply-nested fields | --format=flattened |
Common --filter patterns, which read almost like English once you’ve seen a few:
| You want… | --filter expression |
Notes |
|---|---|---|
| Exact match | status=RUNNING |
= is exact, case-insensitive for enums |
| Substring / prefix | name:web or projectId:prod* |
: is “contains”; * is a wildcard |
| Numeric comparison | diskSizeGb>=100 |
>, <, >=, <= work on numbers |
| Combine conditions | status=RUNNING AND zone:us-central1 |
AND, OR, NOT, parentheses |
| Negate | NOT name:test |
Exclude rows |
| Label match | labels.env=prod |
Filter on resource labels |
The mental model: --filter is your WHERE clause, --format is your SELECT clause. If you’ve ever written SQL, gcloud’s data flags will feel instantly familiar — and if you haven’t, “narrow the rows, then choose the columns” is all you need to remember.
Architecture at a glance
There is no box-and-arrow diagram here, because the architecture of gcloud is a mental one — a short pipeline every command flows through. Picture four stages, left to right. You type a sentence (gcloud GROUP VERB NAME --flags): the CLI parses the grammar, merges your explicit flags with the active configuration’s properties (so an omitted --project is filled in from gcloud config), and resolves which identity to use (your gcloud auth login credentials, or an impersonated service account). Nothing has left your machine yet — gcloud is assembling a complete, authenticated request from your terse input plus your stored context. Then gcloud calls a Google Cloud REST API: under the hood gcloud compute instances create is an HTTPS call to compute.googleapis.com, signed with an OAuth token. This is the key insight — gcloud, the Console and the client libraries are three front doors to the same APIs, which is exactly why the Console can show the “Equivalent command line” for a form and why a gcloud command maps cleanly onto a Terraform resource later.
Finally, Google Cloud checks IAM and acts: the API verifies your identity holds a role granting the permission the verb needs (e.g. compute.instances.create), performs the operation, and returns a result — often an operation handle for long-running work like booting a VM — which gcloud renders back to your terminal through --format, as a table for your eyes or a bare value for your script. Type → assemble-with-context → call API → render: that is the whole machine, and every command you write is just a different sentence poured into the same pipeline.
Real-world scenario
Northwind Books, a mid-sized online retailer, hired a junior platform engineer named Priya to help manage their Google Cloud estate. Northwind runs two projects — northwind-prod and northwind-staging — plus does occasional contracting work in a client’s project, bluewave-prod, that they have guest access to. In her first week, Priya hit every classic gcloud wall, and how she climbed each one is the whole curriculum in miniature.
Day one: the wrong-project scare. Asked to “delete the old test VM,” Priya ran gcloud compute instances list, saw a test-runner instance, and was one keystroke from deleting it — until she noticed gcloud config list reported the active project as bluewave-prod, the client’s. The test-runner she was about to delete was theirs, not Northwind’s. The lesson landed hard: always gcloud config list before any write. She fixed the root cause by creating three named configurations — nw-prod, nw-staging, bluewave — each pinned to its project and the right account. From then on, switching context was gcloud config configurations activate nw-staging, and “wrong project” became structurally impossible rather than a thing she had to remember.
Day two: the credentials that “weren’t there.” Priya wrote a small Python script to tag every untagged bucket. It failed instantly: “your default credentials were not found.” She’d run gcloud auth login and her gcloud commands worked fine, so she assumed she was authenticated. The penny dropped when a colleague explained the two logins: her human login made the CLI work, but the script needed ADC. One gcloud auth application-default login later, the script ran. She wrote both commands into the team’s onboarding doc so the next hire wouldn’t lose an afternoon to it.
Day three: turning gcloud into a report. Finance asked, “how many VMs are running across staging, and what are they costing roughly?” Instead of clicking through the Console three times, Priya wrote one line: gcloud compute instances list --filter="status=RUNNING" --format="csv(name, zone, machineType.basename())", piped it into a spreadsheet, and had the answer in thirty seconds. She turned the same query into a daily Cloud Scheduler check that flags any VM larger than e2-medium left running overnight — her first piece of real automation, built entirely from the --filter/--format she’d learned two days earlier.
The arc is the point: every senior gcloud habit Priya now has — config-per-context, the two-login discipline, output-as-data — started as a small, specific mistake, and none of it required memorising commands, only understanding the shape of the tool. Three weeks in, she was the person other juniors asked, and her runbooks (copy-pasteable gcloud, not screenshots) became the team standard.
Advantages and disadvantages
The CLI is not always the right tool — knowing when to drop back to the Console (or jump forward to Terraform) is part of fluency:
| Aspect | gcloud CLI — advantage | gcloud CLI — disadvantage |
|---|---|---|
| Repeatability | Identical results every run; paste into runbooks | None — this is its core strength |
| Speed | One line beats a menu safari, especially in bulk | Steep at first; you must know roughly what to type |
| Scriptability | Composes with shell, --format=value, CI pipelines |
Imperative — no built-in state/drift tracking like Terraform |
| Auditability | Commands are text: diff-able, reviewable, version-controllable | Ad-hoc commands run outside a pipeline still aren’t reviewed |
| Discoverability | --help at every level; self-documenting grammar |
Worse than the Console for exploring a brand-new service visually |
| Safety | Confirmation prompts; describe before delete |
--quiet + wrong config = fast, silent damage |
| Reproducibility | Great for one-off ops and glue | For long-lived infra, Terraform’s declarative model is better |
When each matters: reach for the Console to learn a service you’ve never used (its forms teach you the options, and many show the equivalent gcloud command), gcloud the instant a task is repetitive or belongs in a runbook, and Terraform when infrastructure must be reproducible, reviewed and tracked over time. gcloud is the scalpel for operations and quick fixes; IaC is the blueprint for the building. A healthy pattern is prototype in gcloud, then codify in Terraform once it’s proven.
Hands-on lab
This is the centerpiece. You will create a real Linux VM on the free tier, log into it, attach a firewall rule, capture its IP with --format, then tear everything down — all from the command line. It stays inside the Always Free tier (one e2-micro in an eligible US region), but you must run delete at the end so nothing lingers. About fifteen minutes end to end.
Where to run it: the zero-install path is Cloud Shell — open console.cloud.google.com, click the terminal icon (top-right), and you have an authenticated SDK instantly. Everything below also works on a local SDK install after gcloud init. Console equivalents are noted so you see both worlds.
Step 0 — Confirm where you are
Never act blind. Check your active account, project and region first:
gcloud config list
gcloud auth list # which identity is active (marked with *)
Expected output: a block showing account, project, and (ideally) compute/region + compute/zone. If project is unset or wrong, fix it before going further:
gcloud config set project YOUR_PROJECT_ID
gcloud config set compute/region us-central1
gcloud config set compute/zone us-central1-a
Console equivalent: the project picker in the top bar shows the active project; the avatar (top-right) shows the active account.
Validate: rerun gcloud config list and confirm project, region and zone are all set. This one habit prevents the wrong-project mistake from the scenario above.
Step 1 — Enable the Compute Engine API
A service’s API must be enabled in the project before use (a fresh project has almost everything off):
gcloud services enable compute.googleapis.com
Expected output: Operation "operations/..." finished successfully. (It can take 30–60 seconds the first time.)
Console equivalent: APIs & Services → Library → search “Compute Engine API” → Enable.
Validate: gcloud services list --enabled --filter="config.name:compute" --format="value(config.name)" should print compute.googleapis.com.
Step 2 — Create the VM
Create a single free-tier e2-micro running Debian (the backslashes just split one long command across lines):
gcloud compute instances create kv-lab-vm \
--zone=us-central1-a \
--machine-type=e2-micro \
--image-family=debian-12 \
--image-project=debian-cloud \
--tags=kv-lab
Expected output: a one-row table showing NAME (kv-lab-vm), ZONE, MACHINE_TYPE (e2-micro), an internal IP and an external IP, and STATUS: RUNNING.
Console equivalent: Compute Engine → VM instances → Create instance → name it, pick e2-micro / us-central1 / Debian 12 → Create. (The “Equivalent command line” link on that page shows the very command above.)
Validate:
gcloud compute instances list --filter="name=kv-lab-vm" \
--format="table(name, status, machineType.basename())"
You should see kv-lab-vm RUNNING e2-micro.
Step 3 — Capture the VM’s external IP with --format
Practise output-as-data: pull just the external IP into a shell variable using --format="value(...)":
IP=$(gcloud compute instances describe kv-lab-vm --zone=us-central1-a \
--format="value(networkInterfaces[0].accessConfigs[0].natIP)")
echo "VM external IP is: $IP"
Expected output: VM external IP is: 34.x.x.x (a real public IP).
Validate: echo $IP prints a non-empty IP. This is the pattern behind nearly all gcloud scripting — a single field, captured cleanly, no header to strip.
Step 4 — SSH into the VM
gcloud compute ssh handles SSH key generation and injection for you — no manual key management:
gcloud compute ssh kv-lab-vm --zone=us-central1-a
Expected output: the first time, it generates a key pair, then drops you at a shell prompt on the VM, e.g. your_user@kv-lab-vm:~$. Run a command to prove you’re inside the box:
hostname && cat /etc/os-release | grep PRETTY_NAME
exit
hostname returns kv-lab-vm and the OS line shows Debian 12. Type exit to return to your own shell.
Console equivalent: the “SSH” button next to the instance opens a browser SSH session — same result, no key handling on your part.
Validate: you saw kv-lab-vm as the hostname from inside the VM, then returned to your local/Cloud Shell prompt.
Step 5 — Add a firewall rule (allow HTTP)
Suppose this VM will serve web traffic. The network blocks inbound HTTP by default; add a rule opening port 80 only to VMs carrying the kv-lab tag:
gcloud compute firewall-rules create allow-http-kv-lab \
--direction=INGRESS \
--action=ALLOW \
--rules=tcp:80 \
--target-tags=kv-lab \
--source-ranges=0.0.0.0/0
Expected output: a one-row table for allow-http-kv-lab showing INGRESS, ALLOW, and tcp:80.
Console equivalent: VPC network → Firewall → Create firewall rule → ingress, allow, tcp:80, target tag kv-lab.
Validate:
gcloud compute firewall-rules list --filter="name=allow-http-kv-lab" \
--format="table(name, direction, allowed[].map().firewall_rule().list())"
You should see the rule listing tcp:80. (We won’t install a web server — the goal is the firewall command pattern, which is identical for any port.)
Step 6 — The same VM as Terraform (a peek forward)
Everything you did imperatively can be declared. You won’t run this, but seeing the gcloud-to-Terraform mapping makes your next stage concrete — the same VM as code:
resource "google_compute_instance" "kv_lab_vm" {
name = "kv-lab-vm"
zone = "us-central1-a"
machine_type = "e2-micro"
tags = ["kv-lab"]
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
}
}
network_interface {
network = "default"
access_config {} # ephemeral external IP
}
}
The shape is the same — name, zone, machine type, image, tag — just declarative instead of a verb. The mental jump from gcloud compute instances create to this is small, which is exactly why prototyping in gcloud first pays off.
Step 7 — Tear everything down
The most important step — leaving resources running is how surprise bills happen. Delete in reverse order (rule, then VM). Each command prompts for confirmation; answer Y:
gcloud compute firewall-rules delete allow-http-kv-lab
gcloud compute instances delete kv-lab-vm --zone=us-central1-a
Expected output: each prompts The following ... will be deleted ... Do you want to continue (Y/n)? — confirm, and each reports deletion.
Validate (the proof it’s clean):
gcloud compute instances list --filter="name=kv-lab-vm" --format="value(name)"
gcloud compute firewall-rules list --filter="name=allow-http-kv-lab" --format="value(name)"
Both should return nothing (empty output) — confirming the VM and rule are gone and nothing is billing. An e2-micro is free-tier eligible, but a forgotten external IP or non-eligible region can still cost a little, so “empty output” is your all-clear.
A compact recap of the whole lab — the verb each step exercised and what it proved:
| Step | Command verb | What it taught |
|---|---|---|
| 0 | config list / auth list |
Always confirm context before acting |
| 1 | services enable |
APIs are off until enabled per project |
| 2 | compute instances create |
The create NAME --flags pattern |
| 3 | describe + --format=value |
Output-as-data; capture one field |
| 4 | compute ssh |
gcloud manages SSH keys for you |
| 5 | firewall-rules create |
The firewall command pattern (any port) |
| 6 | (Terraform) | The gcloud → IaC mapping |
| 7 | delete + validate-empty |
Always tear down; prove it’s clean |
Common mistakes & troubleshooting
These are the failures that actually consume newcomer hours. Each is symptom → root cause → how to confirm → fix. Bookmark this; you will hit most of them in your first month.
1. “Your default credentials were not found” when running a script or Terraform.
Root cause: You ran gcloud auth login (which authenticates the CLI) but never created ADC for your code. The script looks for Application Default Credentials, which don’t exist.
Confirm: gcloud auth application-default print-access-token errors; the file ~/.config/gcloud/application_default_credentials.json is missing.
Fix: Run gcloud auth application-default login. Two logins, two purposes — the script needs the second one.
2. A command changed (or nearly deleted) the wrong resource — wrong project.
Root cause: Your active configuration points at a different project than you assumed; commands silently inherit project from config.
Confirm: gcloud config list shows an unexpected project (or account).
Fix: gcloud config set project RIGHT_ID, or gcloud config configurations activate the-right-one. Build the habit of gcloud config list before any write. Use one named configuration per project/client.
3. “Permission denied” / PERMISSION_DENIED even though you’re logged in.
Root cause: You’re authenticated but your identity lacks the IAM role granting the permission the verb needs (e.g. compute.instances.create). Authentication ≠ authorization.
Confirm: The error names the missing permission; gcloud projects get-iam-policy PROJECT --flatten="bindings[].members" --filter="bindings.members:YOUR_EMAIL" shows your roles.
Fix: Have an admin grant the right role (see Google Cloud IAM explained simply), or — if you should act as a service account — add --impersonate-service-account=SA_EMAIL.
4. “API [compute.googleapis.com] not enabled on project.”
Root cause: A fresh project has most service APIs disabled; you must enable each one before use.
Confirm: The error itself names the API; gcloud services list --enabled doesn’t list it.
Fix: gcloud services enable compute.googleapis.com (swap in the named API). Enabling can take up to a minute to propagate.
5. gcloud keeps asking “Did you mean zone…?” or “region is required.”
Root cause: No default zone/region is set in your configuration, so zonal/regional commands have nothing to inherit.
Confirm: gcloud config list shows no compute/zone or compute/region.
Fix: gcloud config set compute/zone us-central1-a and gcloud config set compute/region us-central1, or pass --zone/--region per command.
6. A command “the docs mention” doesn’t exist — Invalid choice.
Root cause: The command lives in a beta or alpha surface (or a component you haven’t installed), not the stable gcloud.
Confirm: gcloud GROUP --help doesn’t list the verb; the docs show gcloud beta ....
Fix: Prefix with beta/alpha (gcloud beta ...) and/or gcloud components install COMPONENT; run gcloud components update if your SDK is simply old.
7. A script hangs forever, waiting at a prompt.
Root cause: A delete (or other destructive verb) is asking Do you want to continue (Y/n)? and there’s no human to answer in a non-interactive run.
Confirm: The job is stuck with no output after a destructive command.
Fix: Add --quiet to suppress prompts in automation (gcloud compute instances delete X --quiet) — but only where you’re certain, since --quiet removes your safety net.
8. gcloud: command not found after installing the SDK.
Root cause: The SDK’s bin directory isn’t on your PATH (the post-install step that adds it was skipped).
Confirm: which gcloud returns nothing; the SDK folder exists but isn’t referenced in your shell profile.
Fix: Re-run the installer’s path step, or add the SDK bin to PATH in your shell profile and restart the shell. (Cloud Shell sidesteps this entirely — gcloud is already on the path.)
9. SSH to a VM hangs or is refused.
Root cause: No firewall rule allows inbound SSH (tcp:22) from your source, or the VM has no external IP / is still booting.
Confirm: gcloud compute firewall-rules list shows nothing allowing tcp:22; describe shows the VM has no natIP or isn’t RUNNING yet.
Fix: Ensure a rule allows tcp:22 from your range (the default network usually has default-allow-ssh), wait for STATUS: RUNNING, and use gcloud compute ssh (it manages keys). For private VMs, use IAP-based SSH.
10. Output is hard to script — you’re grepping/cutting text fragilely.
Root cause: Parsing the human table with grep/awk/cut instead of asking gcloud for exactly the field.
Confirm: Your script breaks when a column width or header changes.
Fix: Use --format="value(field.path)" to get the bare value, or --format=json and parse with jq. Let gcloud do the extraction; never screen-scrape its tables.
The fastest-to-diagnose cluster — the four “auth-shaped” errors and the one command that tells them apart:
| Symptom | It’s actually | One command to confirm | Fix in a phrase |
|---|---|---|---|
| Code: “default credentials were not found” | Missing ADC | gcloud auth application-default print-access-token |
Run application-default login |
| CLI: “you do not currently have an active account” | Not logged into the CLI | gcloud auth list |
Run gcloud auth login |
| “PERMISSION_DENIED” on an action | Logged in, wrong role | gcloud projects get-iam-policy ... |
Grant the role / impersonate |
| “API … not enabled” | Service API off | gcloud services list --enabled |
gcloud services enable ... |
Best practices
- Run
gcloud config listbefore any write. The single habit that prevents wrong-project disasters — two seconds now saves a postmortem later. - One named configuration per project, client or environment. Switch with
gcloud config configurations activate NAME; never juggle--projectby hand on destructive commands. - Learn the two logins cold.
gcloud auth loginfor the CLI;gcloud auth application-default loginfor code and Terraform. Put both in your onboarding doc. - Set your default region and zone once in your configuration, then override per-command only with intent — this kills the endless “which zone?” prompts.
list→describe→ act. Find the exact name, confirm it’s the right resource, then change or delete it.- Treat output as data with
--formatand--filter.value(...)for one field,jsonfor programs,table(...)for humans. Never screen-scrape tables withgrep. - Reserve
--quietfor automation you trust. It removes confirmation prompts — wonderful in a tested pipeline, dangerous at a 2 a.m. terminal. - Prefer impersonation over downloaded keys.
--impersonate-service-accountacts as a robot identity with no long-lived key on disk to leak. - Discover with
--help, don’t guess. Append it at any level to find the next valid slot; the grammar is regular, so let it teach you. - Keep the SDK current with
gcloud components update; a “missing” command is often just an old SDK or abeta/alphasurface. - Prototype in gcloud, codify in Terraform. The CLI for fast feedback and one-off ops; IaC for anything long-lived.
- Paste commands, not screenshots, into runbooks. A
gcloudline is reproducible, diff-able and reviewable; a screenshot is none of those.
Security notes
- Never download a service-account JSON key unless you truly must. A long-lived key in a laptop or repo is a standing credential usable until revoked. Prefer keyless impersonation (
--impersonate-service-account), Workload Identity Federation for CI/CD, and the attached service account on GCP compute. If a key already exists, disable it (gcloud iam service-accounts keys delete). - Least privilege for the identity you operate as. Don’t run daily commands as a broadly-privileged owner; grant the specific roles the task needs (see GCP IAM explained simply), and impersonate a narrowly-scoped SA for risky actions.
- Guard
~/.config/gcloud. It holds your CLI and ADC credentials — as sensitive as an SSH private key. Never sync, commit or bake it into an image;gcloud auth revokeinvalidates a login when a machine is decommissioned. - Use
--impersonate-service-accountfor elevation, not a downloaded key. It leaves an audit trail (attributed to you acting as the SA), is time-bounded, and leaves no secret behind. - Be deliberate with
--quietand broad--filterdeletes. Dry-run by replacing the destructive verb withlistand the same--filterfirst, to see exactly what would be affected. - Prefer Cloud Shell for admin from untrusted networks. Its credentials live in Google’s environment, not the laptop you’re typing from, shrinking the blast radius if that laptop is compromised.
Cost & sizing
The gcloud CLI itself is free — it’s just a client for the APIs. What costs money is what you create with it, and the lab is engineered to stay free. One e2-micro VM in an eligible US region (us-central1, us-west1, us-east1) sits inside the Always Free monthly allotment, with a small standard-persistent-disk allowance and a modest egress quota; run the teardown and nothing accrues. What turns “free” into a small bill: a non-eligible region or a bigger machine type, a reserved static external IP left unattached (idle reserved IPs are billed), disks left behind after a VM is deleted, and egress beyond the allotment. The Step 7 validate-empty check is the safeguard — if list returns nothing, nothing is billing.
Rough figures so the trade-offs are concrete (prices vary by region and change over time — treat as order-of-magnitude):
| Item | Free-tier status | Rough cost if NOT free | How to stay free |
|---|---|---|---|
e2-micro VM (eligible US region) |
Always Free (1/month) | ~₹600–900 / month if run continuously elsewhere/larger | Use e2-micro in us-central1/us-west1/us-east1 |
| Standard persistent disk (boot) | Free up to 30 GB-months | ~₹3–4 / GB / month beyond | Keep the default small boot disk; delete with the VM |
| Ephemeral external IP (in use) | Free while attached to a running VM | — | Don’t reserve a static IP for the lab |
| Reserved static IP (idle) | Not free when unattached | ~₹250–350 / month idle | Don’t reserve one; release any you created |
| Network egress | Free up to ~1 GB/month (N. America) | Per-GB beyond | The lab sends almost nothing |
| gcloud CLI / Cloud Shell | Always free | — | — |
The discipline that actually controls cost is the teardown habit, not the machine type — a forgotten e2-medium or an orphaned reserved IP is how a “free lab” becomes a line item. For ongoing spend control, set a budget alert on the project early (covered in GCP billing & cost management deep dive).
Interview & exam questions
1. Walk me through the anatomy of a gcloud command. gcloud GROUP [SUBGROUP] VERB [NAME] [--flags] — group is the service area (compute), subgroup narrows it (instances), verb is the action (create), name is the resource, flags supply details. The same six verbs (list/describe/create/update/delete/set) recur across services, so commands are composable rather than memorised.
2. Difference between gcloud auth login and gcloud auth application-default login? The first authenticates you, the human, so the commands you type work; the second writes Application Default Credentials (ADC) that your code, Terraform and client libraries read. A script failing with “default credentials not found” needs the second one, even if your CLI works fine.
3. A command changed the wrong resource. What happened and how do you prevent it? The active configuration pointed at a different project than assumed, and commands silently inherit it. Confirm with gcloud config list; prevent it by keeping one named configuration per project and activating the right one instead of relying on memory to pass --project.
4. How do you extract a single field — say a VM’s external IP — for a script? Use --format="value(field.path)", which returns the bare value with no header (e.g. --format="value(networkInterfaces[0].accessConfigs[0].natIP)"), and assign it to a shell variable. Never grep/cut the human table — let gcloud do the extraction.
5. Difference between --filter and --format? --filter decides which rows you get (the WHERE clause: status=RUNNING); --format decides how output is rendered (the SELECT clause: table, value, json, csv). Together they turn any list/describe into a precise, scriptable query.
6. A teammate gets “PERMISSION_DENIED” but insists they’re logged in. What’s going on? Authentication is not authorization — they’re authenticated but their identity lacks the IAM role granting the permission the verb needs (e.g. compute.instances.create). The fix is an IAM role grant or impersonating a service account that has it, not re-running auth login.
7. Why might a command “from the docs” return Invalid choice? It lives in the beta/alpha surface (or an uninstalled component), not stable gcloud. Prefix with gcloud beta ..., install the component, and/or run gcloud components update if the SDK is old.
8. What are named configurations and why use more than one? A configuration is a named bundle of properties (account, project, default region/zone) commands inherit; one per client/environment lets you switch context in a single command, eliminating wrong-project mistakes from forgetting --project.
9. When should you use gcloud versus the Console versus Terraform? Console to explore an unfamiliar service; gcloud the moment a task is repeatable, scriptable or belongs in a runbook; Terraform when infrastructure must be reproducible, reviewed and tracked. A common pattern is prototype in gcloud, then codify in Terraform.
10. Why is downloading a service-account key discouraged, and what’s the alternative? A long-lived JSON key is a standing secret that grants access until revoked — a leak risk in any laptop, log or repo. Prefer keyless paths: impersonation for human-as-robot, Workload Identity Federation for CI/CD, and the attached service account on GCP compute.
11. What does gcloud init do? It runs the interactive first-time setup — logs you in, lets you pick or create a default project, optionally sets a default region/zone — populating a configuration so subsequent commands have context. You run it once per machine or new identity.
12. A gcloud script hangs in CI. Cause and fix? A destructive verb (often delete) is waiting at a Y/n confirmation prompt no human will answer. Add --quiet (or set gcloud config set disable_prompts true), only where the action is safe and tested.
These map to the Cloud Digital Leader (CDL) and especially the Associate Cloud Engineer (ACE) exams, which probe the CLI, configurations, the auth/ADC distinction and --format/--filter — see the GCP certification prep kit for the full mapping.
Quick check
- In the command
gcloud compute instances create web-1 --zone=us-central1-a, name the four structural parts (group, subgroup, verb, name) and the flag. - Your Python script errors with “your default credentials were not found,” but your
gcloudcommands work fine. Which command fixes it, and why? - You want just the names of all running VMs, one per line, to feed a loop. Write the
--filterand--formatyou’d use. - You manage
prodandstagingin two different projects and keep changing the wrong one. What gcloud feature solves this, and what command switches between them? - True or false: downloading a service-account JSON key is the recommended way to let your laptop act as a service account.
Answers
- Group =
compute, subgroup =instances, verb =create, name =web-1; the flag is--zone=us-central1-a. Reads as “in compute/instances, create web-1 in this zone.” gcloud auth application-default login. Yourgcloud auth loginauthenticated the CLI; the script needs separate Application Default Credentials (ADC) for the client libraries. Two logins, two purposes.gcloud compute instances list --filter="status=RUNNING" --format="value(name)"—--filternarrows to running VMs (the WHERE),--format="value(name)"prints bare names one per line (the SELECT).- Named configurations — keep one per project (e.g.
prod,staging) and switch withgcloud config configurations activate staging. Every command then inherits the right project automatically, so “wrong project” becomes structurally impossible. - False. A long-lived key is a leak risk. Prefer keyless impersonation (
--impersonate-service-account), Workload Identity Federation for CI/CD, or the attached service account on GCP compute.
Glossary
- Cloud SDK — the installable package that contains
gcloud(plusgsutilandbq); pre-installed in Cloud Shell. - Component — an add-on bundled into the SDK (
beta,alpha,gke-gcloud-auth-plugin); managed withgcloud components install/update. - Group / subgroup — the service area in a command (
compute,compute instances); the first thing you type and how you find the verb. - Verb — the action slot in a command (
list,describe,create,update,delete,set); the same handful recur across every service. - Property — a stored setting (
project,account,compute/zone) that commands inherit from the active configuration. - Configuration — a named bundle of properties; switching configurations re-points your account/project/region in one command.
gcloud auth login— authenticates you, the human, so the CLI commands you type succeed.- Application Default Credentials (ADC) — credentials your code, Terraform and client libraries read; created on a laptop with
gcloud auth application-default login. - Service account — a non-human “robot” identity an application or VM runs as; can be impersonated rather than key-downloaded.
- Impersonation — borrowing a service account’s permissions for a single command via
--impersonate-service-account, with no key on disk. - Workload Identity Federation — a keyless way for external CI/CD (e.g. GitHub Actions) to obtain short-lived GCP credentials.
--format— reshapes output (table,value,json,yaml,csv,flattened); the SELECT clause of a gcloud query.--filter— narrows which resources a command returns (status=RUNNING,name:web); the WHERE clause.--quiet— suppresses interactive confirmation prompts; essential for automation, risky at a live terminal.- Cloud Shell — a browser terminal with the SDK pre-installed and pre-authenticated; the zero-install way to run every command here.
Next steps
You can now compose any gcloud command, set up your auth and configurations correctly, and turn output into data. Build outward:
- Next: Google Cloud Hands-On First Steps: Console, gcloud CLI, Cloud Shell & SDKs — the wider tour of every interface, SDK install paths, client libraries and ADC internals.
- Related: Google Cloud IAM Explained Simply: Members, Roles, and Bindings — the permission model behind every “PERMISSION_DENIED” you’ll hit.
- Related: GCP Resource Hierarchy Explained: Organization, Folders and Projects — why the
projectyour config points at matters so much. - Related: GCP Regions and Zones Explained — how to choose the default region/zone you pin in your configuration.
- Related: Cloud Run Explained: Serverless Containers That Scale to Zero — your next service to drive entirely from
gcloud run.