Monitor and Manage Google Cloud Resources

Solution for Monitor and Manage Google Cloud Resources. 1 lab: ARC101. Fast copy-paste commands for Google Cloud.

ARC101 — Monitor and Manage Google Cloud Resources: Challenge Lab

Estimated time: 1 hour 15 minutes

# 🚀 Monitor and Manage Google Cloud Resources: Challenge Lab > ⚠️ **Disclaimer:** This is an independent, community-made walkthrough built to help you understand why each step works. Attempt the challenge yourself first. This guide is provided for educational purposes only and is not affiliated with or endorsed by Google Cloud or Google Cloud Skills Boost. Always follow the official Google Cloud and Qwiklabs terms of service, lab instructions, and usage policies. > > **Walkthrough by Orbit of

# ==============================================================================
# ORBIT OF OPS - ARC101 (COMMAND 1 OF 2: INFRASTRUCTURE & IAM FIX)
# ==============================================================================
CYAN='\e[1;36m'
BLUE='\e[1;34m'
YELLOW='\e[1;33m'
GREEN='\e[1;32m'
RESET='\e[0m'
BOLD='\e[1m'

clear
echo -e "${CYAN}${BOLD}>>> ORBIT OF OPS: ARC101 INITIATED (PART 1) <<<${RESET}\n"

export PROJECT_ID=$(gcloud config get-value project 2>/dev/null)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)' 2>/dev/null)

echo -e "${YELLOW}${BOLD}--- REQUIRED LAB INPUTS ---${RESET}"
read -p "1. Enter the Region (e.g., us-central1): " REGION
read -p "2. Enter the Bucket Name: " BUCKET_NAME
read -p "3. Enter the User 2 Email: " USER_2
read -p "4. Enter the Pub/Sub Topic Name: " TOPIC_NAME
read -p "5. Enter the Cloud Run Function Name: " FUNCTION_NAME
read -p "6. Enter an Alert Notification Email: " ALERT_EMAIL

export REGION BUCKET_NAME USER_2 TOPIC_NAME FUNCTION_NAME ALERT_EMAIL

echo -e "\n${BLUE}${BOLD}[Orbit of Ops] Enabling APIs...${RESET}"
gcloud services enable artifactregistry.googleapis.com cloudfunctions.googleapis.com cloudbuild.googleapis.com eventarc.googleapis.com run.googleapis.com pubsub.googleapis.com storage.googleapis.com monitoring.googleapis.com --quiet

echo -e "${BLUE}${BOLD}[Orbit of Ops] Force-Patching IAM Roles to fix Eventarc 403 Errors...${RESET}"
GCS_SA="service-${PROJECT_NUMBER}@gs-project-accounts.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding $PROJECT_ID --member="serviceAccount:${GCS_SA}" --role="roles/pubsub.publisher" --quiet >/dev/null 2>&1

COMPUTE_SA="${PROJECT_NUMBER}[email protected]"
gcloud projects add-iam-policy-binding $PROJECT_ID --member="serviceAccount:${COMPUTE_SA}" --role="roles/eventarc.eventReceiver" --quiet >/dev/null 2>&1
gcloud projects add-iam-policy-binding $PROJECT_ID --member="serviceAccount:${COMPUTE_SA}" --role="roles/artifactregistry.reader" --quiet >/dev/null 2>&1
gcloud projects add-iam-policy-binding $PROJECT_ID --member="serviceAccount:${COMPUTE_SA}" --role="roles/storage.admin" --quiet >/dev/null 2>&1

EVENTARC_SA="service-${PROJECT_NUMBER}@gcp-sa-eventarc.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding $PROJECT_ID --member="serviceAccount:${EVENTARC_SA}" --role="roles/eventarc.serviceAgent" --quiet >/dev/null 2>&1
gcloud projects add-iam-policy-binding $PROJECT_ID --member="serviceAccount:${EVENTARC_SA}" --role="roles/storage.admin" --quiet >/dev/null 2>&1

echo -e "\n${CYAN}${BOLD}[Orbit of Ops] Task 1: Creating Storage Bucket and Granting User 2 Access...${RESET}"
gcloud storage buckets create gs://$BUCKET_NAME --location=$REGION --quiet 2>/dev/null
gcloud projects add-iam-policy-binding $PROJECT_ID --member="user:$USER_2" --role="roles/storage.objectViewer" --quiet

echo -e "${CYAN}${BOLD}[Orbit of Ops] Task 2: Creating the Pub/Sub Topic...${RESET}"
gcloud pubsub topics create $TOPIC_NAME --quiet 2>/dev/null

echo -e "${CYAN}${BOLD}[Orbit of Ops] Preparing Cloud Run Function Code...${RESET}"
mkdir -p ~/thumbnail_app && cd ~/thumbnail_app

cat > index.js <<EOF
/* globals exports, require */
//jshint strict: false
//jshint esversion: 6
"use strict";
const crc32 = require("fast-crc32c");
const { Storage } = require('@google-cloud/storage');
const gcs = new Storage();
const { PubSub } = require('@google-cloud/pubsub');
const imagemagick = require("imagemagick-stream");

exports.thumbnail = (event, context) => {
  const fileName = event.name;
  const bucketName = event.bucket;
  const size = "64x64"
  const bucket = gcs.bucket(bucketName);
  const topicName = "${TOPIC_NAME}";
  const pubsub = new PubSub();
  if ( fileName.search("64x64_thumbnail") == -1 ){
    var filename_split = fileName.split('.');
    var filename_ext = filename_split[filename_split.length - 1];
    var filename_without_ext = fileName.substring(0, fileName.length - filename_ext.length );
    if (filename_ext.toLowerCase() == 'png' || filename_ext.toLowerCase() == 'jpg'){
      console.log(\`Processing Original: gs://\${bucketName}/\${fileName}\`);
      const gcsObject = bucket.file(fileName);
      let newFilename = filename_without_ext + size + '_thumbnail.' + filename_ext;
      let gcsNewObject = bucket.file(newFilename);
      let srcStream = gcsObject.createReadStream();
      let dstStream = gcsNewObject.createWriteStream();
      let resize = imagemagick().resize(size).quality(90);
      srcStream.pipe(resize).pipe(dstStream);
      return new Promise((resolve, reject) => {
        dstStream
          .on("error", (err) => {
            console.log(\`Error: \${err}\`);
            reject(err);
          })
          .on("finish", () => {
            console.log(\`Success: \${fileName} → \${newFilename}\`);
              gcsNewObject.setMetadata(
              {
                contentType: 'image/'+ filename_ext.toLowerCase()
              }, function(err, apiResponse) {});
              pubsub
                .topic(topicName)
                .publisher()
                .publish(Buffer.from(newFilename))
                .then(messageId => {
                  console.log(\`Message \${messageId} published.\`);
                })
                .catch(err => {
                  console.error('ERROR:', err);
                });
          });
      });
    }
  }
};
EOF

cat > package.json <<EOF
{
  "name": "thumbnails",
  "version": "1.0.0",
  "description": "Create Thumbnail of uploaded image",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "@google-cloud/pubsub": "^2.0.0",
    "@google-cloud/storage": "^5.0.0",
    "fast-crc32c": "1.0.4",
    "imagemagick-stream": "4.1.1"
  },
  "devDependencies": {},
  "engines": {
    "node": ">=4.3.2"
  }
}
EOF

echo -e "${YELLOW}${BOLD}Installing npm dependencies...${RESET}"
npm install --quiet

echo -e "\n${GREEN}${BOLD}🎉 COMMAND 1 COMPLETE! Check progress for Tasks 1 & 2, then proceed to Command 2.${RESET}"
# ==============================================================================
# ORBIT OF OPS - ARC101 (COMMAND 2 OF 2: DEPLOY & ALERTING)
# ==============================================================================
CYAN='\e[1;36m'
YELLOW='\e[1;33m'
GREEN='\e[1;32m'
MAGENTA='\e[1;35m'
RESET='\e[0m'
BOLD='\e[1m'

cd ~/thumbnail_app

echo -e "${YELLOW}${BOLD}[Orbit of Ops] Waiting 60 seconds for IAM policies to propagate...${RESET}"
sleep 60

echo -e "${MAGENTA}${BOLD}[Orbit of Ops] Task 3: Deploying Cloud Run Function (Takes ~3 mins)...${RESET}"
gcloud functions deploy $FUNCTION_NAME \
    --gen2 \
    --runtime=nodejs22 \
    --region=$REGION \
    --source=. \
    --entry-point=thumbnail \
    --trigger-bucket=$BUCKET_NAME \
    --max-instances=1 \
    --quiet

echo -e "\n${CYAN}${BOLD}[Orbit of Ops] Testing Infrastructure (Triggering the function)...${RESET}"
wget -q https://storage.googleapis.com/cloud-training/arc101/travel.jpg
gcloud storage cp travel.jpg gs://$BUCKET_NAME/ --quiet 2>/dev/null

echo -e "\n${YELLOW}${BOLD}[Orbit of Ops] Task 4: Creating Alert Policy...${RESET}"
CHANNEL_ID=$(gcloud beta monitoring channels create \
    --display-name="Personal Email" \
    --type=email \
    --channel-labels=email_address=${ALERT_EMAIL} \
    --format="value(name)")

cat > active-instances-policy.json <<EOF
{
  "displayName": "Active Cloud Run Function Instances",
  "combiner": "OR",
  "conditions": [
    {
      "displayName": "Cloud Function Active Instances",
      "conditionThreshold": {
        "filter": "resource.type=\"cloud_function\" AND metric.type=\"cloudfunctions.googleapis.com/function/active_instances\"",
        "aggregations": [
          {
            "alignmentPeriod": "60s",
            "perSeriesAligner": "ALIGN_MAX"
          }
        ],
        "comparison": "COMPARISON_GT",
        "thresholdValue": 0,
        "duration": "0s"
      }
    }
  ],
  "notificationChannels": ["$CHANNEL_ID"]
}
EOF

gcloud alpha monitoring policies create --policy-from-file="active-instances-policy.json" --quiet
echo -e "${GREEN}✅ Alerting Policy created successfully!${RESET}"

echo -e "\n${GREEN}${BOLD}🎉 PIPELINE AUTOMATION COMPLETE! You can now safely click 'Check my progress' on Tasks 3 and 4.${RESET}"