GCP: Overview
College Credits
Google learning and cloud content divides into at least the following kinds:
- Google Certification
- Proctored examinations through Pearson.
- These are not ACE College Credit eligible in-of-themselves (although they are more valuable professionally).
- Google Certificates
- Through skills.google/
- Not all Google Courses, Labs, etc. result in a Certificate.
- In fact only a handful do - and of these only the those described here are ACE College Credit eligible.
- Coursera Certificates
- These are offered through Coursera but don't automatically confer a Google Certificate nor a Google Certification.
- These don't appear to be ACE College Credit eligible.
Idioms
Ways that Google defines/does things:
- PaaS - a "type of cloud computing service lets you bind your application code to libraries that give access to the infrastructure your application needs".
- FaaS - Function-as-a-Service a subset of PaaS.
- Virtual Private Cloud (VPC) - a "secure, individual, private cloud-computing model hosted within a public cloud".
Some Quick Comparison with AWS
Subnets
- Are actually Regional (spanning multiple Zones) in GCP.
- (In AWS, these are restricted to single Available Zones.)
Cloud Load Balancer
- Load balances between Compute Engine Regions in GCP.
- (In AWS, these typically load balance within the same Region althought there are some exceptions)
Projects
- Permissions, environments, etc. are usually grouped by Project in GCP.
- (In AWS, these are usually grouped by Account or Organizational Unit.)
Billing
- Projects require a Billing Account to use paid features.
- Billing Account are associated with one or more Projects.
Google App Engine's most direct comparison is AWS Lightsail.
- Developers focus solely on source code (rather than the underlying infrastructure), to quickly skaffold and deploy apps.
GKE vs. EKS
- Respective Kubernetes Services
- GKE is classified as a PaaS using GCP's classification taxonomies.
Cost Summary
- Generally, AWS is better for high-traffic, resource and data intensive Enterprise operations (it has slightly better discount data and I/O rates, better Instance Reservation discounts, and better pricing for Large Tier Instances and SSD).
- At scale, GCP quickly becomes more expensive.
- GCP is slightly more cost-performant (roughly
10%) for low-volume, low-traffic, Applications demanding smaller Containers and Compute Instances.- It's also often considered to have better NVIDIA and GPU support although AWS has continued to mature its offerings.
Studies, models, cost comparisons:
- https://www.qovery.com/blog/google-cloud-vs-aws-cost-breakdown-by-service
- https://spot.rackspace.com/blog/cloud-computing-cost
- https://www.usage.ai/blogs/finops/multi-cloud/cloud-pricing-comparison/
Resources and Links
- https://docs.aws.amazon.com/whitepapers/latest/real-time-communication-on-aws/cross-region-dns-based-load-balancing-and-failover.html
- https://www.acenet.edu/National-Guide/Pages/Organization.aspx?oid=3fdb1492-e04f-e911-a97b-000d3a363c81
- https://www.qovery.com/blog/google-cloud-vs-aws-cost-breakdown-by-service
- https://spot.rackspace.com/blog/cloud-computing-cost
- https://www.usage.ai/blogs/finops/multi-cloud/cloud-pricing-comparison/
GCP: Google Cloud Computing Foundations Certificate
Cloud Computing Fundamentals
Notes.
Best ways to interact with Google Cloud Resources:
- Google Cloud CLI
- Google Cloud Console
- Official Google Cloud Client Libraries and Google API Client Libraries
Useful Google Cloud Shell Commands
Used in many Google Skill Labs and Challenges.
Note that the early Skill Labs and Challenges tend to be Imperative (emphasizing sequences of CLI commands for new users to gain familiarity).
Google Cloud Shell (gcloud through the interactive terminal):
gcloud config set compute/region asia-southeast1
gcloud config get-value compute/region
gcloud config set compute/zone asia-southeast1-a
gcloud config get-value compute/zone
gcloud compute project-info describe --project $(gcloud config get-value project)
# Define an Environment Variable
export PROJECT_ID=$(gcloud config get-value project)
export ZONE=$(gcloud config get-value compute/zone)
export REGION=$(gcloud config get-value compute/region)
echo -e "PROJECT ID: $PROJECT_ID\nZONE: $ZONE\nZONE: $REGION"
Virtual Machines
# Create VM
## Used in several skill labs
gcloud compute instances create www1 --machine-type e2-medium --zone $ZONE
gcloud compute instances create www2 \
--zone=asia-southeast1-b \
--tags=network-lb-tag \
--machine-type=e2-small \
--image-family=debian-12 \
--image-project=debian-cloud \
--metadata=startup-script='#!/bin/bash
apt-get update
apt-get install apache2 -y
service apache2 restart
echo "<h3>Web Server: www1</h3>" | tee /var/www/html/index.html'
## Then list and filter them
gcloud compute instances list
gcloud compute instances list --filter="name=('www1')"
# SSH
gcloud compute ssh www2 --zone $ZONE
# Deploy contents of current directory to App Engine
gcloud app deploy
gcloud app browse
Load Balancers
gcloud config set compute/region asia-southeast1
gcloud config set compute/zone asia-southeast1-b
export ZONE=$(gcloud config get-value compute/zone)
export REGION=$(gcloud config get-value compute/region)
Network Load Balancer:
gcloud compute instances create web1 \
--zone=$ZONE\
--tags=network-lb-tag \
--machine-type=e2-small \
--image-family=debian-12 \
--image-project=debian-cloud \
--metadata=startup-script='#!/bin/bash
apt-get update
apt-get install apache2 -y
service apache2 restart
echo "<h3>Web Server: web1</h3>" | tee /var/www/html/index.html'
gcloud compute firewall-rules create www-firewall-network-lb \
--target-tags network-lb-tag --allow tcp:80
gcloud compute addresses create network-lb-ip-1 \
--region $REGION
gcloud compute http-health-checks create basic-check
gcloud compute target-pools create www-pool \
--region $REGION --http-health-check basic-check
gcloud compute target-pools add-instances www-pool \
--instances web1,web2,web3
gcloud compute forwarding-rules create www-rule \
--region $REGION \
--ports 80 \
--address network-lb-ip-1 \
--target-pool www-pool
Application Load Balancer:
- Managed Instance Group - akin to an Autoscaling Group.
- Backend Service - akin to a Target Group, how the Managed Instance Group is associated with the Load Balancer.
- Target HTTP Proxy - akin to AWS ELB Listeners.
- URL Maps, Global Forwarding Rule - akin to AWS Route 53 Records.
gcloud compute instance-templates create lb-backend-template \
--region=$REGION \
--network=default \
--subnet=default \
--tags=allow-health-check \
--machine-type=e2-medium \
--image-family=debian-12\
--image-project=debian-cloud \
--metadata=startup-script='#!/bin/bash
apt-get update
apt-get install apache2 -y
a2ensite default-ssl
a2enmod ssl
vm_hostname="$(curl -H "Metadata-Flavor:Google" \
http://169.254.169.254/computeMetadata/v1/instance/name)"
echo "Page served from: $vm_hostname" | \
tee /var/www/html/index.html
systemctl restart apache2'
# Managed Instance Group
## Automatically creates and associates VM Instances
## using the Template created above.
gcloud compute instance-groups managed create lb-backend-group \
--template=lb-backend-template --size=2 --zone=$ZONE
# Health Checks, Firewall Rules
## Make sure the Tags match!
gcloud compute firewall-rules create fw-allow-health-check \
--network=default \
--action=allow \
--direction=ingress \
--source-ranges=130.211.0.0/22,35.191.0.0/16 \
--target-tags=allow-health-check \
--rules=tcp:80
gcloud compute addresses create lb-ipv4-1 \
--ip-version=IPV4 \
--global
gcloud compute health-checks create http http-basic-check \
--port 80
# Create the Backend Service...
gcloud compute backend-services create web-backend-service \
--protocol=HTTP \
--port-name=http \
--health-checks=http-basic-check \
--global
## ...and Associate the Managed Instance Group
gcloud compute backend-services add-backend web-backend-service \
--instance-group=lb-backend-group \
--instance-group-zone=asia-southeast1-b \
--global
# Expose the Application Load Balancer
gcloud compute url-maps create web-map-http \
--default-service web-backend-service
## And define listeners
gcloud compute target-http-proxies create http-lb-proxy \
--url-map web-map-http
gcloud compute forwarding-rules create http-content-rule \
--address=lb-ipv4-1\
--global \
--target-http-proxy=http-lb-proxy \
--ports=80
Big Query Dataflow
gcloud services disable dataflow.googleapis.com --project qwiklabs-gcp-02-3087b0d6e7e5 --force
gcloud services enable dataflow.googleapis.com --project qwiklabs-gcp-02-3087b0d6e7e5
bq mk lab_737
## Create Table in the UI
## Create Bucket in the UI
## Create Dataflow from batch template "Text Files on Cloud Storage to BigQuery"
Managed Apache Spark
gcloud dataproc clusters create example-cluster \
--region=us-central1 \
--master-machine-type=n2d-standard-2 \
--master-boot-disk-type=pd-standard \
--master-boot-disk-size=100GB \
--worker-machine-type=n2d-standard-2 \
--worker-boot-disk-type=pd-standard \
--worker-boot-disk-size=100GB \
--num-workers=2
## Create a Job through the UI
Google Cloud Speech-to-Text
## Create an API Key
export API_KEY=<YOUR_API_KEY>
## SSH into a Linux VM
touch request.json
nano request.json
## Copy contents into request.json
### {
### "config": {
### "encoding":"FLAC",
### "languageCode": "en-US"
### },
### "audio": {
### "uri":"gs://cloud-samples-tests/speech/brooklyn.flac"
### }
### }
curl -s -X POST -H "Content-Type: application/json" --data-binary @request.json \
"https://speech.googleapis.com/v1/speech:recognize?key=${API_KEY}"
curl -s -X POST -H "Content-Type: application/json" --data-binary @request.json \
"https://speech.googleapis.com/v1/speech:recognize?key=${API_KEY}" > result.json
## Upload and rename result.json to and within the relevant bucket
Cloud Natural Language API
export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value core/project)
gcloud iam service-accounts create my-natlang-sa \
--display-name "my natural language service account"
gcloud iam service-accounts keys create ~/key.json \
--iam-account my-natlang-sa@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com
export GOOGLE_APPLICATION_CREDENTIALS="/home/USER/key.json"
## SSH into a VM
gcloud ml language analyze-entities --content="Old Norse texts portray Odin as one-eyed and long-bearded, frequently wielding a spear named Gungnir and wearing a cloak and a broad hat." > result.json
## Upload and rename result.json to and within the relevant bucket
Secure a Bastion
export ZONE="asia-south1-b"
export REGION="asia-south1"
export TAG_ONE=accept-ssh-iap-ingress-ql-609
export TAG_TWO=accept-http-ingress-ql-609
export TAG_THREE=accept-ssh-internal-ingress-ql-609
gcloud compute firewall-rules delete open-access
gcloud compute instances start bastion --zone=$ZONE
gcloud compute instances add-tags bastion \
--zone=$ZONE \
--tags=$TAG_ONE
gcloud compute firewall-rules create allow-ssh-iap-ingress \
--network acme-vpc \
--direction=INGRESS \
--action=ALLOW \
--rules=tcp:22 \
--source-ranges=35.235.240.0/20 \
--target-tags=$TAG_ONE
gcloud compute instances add-tags juice-shop \
--zone=$ZONE \
--tags=$TAG_TWO
gcloud compute firewall-rules create allow-http-juice-shop \
--network acme-vpc \
--direction=INGRESS \
--action=ALLOW \
--rules=tcp:80 \
--source-ranges=0.0.0.0/0 \
--target-tags=$TAG_TWO
gcloud compute instances add-tags juice-shop \
--zone=$ZONE \
--tags=$TAG_THREE
## Subnet must be represented by exact IP Range
### Fetch it
export MGMT_SUBNET_RANGE=$(gcloud compute networks subnets describe acme-mgmt-subnet \
--region=$REGION \
--format="value(ipCidrRange)")
gcloud compute firewall-rules create allow-ssh-internal-ingress \
--network=acme-vpc \
--direction=INGRESS \
--action=ALLOW \
--rules=tcp:22 \
--source-ranges=$MGMT_SUBNET_RANGE \
--target-tags=$TAG_THREE
## SSH in then run
gcloud compute ssh juice-shop --internal-ip
Compute Services Comparison
| Service | Description | Workload | Type |
|---|---|---|---|
| GCP Compute Engine | Configurable Virtual Machines. | VM's | IaaS |
| GCP App Engine | Quickly deploying Applications from Source Code. | Managed Serverless Applications | PaaS |
| GCP Cloud Run | Deploy Fully Managed Containerized Applications. | Managed Serverless Containers | PaaS |
| GCP Cloud Run Functions | Serverless Function execution environment. | Serverless Functions | FaaS (PaaS) |
| GCP Google Kubernetes Engine | Managed Kubernetes Service. | Serverless Kubernetes, Containers/Pods | PaaS |
GCP Databases
| Service | Description | Type |
|---|---|---|
| GCP Spanner | Globally distributed, strongly consistent SQL relational database for structured data. | Managed, SQL |
| GCP Firestore | Serverless NoSQL document database for semi-structured and unstructured data. | Managed, NoSQL |
| GCP Bigtable | Scalable NoSQL wide-column database for large-scale, low-latency structured and semi-structured data. | Managed, NoSQL |
| GCP Cloud Storage | Scalable object storage for unstructured data such as files, images, videos, and backups. | Managed, Blob |
| GCP Cloud SQL | Managed relational database service for MySQL, PostgreSQL, and SQL Server with structured data. | Managed, SQL |
Resources and Links
GCP: Associate Cloud Engineer Certification
https://cloud.google.com/learn/certification/cloud-engineer/
XLA
Accelerated Linear Algebra (XLA) integrates with existing, widely used, ML libraries while offering deep compiler-level optimizations:
PyTorch/XLAJAX/XLA
Google Cloud Dynamic Workload Scheduler
| Consumption option | Best for | Capacity assurance | Lifespan | Preemptible | Quota | Discounts |
|---|---|---|---|---|---|---|
| Flex-start | Short-duration workloads up to seven days that can wait for capacity. | Best-effort | Up to seven days | No | Preemptible quota | Discounted (up to 53%) on supported series |
| Spot VMs | Fault-tolerant, short-duration general GPU workloads. | Best-effort | Preemptible | Yes | Preemptible quota | Deeply discounted (up to 91%) |
| Standard Reservations | Critical general GPU workloads that require a very high level of assurance for capacity. | Very high | Very high | User-defined | No | No quota consumed |
| Future Reservations | Large-scale, long-running training on clustered GPU. | Very high | Unlimited within the reservation period. | No | Automatically increased | Discounted (up to 53%) with CUDs |
| Future Reservations in Calendar Mode | Clustered GPU workloads up to 90 days that require reserved capacity. | Very high | Up to 90 days | No | No quota consumed | Discounted (up to 53%) |
| On-Demand VMs | General GPU workloads with no specific duration. | Best-effort | Unlimited | No | On-demand quota | None (pay-as-you-go) |
https://cloud.google.com/blog/products/compute/introducing-dynamic-workload-scheduler
GKE TPU
Kubernetes added DRA in 2025:
ResourceClaimswere added then as well.- Kubernetes workloads are often used to manage AI workloads in the cloud (GKE, TPU, DRA).
See:
- https://www.skills.google/paths/11/course_templates/1403
- https://medium.com/google-cloud/part-i-gke-managed-dranet-with-tpus-ece22b31e4d9
- https://github.com/GoogleCloudPlatform/cluster-toolkit/tree/main/examples/gke-tpu-v6e
GKE
Inference Gateway:
Resources and Links
- https://cloud.google.com/blog/products/compute/introducing-dynamic-workload-scheduler
- https://www.skills.google/paths/11/course_templates/1403
- https://medium.com/google-cloud/part-i-gke-managed-dranet-with-tpus-ece22b31e4d9
- https://github.com/GoogleCloudPlatform/cluster-toolkit/tree/main/examples/gke-tpu-v6e
- https://docs.cloud.google.com/kubernetes-engine/docs/concepts/about-gke-inference-gateway
GCP: Professional Cloud Architect Certification
https://cloud.google.com/learn/certification/cloud-architect/
GKE
| Problem | Solution |
|---|---|
| Need more pods | Horizontal Pod Autoscaler (HPA) |
| Need more nodes | Cluster Autoscaler |
| Pods need different resource requests | Vertical Pod Autoscaler (VPA) |
| Pods are Pending because the cluster lacks capacity | Cluster Autoscaler |
Recollect that Kubernetes Secrets are akin to Kubernetes Config Maps but are specifically intended to store Secrets.
Useful Google Cloud Shell Commands
Used in many Google Skill Labs and Challenges.
GKE
export PROJECT_ID="qwiklabs-gcp-01-00ab3bd88327"
export REGION="us-central1"
export ZONE="us-central1-a"
export CLUSTER_NAME="hello-world-uuyh"
export NAMESPACE_NAME="gmp-t3gp"
export REPO_NAME="demo-repo"
export SERVICE_NAME="helloweb-service-5t69"
export DEFAULT_CLUSTER_VER=$(gcloud container get-server-config)
## Create Cluster with specified settings
gcloud container clusters create $CLUSTER_NAME \
--zone=$ZONE \
--release-channel=regular \
--cluster-version=$DEFAULT_CLUSTER_VER \
--enable-autoscaling \
--num-nodes 3 \
--min-nodes 2 \
--max-nodes 6
## This has to be run for credit
gcloud container clusters update $CLUSTER_NAME \
--zone=$ZONE \
--enable-managed-prometheus
## Create Namespace
kubectl create ns $NAMESPACE_NAME
## Download yaml
gcloud storage cp gs://spls/gsp510/prometheus-app.yaml .
### Edit
nano prometheus-app.yaml
## Deploy into the Namespace
kubectl -n $NAMESPACE_NAME apply -f prometheus-app.yaml
## Download yaml
gcloud storage cp gs://spls/gsp510/pod-monitoring.yaml .
### Edit
nano pod-monitoring.yaml
kubectl -n $NAMESPACE_NAME apply -f pod-monitoring.yaml
## Download yaml
gcloud storage cp -r gs://spls/gsp510/hello-app/ .
### Edit
cd hello-app/manifests
nano helloweb-deployment.yaml
## Deploy
kubectl create -f helloweb-deployment.yaml
## Use this filter in 'Logs explorer'
### "Error: InvalidImageName" OR "Failed to apply default image tag \"<todo>\": couldn't parse image name \"<todo>\": invalid reference format"
### severity="WARNING"
### resource.type="k8s_pod"
## This was unnecessarily convoluted
### The lab says that the repo is already created, it isn't...
### I finally figured out that you have to create it like so
export REPO_NAME=demo-repo
gcloud auth configure-docker $REGION-docker.pkg.dev
gcloud artifacts repositories list
docker build -t $REGION-docker.pkg.dev/$PROJECT_ID/$REPO_NAME/hello-app:v2 .
gcloud artifacts repositories create $REPO_NAME \
--repository-format=docker \
--location=$REGION \
--description="Docker repository for lab"
docker push $REGION-docker.pkg.dev/$PROJECT_ID/$REPO_NAME/hello-app:v2
## Expose the deployment through an external kubernetes loadbalancer
kubectl expose deployment helloweb --name=helloweb-service-5t69 --type=LoadBalancer --port=8080 --target-port=8080
https://docs.cloud.google.com/sdk/gcloud/reference/container/clusters/create
export REGION=europe-west1
export ZONE=europe-west1-c
export PROJECT=qwiklabs-gcp-01-8dd3c7a798ca
gcloud config set compute/region $REGION
gcloud config set compute/zone $ZONE
source <(gcloud storage cat gs://spls/gsp318/script.sh)
gcloud storage cp gs://spls/gsp318/valkyrie-app.tgz .
tar -xzf valkyrie-app.tgz
cd valkyrie-app
touch Dockerfile
nano Dockerfile
## FROM golang:1.10
## WORKDIR /go/src/app
## COPY source .
## RUN go install -v
## ENTRYPOINT ["app","-single=true","-port=8080"]
cat Dockerfile
docker build -t valkyrie-dev:v0.0.1 .
docker images
# Find ImageID
## export IMAGEID=
docker run -p 8080:8080 $IMAGEID &
docker ps -a
gcloud auth configure-docker $REGION-docker.pkg.dev
docker images
# Retag
export REPONAME=valkyrie-repo
## Use this exact name going forward
export IMAGEID=valkyrie-dev:v0.0.1
docker tag valkyrie-dev:v0.0.1 $REGION-docker.pkg.dev/$PROJECT/$REPONAME/$IMAGEID
gcloud artifacts repositories create $REPONAME \
--repository-format=docker \
--location=$REGION \
--description="Docker repository for lab"
## Verify
docker images
## Push
docker push $REGION-docker.pkg.dev/$PROJECT/$REPONAME/$IMAGEID
# Deployment and Expose 80
## Replace with the above
## e.g. - europe-west1-docker.pkg.dev/qwiklabs-gcp-01-8dd3c7a798ca/valkyrie-repo/valkyrie-dev:v0.0.1
nano k8s/deployment.yaml
cat k8s/deployment.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl expose deployment valkyrie-dev --name=valkyrie-dev --type=LoadBalancer --port=80 --target-port=80
# Info
kubectl get pods
gcloud compute instances list
kubectl get services
## Find CONTAINERID
export CONTAINERID=
docker logs $CONTAINERID
TF
https://www.skills.google/paths/12/course_templates/636/labs
This lab uses "Modules" that are actually "Resource" definitions. This was confusing to me.
You have to install tf in the outer shell (not from within the Open Editor).
Create necessary files:
terraform --version
export REGION=europe-west1
export ZONE=europe-west1-d
export PROJECT=qwiklabs-gcp-02-1ec38e8c48e3
touch main.tf
touch variables.tf
mkdir modules && mkdir modules/instances
touch modules/instances/instances.tf
touch modules/instances/outputs.tf
touch modules/instances/variables.tf
mkdir modules/storage
touch modules/storage/storage.tf
touch modules/storage/outputs.tf
touch modules/storage/variables.tf
Create root variables.tf:
cat << EOF > variables.tf
variable "region" {
type = string
default = "$REGION"
}
variable "zone" {
type = string
default = "$ZONE"
}
variable "project_id" {
type = string
default = "$PROJECT"
}
EOF
Prepare instances.tf:
cat << EOF > modules/instances/instances.tf
resource "google_compute_instance" "tfinstance1" {
name = "tf-instance-1"
machine_type = "e2-micro"
zone = "$ZONE"
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
}
}
network_interface {
network = "default"
}
metadata_startup_script = <<-EOT
#!/bin/bash
EOT
allow_stopping_for_update = true
}
resource "google_compute_instance" "tfinstance2" {
name = "tf-instance-2"
machine_type = "e2-micro"
zone = "$ZONE"
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
}
}
network_interface {
network = "default"
}
metadata_startup_script = <<-EOT
#!/bin/bash
EOT
allow_stopping_for_update = true
}
EOF
Prep main.tf:
cat << EOF > main.tf
provider "google" {
project = var.project_id
region = var.region
zone = var.zone
}
module "instances" {
source = "./modules/instances"
}
EOF
So one can import by the instance name: tf-instance-1 rather than the id:
terraform init
terraform fmt
terraform import 'module.instances.google_compute_instance.tfinstance1' projects/$PROJECT/zones/$ZONE/instances/tf-instance-1
terraform import 'module.instances.google_compute_instance.tfinstance2' projects/$PROJECT/zones/$ZONE/instances/tf-instance-2
terraform show
terraform plan
terraform apply --auto-approve
export BUCKET=tf-bucket-963122
cat << EOF > modules/storage/storage.tf
resource "google_storage_bucket" "storage-bucket" {
name = "$BUCKET"
location = "US"
force_destroy = true
uniform_bucket_level_access = true
}
EOF
Prep main.tf:
cat << EOF > main.tf
provider "google" {
project = var.project_id
region = var.region
zone = var.zone
}
module "instances" {
source = "./modules/instances"
}
module "storage" {
source = "./modules/storage"
}
EOF
terraform fmt
terraform init
terraform apply --auto-approve
The Bucket must be created in GCP before switching backends over:
cat << EOF > main.tf
terraform {
backend "gcs" {
bucket = "$BUCKET"
prefix = "terraform/state"
}
}
provider "google" {
project = var.project_id
region = var.region
zone = var.zone
}
module "instances" {
source = "./modules/instances"
}
module "storage" {
source = "./modules/storage"
}
EOF
terraform init -migrate-state
Update instances.tf with the correct tfinstance3.name:
cat << EOF > modules/instances/instances.tf
resource "google_compute_instance" "tfinstance1" {
name = "tf-instance-1"
machine_type = "e2-standard-2"
zone = "$ZONE"
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
}
}
network_interface {
network = "default"
}
metadata_startup_script = <<-EOT
#!/bin/bash
EOT
allow_stopping_for_update = true
}
resource "google_compute_instance" "tfinstance2" {
name = "tf-instance-2"
machine_type = "e2-standard-2"
zone = "$ZONE"
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
}
}
network_interface {
network = "default"
}
metadata_startup_script = <<-EOT
#!/bin/bash
EOT
allow_stopping_for_update = true
}
resource "google_compute_instance" "tfinstance3" {
name = "tf-instance-230370"
machine_type = "e2-standard-2"
zone = "$ZONE"
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
}
}
network_interface {
network = "default"
}
metadata_startup_script = <<-EOT
#!/bin/bash
EOT
allow_stopping_for_update = true
}
EOF
terraform apply
Update instances.tf:
cat << EOF > modules/instances/instances.tf
resource "google_compute_instance" "tfinstance1" {
name = "tf-instance-1"
machine_type = "e2-standard-2"
zone = "$ZONE"
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
}
}
network_interface {
network = "default"
}
metadata_startup_script = <<-EOT
#!/bin/bash
EOT
allow_stopping_for_update = true
}
resource "google_compute_instance" "tfinstance2" {
name = "tf-instance-2"
machine_type = "e2-standard-2"
zone = "$ZONE"
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
}
}
network_interface {
network = "default"
}
metadata_startup_script = <<-EOT
#!/bin/bash
EOT
allow_stopping_for_update = true
}
EOF
terraform apply
export VPC=tf-vpc-803543
Update main.tf with the correct network_name:
cat << EOF > main.tf
terraform {
backend "gcs" {
bucket = "$BUCKET"
prefix = "terraform/state"
}
required_providers {
google = {
source = "hashicorp/google"
version = "~> 7.46.0"
}
}
}
provider "google" {
project = var.project_id
region = var.region
zone = var.zone
}
module "instances" {
source = "./modules/instances"
}
module "storage" {
source = "./modules/storage"
}
module "vpc" {
source = "terraform-google-modules/network/google"
version = "~> 18.2"
project_id = "$PROJECT"
network_name = "$VPC"
routing_mode = "GLOBAL"
subnets = [
{
subnet_name = "subnet-01"
subnet_ip = "10.10.10.0/24"
subnet_region = "$REGION"
},
{
subnet_name = "subnet-02"
subnet_ip = "10.10.20.0/24"
subnet_region = "$REGION"
},
]
}
EOF
Note that the exact combination:
~> 18.2and~> 7.46.0were required to finish the lab (the lab itself states that one must use10.0.0). Otherwise, Provider and version errors will prevent the creation of required resources.
Also note that the lab seemingly requires creating the
vpcin two places you will see an error that the Resource already exists but this can be ignored.
To be clearer it requires the student to create the
vpcModule inmain.tfbefore referring to it withininstances.tf-instances.tf, however, is already referenced bymain.tfso it has to be added toinstances.tf- this strange convention is apparently what others have done to complete the challenge as well.
rm .terraform.lock.hcl
terraform init
terraform apply
Update instances.tf:
cat << EOF > modules/instances/instances.tf
resource "google_compute_instance" "tfinstance1" {
name = "tf-instance-1"
machine_type = "e2-standard-2"
zone = "$ZONE"
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
}
}
network_interface {
network = "$VPC"
subnetwork = "subnet-01"
}
metadata_startup_script = <<-EOT
#!/bin/bash
EOT
allow_stopping_for_update = true
}
resource "google_compute_instance" "tfinstance2" {
name = "tf-instance-2"
machine_type = "e2-standard-2"
zone = "$ZONE"
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
}
}
network_interface {
network = "$VPC"
subnetwork = "subnet-02"
}
metadata_startup_script = <<-EOT
#!/bin/bash
EOT
allow_stopping_for_update = true
}
module "vpc" {
source = "terraform-google-modules/network/google"
version = "~> 18.2"
project_id = "$PROJECT"
network_name = "$VPC"
routing_mode = "GLOBAL"
subnets = [
{
subnet_name = "subnet-01"
subnet_ip = "10.10.10.0/24"
subnet_region = "$REGION"
},
{
subnet_name = "subnet-02"
subnet_ip = "10.10.20.0/24"
subnet_region = "$REGION"
},
]
}
EOF
terraform apply
Update main.tf one last time updating the tf-firewall.name:
cat << EOF > main.tf
terraform {
backend "gcs" {
bucket = "$BUCKET"
prefix = "terraform/state"
}
required_providers {
google = {
source = "hashicorp/google"
version = "~> 7.46.0"
}
}
}
provider "google" {
project = var.project_id
region = var.region
zone = var.zone
}
module "instances" {
source = "./modules/instances"
}
module "storage" {
source = "./modules/storage"
}
module "vpc" {
source = "terraform-google-modules/network/google"
version = "~> 18.2"
project_id = "$PROJECT"
network_name = "$VPC"
routing_mode = "GLOBAL"
subnets = [
{
subnet_name = "subnet-01"
subnet_ip = "10.10.10.0/24"
subnet_region = "$REGION"
},
{
subnet_name = "subnet-02"
subnet_ip = "10.10.20.0/24"
subnet_region = "$REGION"
},
]
}
resource "google_compute_firewall" "tf-firewall"{
name = "tf-firewall"
network = "projects/$PROJECT/global/networks/$VPC"
allow {
protocol = "tcp"
ports = ["80"]
}
source_tags = ["web"]
source_ranges = ["0.0.0.0/0"]
}
EOF
terraform apply
Resources and Links
- https://www.skills.google/paths/12
- https://docs.cloud.google.com/sdk/gcloud/reference/container/clusters/create
- https://registry.terraform.io/providers/hashicorp/google/latest/docs
- https://github.com/tariqsheikhsw/GoogleCloudArchitectLabs/blob/main/Solutions/GSP345.sh <- this was helpful to prep for the PCA