So far we have worked mostly with concepts and with the web console. In this lesson we change gear: we will learn the tool with which Google Cloud is really administered day to day, the gcloud CLI, and the environment that makes it accessible from any browser without installing anything, Cloud Shell.

Mastering gcloud is not about memorising commands, but about understanding its structure, knowing how to ask it for help and knowing how to extract exactly the piece of data you need. With that, any operation on any service becomes something you can work out. We will look at what Cloud Shell is and what limits it has, how to install the CLI locally, the anatomy of a command and the help system, the output formats and the filters, authentication and its two modes, named configurations for switching between alpinashop-dev and alpinashop-prod, the other SDK tools, and the basic scripting that chains commands together.

And we will close the module with the course's first real deployment: Dani is going to publish an AlpinaShop "coming soon" page on the internet, end to end, without leaving the browser.

Contents

  1. What Cloud Shell is
  2. Installing the Google Cloud CLI locally
  3. Cloud Shell versus a local CLI
  4. Anatomy of a gcloud command and the help system
  5. Output formats: --format
  6. Filters: --filter
  7. Authentication: gcloud init, auth login and application-default
  8. Named configurations
  9. Other SDK tools
  10. Basic scripting with gcloud
  11. The first real deployment: AlpinaShop's "coming soon" page

  1. What Cloud Shell is

Cloud Shell is a free Linux virtual machine that opens inside the browser from the >_ icon in the console. It is designed so that you can administer Google Cloud without installing anything and without configuring credentials.

What it comes with already installed and configured:

Category Contents
Google Cloud CLI gcloud, gsutil, bq, kubectl
Languages Python, Java, Go, Node.js, .NET, Ruby, PHP
Tools git, docker, terraform, make, vim, nano, jq, curl
Authentication Already authenticated as your console user
Editor Cloud Shell Editor, based on VS Code technology

Characteristics and limits you need to know before relying on it:

Aspect Detail
Cost Free
Machine A small instance (on the order of 1-2 vCPU and a few GB of RAM)
Persistent disk 5 GB mounted on your $HOME, which does persist between sessions
Outside $HOME Everything is lost: packages installed with apt, changes in /etc, and so on
Inactivity The session closes after roughly an hour without use
Maximum session Around 12 hours at a stretch
Deletion through inactivity If you do not use Cloud Shell for several months, the disk may be deleted (with prior warning)
Usage quota There is a weekly hours limit

The practical consequence of the persistence point is important: if you install something with apt install, it will be gone in the next session. For it to survive, either you install it in your $HOME, or you automate it in the ~/.customize_environment file, which Cloud Shell runs when the machine starts.

Two additional features we will use:

  • Cloud Shell Editor: opened with the "Open editor" button, it gives you an IDE in the browser, with a file explorer, an integrated terminal and syntax highlighting. Very convenient for writing scripts or editing a Dockerfile without leaving the console.
  • Web preview: the eye-shaped icon lets you open in the browser a service you are running in Cloud Shell (on port 8080 by default) through a temporary, authenticated URL. It is what lets you try out AlpinaShop's Flask application without deploying it.
# Check the machine you are working on
cat /etc/os-release | head -n2
nproc          # number of CPUs available
free -h        # memory
df -h $HOME    # space on the persistent disk
# CLI version and installed components
gcloud version

  1. Installing the Google Cloud CLI locally

Cloud Shell is excellent for learning and for one-off tasks, but for day-to-day work you will want the CLI on your own machine, with your editor, your files and your scripts.

Linux (Debian/Ubuntu), through the official repository:

# 1. Dependencies and Google's repository key
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates gnupg curl

# 2. Add the repository's signing key
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg \
  | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg

# 3. Add the repository to the apt sources
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] \
https://packages.cloud.google.com/apt cloud-sdk main" \
  | sudo tee /etc/apt/sources.list.d/google-cloud-sdk.list

# 4. Install
sudo apt-get update && sudo apt-get install -y google-cloud-cli

macOS, with Homebrew:

brew install --cask google-cloud-sdk

Windows: download the GoogleCloudSDKInstaller.exe installer from the official documentation and run it. When it finishes it offers to launch gcloud init. Installing the Linux version inside WSL2 also works perfectly, and is the option many developers prefer.

Managing additional components:

# See which components are installed and which are available
gcloud components list
# Install components that do not come by default
gcloud components install kubectl
gcloud components install beta alpha
# Update the CLI and all its components
gcloud components update

A word of warning: if you installed the CLI through apt or Homebrew, gcloud components update may be disabled, because the package manager handles updates. In that case, update with apt upgrade or brew upgrade. In Cloud Shell, the CLI updates itself.

  1. Cloud Shell versus a local CLI

Criterion Cloud Shell Local CLI
Installation None Requires installing and updating
Authentication Automatic, as your user Requires gcloud init / auth login
Cost Free Free (but it uses your machine)
Persistence Only 5 GB in $HOME Your whole disk
Power A small machine Your computer's
Preinstalled tools Many and up to date Whatever you install
Access to local files They have to be uploaded Direct
Integration with your editor/IDE Limited to the web editor Complete
Long sessions or heavy processes Limited (timeout, quota) No limit
Use from any device Yes, all you need is a browser No
Recommended for Learning, one-off tasks, emergencies, demos Daily work, development, scripts

The recommendation for this course: use Cloud Shell. It removes all the installation friction and guarantees that you are working with the same version described here. Install the local CLI when you start developing in earnest.

  1. Anatomy of a gcloud command and the help system

Every gcloud command follows the same structure, and understanding it is what turns the CLI into something you can work out rather than something you have to memorise:

gcloud [GROUP] [SUBGROUP...] [ACTION] [POSITIONAL_ARGUMENTS] [--FLAGS]
Part What it is Examples
Group The service or area compute, storage, projects, iam, sql, run, billing
Subgroup The type of resource within the service instances, disks, firewall-rules, buckets
Action The verb list, describe, create, delete, update, add-iam-policy-binding
Arguments The name of the resource alpinashop-dev, web-1
Flags Options --zone=europe-west1-b, --format=json

Examples read according to this structure:

# group=compute, subgroup=instances, action=list
gcloud compute instances list

# group=projects, action=describe, argument=alpinashop-dev
gcloud projects describe alpinashop-dev

# group=compute, subgroup=firewall-rules, action=create, argument=permitir-http
gcloud compute firewall-rules create permitir-http --allow=tcp:80

The verbs are consistent across services, and that consistency is what lets you work out commands you have never seen:

Verb What it does
list Lists resources of the given type
describe Shows the full detail of ONE resource
create Creates a resource
delete Deletes it
update Modifies attributes
add-iam-policy-binding Grants a role
get-iam-policy Shows the permissions policy

If you know that gcloud compute instances list exists, you can work out that gcloud sql instances list and gcloud run services list will exist too. And you would be right.

Asking for help

# See the available top-level groups
gcloud help
# Help for a group: which subgroups and actions it offers
gcloud compute --help
gcloud compute instances --help
# Detailed help for a specific command, with ALL its flags and examples
gcloud compute instances create --help
# Search for commands by keyword when you do not know where something lives
gcloud search-help "budget"
gcloud search-help "service account key"

gcloud search-help is an underrated tool: it searches all the CLI help and returns the relevant commands ordered by relevance. When you do not know which command to use, start there.

Alpha and beta versions

Some commands are only available in preview stages and need a prefix:

gcloud beta run deploy ...
gcloud alpha billing budgets list ...

A rule of thumb: if a command "does not exist", try beta and alpha before giving up on it. And do not use alpha commands in production: their interface can change without notice.

  1. Output formats: --format

By default, gcloud returns a table designed for humans. The --format flag lets you change that output completely, and it is the key to using gcloud inside scripts.

Format What it is for
--format=table(...) A readable table with the columns you choose
--format=json Full JSON, ideal for processing with jq
--format=yaml YAML, convenient to read for complex objects
--format=value(...) Just the values, with no headers: the format for scripts
--format=csv(...) CSV, for spreadsheets
--format=flattened Every field as a key-value pair, one per line

Applied examples:

# A bespoke table: only the fields you care about
gcloud projects list \
  --format="table(projectId, name, projectNumber, lifecycleState)"
# Full JSON for a resource: useful for discovering which fields exist
gcloud projects describe alpinashop-dev --format=json
# Just one value, with no header or decoration: perfect for storing in a variable
PROJECT_NUMBER=$(gcloud projects describe alpinashop-dev \
  --format="value(projectNumber)")
echo "Project number: $PROJECT_NUMBER"
# Several values on one line, separated by tabs
gcloud projects list --format="value(projectId, projectNumber)"

Useful functions inside --format, which save a lot of post-processing:

# basename() extracts the last segment of a resource URL.
# Without it, the zone appears as a long API URL.
gcloud compute instances list \
  --format="table(name, zone.basename(), status, machineType.basename())"
# Change the column names and sort the output
gcloud projects list \
  --format="table[box](projectId:label=ID, name:label=NAME)" \
  --sort-by=projectId

The rule worth committing to memory: table for you to read, value for a script to read, json to process with jq.

  1. Filters: --filter

--filter narrows down the set of results on the server or the client side depending on the command, saving you from filtering with grep, which is fragile.

Available operators:

Operator Meaning Example
= Equal --filter="status=RUNNING"
!= Not equal --filter="status!=TERMINATED"
>, <, >=, <= Numeric or date comparison --filter="creationTimestamp>2026-01-01"
: Contains / has the key --filter="labels.entorno:*"
~ Matches a regular expression --filter="name~^web-"
!~ Does not match the regex --filter="name!~^test-"
AND, OR, NOT Logical combination --filter="status=RUNNING AND zone:europe-west1"
( ) Grouping --filter="(a=1 OR b=2) AND c=3"

Examples applied to AlpinaShop:

# Running instances in any zone of europe-west1
gcloud compute instances list \
  --filter="status=RUNNING AND zone:europe-west1" \
  --format="table(name, zone.basename(), status)"
# Projects labelled entorno=desarrollo
gcloud projects list \
  --filter="labels.entorno=desarrollo" \
  --format="table(projectId, name)"
# All enabled APIs related to storage or databases
gcloud services list --enabled \
  --filter="config.name~storage OR config.name~sql" \
  --format="value(config.name)"

How to discover which fields you can filter on: look at the JSON output first. The JSON field names are exactly the ones --filter accepts.

# Step 1: see the object's full structure
gcloud compute instances describe web-1 --zone=europe-west1-b --format=json

# Step 2: filter using the field names you have seen
gcloud compute instances list --filter="machineType~e2-micro"

  1. Authentication: gcloud init, auth login and application-default

This section clears up one of the most common confusions among beginners.

gcloud init

It is the initial setup wizard. It does three things in one: it authenticates you, it lets you choose a default project and it lets you choose a default region and zone.

gcloud init

Use it the first time you configure the CLI on a machine, or when you want to create a new configuration from scratch.

gcloud auth login versus gcloud auth application-default login

They are two different credentials, for two different consumers:

gcloud auth login gcloud auth application-default login
Who it is for For the gcloud command and other SDK tools For your code: Python, Java, Go client libraries and so on
What it stores SDK user credentials An Application Default Credentials (ADC) file
Where gcloud's internal configuration ~/.config/gcloud/application_default_credentials.json
Used by gcloud, gsutil, bq The code using google-cloud-* on your machine
When you need it Always, in order to administer Only if you develop locally against Google Cloud APIs
# Authenticate the CLI
gcloud auth login
# Authenticate the CODE you run on your machine (ADC)
gcloud auth application-default login
# See which accounts are authenticated and which one is active
gcloud auth list

The classic scenario that confuses everyone: Dani runs gcloud storage ls and it works, but his Python script fails with a credentials error. The cause is that he ran gcloud auth login but not gcloud auth application-default login: the client library looks for the ADC, not for the CLI's credentials.

Service accounts

For automated processes (a CI/CD pipeline, a scheduled task) user credentials are not used, service accounts are. And within Google Cloud, the right approach is not to download keys: a VM, a Cloud Run service or a GKE pod obtain credentials automatically from the service account associated with them.

# Authenticate with a service account from a key file.
# Avoid this whenever you can: downloaded keys are a security risk.
gcloud auth activate-service-account \
  --key-file=/path/key.json

Service accounts, their roles and the secure alternatives to downloaded keys (Workload Identity Federation) are studied in lesson 03-04.

  1. Named configurations

A gcloud configuration is a set of properties: active account, project, default region and zone. You can have several named ones and jump between them with a single command, which elegantly solves the problem of switching between alpinashop-dev and alpinashop-prod.

# See the existing configurations and which one is active
gcloud config configurations list
# Create the development configuration
gcloud config configurations create alpinashop-dev
gcloud config set account [email protected]
gcloud config set project alpinashop-dev
gcloud config set compute/region europe-west1
gcloud config set compute/zone europe-west1-b
# Create the production configuration
gcloud config configurations create alpinashop-prod
gcloud config set account [email protected]
gcloud config set project alpinashop-prod
gcloud config set compute/region europe-west1
gcloud config set compute/zone europe-west1-b
# Switch from one to the other
gcloud config configurations activate alpinashop-dev
# See the full active configuration
gcloud config list

Two highly recommended complementary techniques:

# Run ONE command with another configuration without changing the active one
gcloud compute instances list --configuration=alpinashop-prod
# Override the project for a single command only
gcloud compute instances list --project=alpinashop-prod

And a tip that prevents serious accidents: add the active project to your bash prompt. Seeing on every line which project you are working on is the best protection against running something meant for development in production.

# Add to ~/.bashrc: shows the active project in the prompt
export PS1='[$(gcloud config get-value project 2>/dev/null)] \w\$ '

  1. Other SDK tools

The Google Cloud CLI includes several tools besides gcloud:

Tool What for Status
gcloud General Google Cloud administration The main one
gcloud storage Cloud Storage operations Currently recommended: faster than gsutil
gsutil The classic Cloud Storage tool Still works; it is being replaced by gcloud storage
bq BigQuery queries and administration The standard for BigQuery
kubectl Kubernetes / GKE administration The Kubernetes standard, not Google-specific
# Cloud Storage: list buckets (current form and classic form)
gcloud storage ls
gsutil ls
# BigQuery: list the project's datasets
bq ls
# GKE: get a cluster's credentials so that kubectl can be used
gcloud container clusters get-credentials alpinashop-cluster \
  --region=europe-west1
kubectl get nodes

These tools will be used in depth in their corresponding modules: Cloud Storage in 02-02, GKE in 02-05 and BigQuery in 04-01. Here we simply note that they exist and that they are installed together.

  1. Basic scripting with gcloud

Combining --format=value(...) with bash turns gcloud into a powerful automation tool. The fundamental pattern is always the same: list with a filter, extract a clean value, iterate.

# Store a value in a variable
PROJECT_ID=$(gcloud config get-value project)
PROJECT_NUMBER=$(gcloud projects describe "$PROJECT_ID" \
  --format="value(projectNumber)")

echo "Project: $PROJECT_ID (number $PROJECT_NUMBER)"
# Iterate over the results of a listing.
# This loop goes through all stopped instances and shows their name and zone.
gcloud compute instances list \
  --filter="status=TERMINATED" \
  --format="value(name, zone.basename())" |
while read -r NAME ZONE; do
  echo "Stopped instance: $NAME in $ZONE"
  # Here you could run, for example:
  # gcloud compute instances delete "$NAME" --zone="$ZONE" --quiet
done
# The --quiet flag answers "yes" to every confirmation.
# Essential in unattended scripts, DANGEROUS when typing by hand.
gcloud compute instances delete web-pruebas --zone=europe-west1-b --quiet
# Combine gcloud with jq for more complex queries over the JSON
gcloud projects list --format=json |
  jq -r '.[] | select(.labels.entorno == "desarrollo") | .projectId'

Good practices for gcloud scripts:

  • Always start with set -e so that the script stops as soon as a command fails, instead of carrying on over an inconsistent state.
  • State the project explicitly with --project in important scripts, rather than relying on the active configuration. A script that deletes resources and trusts the active configuration is an accident waiting to happen.
  • Try it first without --quiet, so you can see what it is going to ask you and which resources it would act on.
  • Many commands accept --dry-run or have read-only equivalents: use them before running destructive operations.

  1. The first real deployment: AlpinaShop's "coming soon" page

The moment has come to put something on the internet. Dani is going to publish a static "coming soon" page while the team prepares the migration, using Cloud Storage to host it. It is a deliberate preview of the service studied in depth in lesson 02-02: here we use it as an exercise that pulls together everything learned in the module, without going into storage classes, lifecycles or versioning.

Step 1. Prepare the environment. Open Cloud Shell and confirm where you are:

gcloud config set project alpinashop-dev
gcloud config set compute/region europe-west1
gcloud config list

Step 2. Create the page. Use the editor or the terminal directly:

mkdir -p ~/alpinashop-proximamente && cd ~/alpinashop-proximamente

cat > index.html <<'HTML'
<h1>AlpinaShop</h1>
<p>Mountaineering gear. We are getting our new online shop ready.</p>
<p>Coming here very soon.</p>
HTML

cat > 404.html <<'HTML'
<h1>Page not found</h1>
<p>Go back to the <a href="/">AlpinaShop home page</a>.</p>
HTML

The cat > file <<'HTML' ... HTML block is a here-document: it writes into the file everything between the two markers. Putting the marker in single quotes prevents bash from trying to interpret $ or quotes inside the content.

Step 3. Create the bucket. Bucket names are unique across the whole of Google Cloud, just like projectIds, so we add a suffix to avoid collisions:

# Generates a unique name by adding a random suffix
export BUCKET="alpinashop-proximamente-$RANDOM"
echo "Bucket: $BUCKET"

gcloud storage buckets create "gs://$BUCKET" \
  --location=europe-west1 \
  --uniform-bucket-level-access

A breakdown of the command:

  • gs:// is the Cloud Storage URI scheme.
  • --location=europe-west1 sets the region. Remember from lesson 01-05: a bucket's location is immutable.
  • --uniform-bucket-level-access disables per-object access control lists and makes all permissions be managed through IAM alone. It is the recommended option because it hugely simplifies reasoning about who can see what.

Step 4. Upload the files:

gcloud storage cp index.html 404.html "gs://$BUCKET/"
gcloud storage ls "gs://$BUCKET/"

Step 5. Make the content public. This step grants read access to anyone on the internet. It is exactly what we want for a public page, and exactly what you must never do with a bucket containing internal data:

gcloud storage buckets add-iam-policy-binding "gs://$BUCKET" \
  --member=allUsers \
  --role=roles/storage.objectViewer

allUsers is a special IAM identifier meaning "anyone, authenticated or not". The roles/storage.objectViewer role grants read-only access to objects: it does not allow the bucket configuration to be listed or anything to be written.

Step 6. Configure the bucket as a website and check the result:

gcloud storage buckets update "gs://$BUCKET" \
  --web-main-page-index=index.html \
  --web-error-page=404.html
# Check from the terminal itself that the page is being served
curl -s "https://storage.googleapis.com/$BUCKET/index.html"
# Print the URL so you can open it in the browser
echo "https://storage.googleapis.com/$BUCKET/index.html"

If curl returns the HTML you wrote, you have just published your first content on Google Cloud: you have created a resource in the right region, configured its permissions and verified it, all from the browser and without installing anything.

Step 7. Clean up. Essential: do not leave resources running that you are not going to use.

# Deletes the bucket and all its contents
gcloud storage rm --recursive "gs://$BUCKET"

What we have not done here, and will come later: serving the page under the alpinashop.example domain with a TLS certificate (it requires a load balancer, lesson 03-07), speeding it up with Cloud CDN (03-03), choosing a storage class and lifecycle rules (02-02) and automating the deployment with Cloud Build (06-01).

Common Mistakes and Tips

  • Confusing gcloud auth login with gcloud auth application-default login. The first authenticates the CLI; the second, your code. If your Python script fails with a credentials error and gcloud works, this is why.
  • Installing packages in Cloud Shell and losing them. Only $HOME persists. Use ~/.customize_environment for whatever you need on every start-up.
  • Running a command in the wrong project. Use named configurations, put the project in your prompt and add an explicit --project in dangerous scripts.
  • Filtering with grep instead of --filter. grep depends on the output format, which can change; --filter operates on the object's real fields.
  • Using --quiet when typing by hand. It is designed for unattended scripts. Interactively, those confirmations are your last line of defence.
  • Assuming a command does not exist. Try gcloud search-help, and try the beta and alpha prefixes.
  • Working with an out-of-date CLI. Many strange errors are resolved with gcloud components update.
  • Tip: learn --format=json for exploring. It is how you discover what fields a resource has and therefore which fields you can filter on and what you can extract.
  • Tip: save your commands in scripts from the start. What you type by hand today, you will repeat tomorrow; and in module 6 you will turn it into a pipeline.
  • Tip: use the console's "Equivalent command line" link. It remains the fastest way to learn new commands.

Exercises

Exercise 1: mastering --format and --filter

Write the gcloud commands that solve the following. Run them in your project to check them:

  1. Show all your projects in a table with just the ID and the project number.
  2. Store the number of the alpinashop-dev project in a bash variable called NUM, with no headers or extra text.
  3. List the enabled APIs whose name contains storage or sql, showing only the name.
  4. List the zones of the europe-west1 region showing name and status in table format.
  5. Find out, without leaving the terminal, which gcloud command is used to manage billing budgets.

Exercise 2: configurations and authentication

Marta needs to work on two projects and wants to avoid accidents.

  1. Create two named configurations, alpinashop-dev and alpinashop-prod, each with its project, the europe-west1 region and the europe-west1-b zone.
  2. Write the command to list the production instances without changing the active configuration (two different ways of achieving it).
  3. Dani runs gcloud storage ls successfully but his Python script fails with DefaultCredentialsError. Explain the cause and give the command that fixes it.
  4. Propose an additional measure that reduces the risk of accidentally running a destructive command in production.

Exercise 3: full deployment and cleanup

Reproduce the deployment from section 11 with a variation: instead of a fixed page, the home page must show the date it was generated and the region where the bucket is hosted.

  1. Generate the index.html including the current date and the region name.
  2. Create the bucket with a unique name, upload the file, make it public and configure it as a website.
  3. Verify with curl that the content served includes the date.
  4. Write a small script that checks that the bucket exists and shows how many objects it contains.
  5. Delete all the resources created.

Solutions

Solution 1

# 1
gcloud projects list --format="table(projectId, projectNumber)"

# 2
NUM=$(gcloud projects describe alpinashop-dev --format="value(projectNumber)")
echo "$NUM"

# 3
gcloud services list --enabled \
  --filter="config.name~storage OR config.name~sql" \
  --format="value(config.name)"

# 4
gcloud compute zones list \
  --filter="region:europe-west1" \
  --format="table(name, status)"

# 5
gcloud search-help "budget"

Point 5 will return, among other results, gcloud billing budgets, with its create, list, describe, update and delete actions. It is the right way to discover commands without leaving the terminal.

Solution 2

# 1. Development configuration
gcloud config configurations create alpinashop-dev
gcloud config set project alpinashop-dev
gcloud config set compute/region europe-west1
gcloud config set compute/zone europe-west1-b

# Production configuration
gcloud config configurations create alpinashop-prod
gcloud config set project alpinashop-prod
gcloud config set compute/region europe-west1
gcloud config set compute/zone europe-west1-b

# Go back to development as the active configuration
gcloud config configurations activate alpinashop-dev
# 2. Two ways of querying production without changing the active configuration
gcloud compute instances list --configuration=alpinashop-prod
gcloud compute instances list --project=alpinashop-prod

The first uses the complete production configuration (account, project, region); the second only overrides the project and keeps the rest of the active configuration. For read-only operations either will do; if the configurations used different accounts, the first would be the correct one.

  1. The cause is that gcloud auth login authenticates the CLI, but Python client libraries look for the Application Default Credentials, which are a different file. The fix:
gcloud auth application-default login
  1. Several measures are valid, and ideally you combine them: show the active project in the bash prompt (export PS1='[$(gcloud config get-value project)] \w\$ '); always use an explicit --project in scripts that delete or modify resources; do not grant the day-to-day working account deletion permissions in production, applying the least privilege principle (lesson 03-04); and use different browsers or profiles for the development and production consoles.

Solution 3

#!/bin/bash
set -e

REGION="europe-west1"
BUCKET="alpinashop-proximamente-$RANDOM"

# 1. Generate the page with date and region
mkdir -p ~/alpinashop-proximamente && cd ~/alpinashop-proximamente

cat > index.html <<HTML
<h1>AlpinaShop</h1>
<p>Mountaineering gear. New online shop on the way.</p>
<p>Generated on $(date '+%d/%m/%Y %H:%M') | Hosted in the region $REGION</p>
HTML

Note one difference from the example in section 11: here the here-document marker is not in single quotes (<<HTML instead of <<'HTML'), precisely because we want bash to substitute $(date ...) and $REGION before writing the file.

# 2. Create the bucket, upload, publish and configure as a website
gcloud storage buckets create "gs://$BUCKET" \
  --location="$REGION" \
  --uniform-bucket-level-access

gcloud storage cp index.html "gs://$BUCKET/"

gcloud storage buckets add-iam-policy-binding "gs://$BUCKET" \
  --member=allUsers \
  --role=roles/storage.objectViewer

gcloud storage buckets update "gs://$BUCKET" \
  --web-main-page-index=index.html
# 3. Verify the content being served
curl -s "https://storage.googleapis.com/$BUCKET/index.html" | grep "Generated on"
# 4. Check existence and count objects
if gcloud storage buckets describe "gs://$BUCKET" \
     --format="value(name)" > /dev/null 2>&1; then
  OBJECTS=$(gcloud storage ls "gs://$BUCKET/**" | wc -l)
  echo "Bucket $BUCKET exists and contains $OBJECTS object(s)."
else
  echo "Bucket $BUCKET does not exist."
  exit 1
fi

The check uses describe, redirecting the output to /dev/null and evaluating only its exit code: if the bucket does not exist, the command fails and the else branch is taken. The gs://$BUCKET/** pattern lists every object recursively, and wc -l counts them.

# 5. Full cleanup
gcloud storage rm --recursive "gs://$BUCKET"

Conclusion

With this lesson we close module 1, and we do so with something running on the internet. We have learned what Cloud Shell is, what it comes preinstalled with and what its real limits are —especially that only the 5 GB of your $HOME persist—, as well as its web editor and the web preview. We have seen how to install the CLI on Linux, macOS and Windows, and when each option is worth it. We have taken apart the anatomy of a gcloud command into group, subgroup, action and flags, which turns the CLI into something you can work out, and we have learned to ask it for help with --help and gcloud search-help. We have mastered --format (with table for reading, value for scripts and json for exploring) and --filter with all its operators. We have cleared up the difference between gcloud auth login and gcloud auth application-default login, and we have set up named configurations for switching safely between alpinashop-dev and alpinashop-prod. We have met gcloud storage, bq and kubectl, which will accompany us in later modules, and we have written our first scripts chaining gcloud together with bash. And, above all, Dani has published AlpinaShop's "coming soon" page from the browser, checked that it is being served and cleaned up the resources afterwards.

Looking back over the whole module: we understood what the cloud is and why AlpinaShop needs it, we opened the account with safety-net budgets from day one, we learned to find our way around the console, we designed the project hierarchy and the billing, we chose europe-west1 as our region and grasped which part of security is ours, and now we know how to handle the tools. The ground is prepared and we know how to use the machinery.

In module 2, Core GCP Services, we start building in earnest: we will create virtual machines with Compute Engine, move the 60 GB of product images to the alpinashop-catalogo Cloud Storage bucket, migrate the tienda database to the Cloud SQL instance alpinashop-pedidos, get to know App Engine and the alpinashop-cluster cluster in Google Kubernetes Engine, explore the NoSQL databases and finish with the criteria for choosing the right compute service in each case. AlpinaShop's migration stops being a plan and starts being code.

Google Cloud Platform (GCP) Course

Module 1: Introduction to Google Cloud Platform

Module 2: Core GCP Services

Module 3: Networking and Security

Module 4: Data and Analytics

Module 5: Machine Learning and AI

Module 6: DevOps and Monitoring

Module 7: Advanced GCP Topics

Module 8: Final Project

© Copyright 2026. All rights reserved