Set Up an App Dev Environment on Google Cloud
Solution for Set Up an App Dev Environment on Google Cloud. 1 lab: GSP315. Fast copy-paste commands for Google Cloud.
GSP315 — Set Up an App Dev Environment on Google Cloud: Challenge Lab
Estimated time: 1 hour 15 minutes
# 🚀 Set Up an App Dev Environment on Google Cloud: Challenge Lab > ⚠️ **Disclaimer:** This guide is provided for educational and learning purposes only. It is intended to help you understand Google Cloud application development environments, API configuration, Cloud Run functions, Pub/Sub topics, IAM permissions, service accounts, and access management while practicing for Google Cloud certifications and hands-on Challenge Labs. Always follow the official Google Cloud and Google Cloud Skills B
#!/bin/bash
clear
# ==============================================================================
# Color Variables & Orbit of Ops Branding
# ==============================================================================
RED='\e[1;31m'
GREEN='\e[1;32m'
YELLOW='\e[1;33m'
BLUE='\e[1;34m'
MAGENTA='\e[1;35m'
CYAN='\e[1;36m'
WHITE='\e[1;37m'
BOLD='\e[1m'
RESET='\e[0m'
echo -e "${CYAN}${BOLD}"
cat << "EOF"
____ _ _ _ __ ___
/ __ \ | | (_) | / _| / _ \
| | | |_ __| |__ _| |_ ___ | |_ | | | |_ __ ___
| | | | '__| '_ \| | __| / _ \ | _| | | | | '_ \/ __|
| |__| | | | |_) | | |_ | (_) || | | |_| | |_) \__ \
\____/|_| |_.__/|_|\__| \___/ |_| \___/| .__/|___/
| |
|_|
EOF
echo -e "${RESET}"
echo -e "${MAGENTA}${BOLD} 🚀 Starting Orbit of Ops Master Execution (GSP315)... ${RESET}"
echo -e "${BLUE}--------------------------------------------------------------------------------${RESET}\n"
# ==============================================================================
# PRE-FLIGHT CHECKS & VARIABLES (DYNAMIC AUTO-FETCH)
# ==============================================================================
echo -e "${BOLD}${YELLOW}[Orbit of Ops] Auto-fetching Project Configuration...${RESET}"
export PROJECT_ID=$(gcloud config get-value project 2>/dev/null)
if [[ -z "$PROJECT_ID" ]]; then
export PROJECT_ID=$DEVSHELL_PROJECT_ID
fi
export ZONE=$(gcloud compute project-info describe \
--format="value(commonInstanceMetadata.items[google-compute-default-zone])" 2>/dev/null | tail -n 1)
if [[ -z "$ZONE" ]]; then
echo -e "${BOLD}${RED}⚠️ Could not auto-detect the default zone via gcloud metadata.${RESET}"
echo -ne "${BOLD}${CYAN}Please enter the lab Zone (e.g., us-east1-c): ${RESET}"
read ZONE
export ZONE
fi
export REGION=${ZONE%-*}
gcloud config set compute/zone $ZONE 2>/dev/null
gcloud config set compute/region $REGION 2>/dev/null
echo -e "✅ Project ID: ${GREEN}$PROJECT_ID${RESET}"
echo -e "✅ Zone: ${GREEN}$ZONE${RESET}"
echo -e "✅ Region: ${GREEN}$REGION${RESET}\n"
echo -e "${BLUE}--------------------------------------------------------------------------------${RESET}\n"
# ==============================================================================
# USER INPUT
# ==============================================================================
echo -e "${BOLD}${YELLOW}⚠️ ATTENTION: Check your lab instructions for the following values: ${RESET}"
echo -ne "${BOLD}${CYAN}Enter the Bucket Name (from Task 1): ${RESET}"
read BUCKET_NAME
echo -ne "${BOLD}${CYAN}Enter the Pub/Sub Topic Name (from Task 2): ${RESET}"
read TOPIC_NAME
echo -ne "${BOLD}${CYAN}Enter the Cloud Run Function Name (from Task 3): ${RESET}"
read FUNCTION_NAME
echo -ne "${BOLD}${CYAN}Enter Username 2 (The user to be removed from Task 4): ${RESET}"
read USERNAME_2
echo -e "\n${BLUE}--------------------------------------------------------------------------------${RESET}\n"
# ==============================================================================
# MAIN SCRIPT EXECUTION
# ==============================================================================
echo -e "${BOLD}${CYAN}[Orbit of Ops] Enabling required APIs...${RESET}"
gcloud services enable \
artifactregistry.googleapis.com \
cloudfunctions.googleapis.com \
cloudbuild.googleapis.com \
eventarc.googleapis.com \
run.googleapis.com \
logging.googleapis.com \
pubsub.googleapis.com --quiet
echo -e "\n${BOLD}${CYAN}[Orbit of Ops] Configuring necessary IAM permissions...${RESET}"
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:[email protected] \
--role=roles/eventarc.eventReceiver --quiet
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:[email protected] \
--role=roles/pubsub.publisher --quiet
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:[email protected] \
--role=roles/eventarc.serviceAgent --quiet
SERVICE_ACCOUNT="$(gsutil kms serviceaccount -p $PROJECT_ID)"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SERVICE_ACCOUNT}" \
--role='roles/pubsub.publisher' --quiet
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:[email protected] \
--role=roles/iam.serviceAccountTokenCreator --quiet
echo -e "\n${BOLD}${CYAN}[Orbit of Ops] Task 1: Creating bucket '$BUCKET_NAME'...${RESET}"
# Ignore error if bucket already exists from previous run
gsutil mb -l $REGION gs://$BUCKET_NAME 2>/dev/null || true
echo -e "\n${BOLD}${CYAN}[Orbit of Ops] Task 2: Creating Pub/Sub topic '$TOPIC_NAME'...${RESET}"
# Ignore error if topic already exists
gcloud pubsub topics create $TOPIC_NAME 2>/dev/null || true
echo -e "\n${BOLD}${CYAN}[Orbit of Ops] Task 3: Writing Node.js payload...${RESET}"
mkdir -p ~/cloud-function-code
cd ~/cloud-function-code
cat > index.js <<EOF
const functions = require('@google-cloud/functions-framework');const { Storage } = require('@google-cloud/storage');const { PubSub } = require('@google-cloud/pubsub');const sharp = require('sharp');
functions.cloudEvent('$FUNCTION_NAME', async cloudEvent => {
const event = cloudEvent.data;
console.log(\`Event: \${JSON.stringify(event)}\`);
console.log(\`Hello \${event.bucket}\`);
const fileName = event.name;
const bucketName = event.bucket;
const size = "64x64";
const bucket = new Storage().bucket(bucketName);
const topicName = "$TOPIC_NAME";
const pubsub = new PubSub();
if (fileName.search("64x64_thumbnail") === -1) {
const filename_split = fileName.split('.');
const filename_ext = filename_split[filename_split.length - 1].toLowerCase();
const filename_without_ext = fileName.substring(0, fileName.length - filename_ext.length - 1);
if (filename_ext === 'png' || filename_ext === 'jpg' || filename_ext === 'jpeg') {
console.log(\`Processing Original: gs://\${bucketName}/\${fileName}\`);
const gcsObject = bucket.file(fileName);
const newFilename = \`\${filename_without_ext}_64x64_thumbnail.\${filename_ext}\`;
const gcsNewObject = bucket.file(newFilename);
try {
const [buffer] = await gcsObject.download();
const resizedBuffer = await sharp(buffer)
.resize(64, 64, {
fit: 'inside',
withoutEnlargement: true,
})
.toFormat(filename_ext)
.toBuffer();
await gcsNewObject.save(resizedBuffer, {
metadata: {
contentType: \`image/\${filename_ext}\`,
},
});
console.log(\`Success: \${fileName} → \${newFilename}\`);
await pubsub
.topic(topicName)
.publishMessage({ data: Buffer.from(newFilename) });
console.log(\`Message published to \${topicName}\`);
} catch (err) {
console.error(\`Error: \${err}\`);
}
} else {
console.log(\`gs://\${bucketName}/\${fileName} is not an image I can handle\`);
}
} else {
console.log(\`gs://\${bucketName}/\${fileName} already has a thumbnail\`);
}
});
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/functions-framework": "^3.0.0",
"@google-cloud/pubsub": "^2.0.0",
"@google-cloud/storage": "^6.11.0",
"sharp": "^0.32.1"
},
"devDependencies": {},
"engines": {
"node": ">=4.3.2"
}
}
EOF
echo -e "\n${BOLD}${CYAN}[Orbit of Ops] Task 3: Deploying Gen 2 Cloud Function...${RESET}"
echo -e "${YELLOW}Note: If Eventarc permissions are still propagating, this step will auto-retry until successful.${RESET}"
MAX_RETRIES=4
RETRY_COUNT=0
DEPLOY_SUCCESS=false
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if gcloud functions deploy $FUNCTION_NAME \
--gen2 \
--runtime nodejs22 \
--trigger-resource $BUCKET_NAME \
--trigger-event google.storage.object.finalize \
--entry-point $FUNCTION_NAME \
--region=$REGION \
--source . \
--quiet; then
DEPLOY_SUCCESS=true
break
else
echo -e "${RED}⚠️ Eventarc permissions propagating. Retrying in 30 seconds... ($((RETRY_COUNT+1))/$MAX_RETRIES)${RESET}"
sleep 30
RETRY_COUNT=$((RETRY_COUNT+1))
fi
done
if [ "$DEPLOY_SUCCESS" = false ]; then
echo -e "${RED}❌ Deployment failed after $MAX_RETRIES attempts. Please check Cloud Shell for details.${RESET}"
exit 1
fi
echo -e "\n${BOLD}${CYAN}[Orbit of Ops] Task 3: Uploading test image to trigger function...${RESET}"
curl -s -o map.jpg https://storage.googleapis.com/cloud-training/gsp315/map.jpg
gsutil cp map.jpg gs://$BUCKET_NAME/map.jpg
echo -e "\n${BOLD}${CYAN}[Orbit of Ops] Task 4: Removing Username 2 ($USERNAME_2) from project...${RESET}"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member=user:$USERNAME_2 \
--role=roles/viewer --quiet
# ==============================================================================
# COMPLETION
# ==============================================================================
echo -e "\n${MAGENTA}${BOLD}╔════════════════════════════════════════════════════════════╗${RESET}"
echo -e "${MAGENTA}${BOLD}║ 🎉 AUTOMATION COMPLETED SUCCESSFULLY 🎉 ║${RESET}"
echo -e "${MAGENTA}${BOLD}╚════════════════════════════════════════════════════════════╝${RESET}"
echo -e "${GREEN}${BOLD}You can now safely click ALL 'Check my progress' buttons in your lab manual.${RESET}"
echo -e "${CYAN}${BOLD}Subscribe to Orbit of Ops: https://www.youtube.com/@orbitofops/videos${RESET}\n"