Build Serverless Applications with Cloud Run Functions
Solution for Build Serverless Applications with Cloud Run Functions. 1 lab: ARC104. Fast copy-paste commands for Google Cloud.
ARC104 — Build Serverless Applications with Cloud Run Functions: Challenge Lab
Estimated time: 30 minutes
# 🚀 Cloud Run Functions: Challenge Lab > ⚠️ **Disclaimer:** This guide is provided for educational and learning purposes only. It is intended to help you understand Google Cloud Run functions, API enablement, IAM permissions, Cloud Storage, Eventarc event-driven architectures, function deployment, and serverless application workflows while preparing for Google Cloud certifications and hands-on Challenge Labs. Always follow the official Google Cloud and Google Cloud Skills Boost/Qwiklabs instru
#!/bin/bash
set +H
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 (ARC104)... ${RESET}"
echo -e "${BLUE}--------------------------------------------------------------------------------${RESET}\n"
# ==============================================================================
# USER INPUT (RESTORED DYNAMIC VARIABLES)
# ==============================================================================
echo -e "${BOLD}${YELLOW}⚠️ ATTENTION: Check your lab instructions for the following values: ${RESET}"
echo -ne "${BOLD}${CYAN}Enter the Cloud Storage Function Name (Task 2, e.g. cs-logger): ${RESET}"
read STORAGE_FUNCTION
echo -ne "${BOLD}${CYAN}Enter the HTTP Function Name (Task 3, e.g. http-messenger): ${RESET}"
read HTTP_FUNCTION
echo -e "\n${BLUE}--------------------------------------------------------------------------------${RESET}\n"
# ==============================================================================
# PRE-FLIGHT CHECKS
# ==============================================================================
echo -e "${BOLD}${YELLOW}[Orbit of Ops] Auto-fetching Project and Region...${RESET}"
export PROJECT_ID=$(gcloud config get-value project 2>/dev/null)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
export BUCKET_NAME="$PROJECT_ID"
export BUCKET_URI="gs://$PROJECT_ID"
export STORAGE_SA="serviceAccount:service-$PROJECT_NUMBER@gs-project-accounts.iam.gserviceaccount.com"
export EVENTARC_SA="serviceAccount:[email protected]"
export REGION=$(gcloud compute project-info describe --format="value(commonInstanceMetadata.items[google-compute-default-region])" 2>/dev/null)
if [[ -z "$REGION" ]]; then
echo -e "${BOLD}${RED}⚠️ Could not auto-detect the default region.${RESET}"
echo -ne "${BOLD}${CYAN}Please enter the lab Region (e.g., europe-west4): ${RESET}"
read REGION
fi
gcloud config set compute/region $REGION 2>/dev/null
echo -e "✅ Project ID: ${GREEN}$PROJECT_ID${RESET}"
echo -e "✅ Dynamic Region: ${GREEN}$REGION${RESET}"
echo -e "✅ Storage Function: ${GREEN}$STORAGE_FUNCTION${RESET}"
echo -e "✅ HTTP Function: ${GREEN}$HTTP_FUNCTION${RESET}\n"
echo -e "${BLUE}--------------------------------------------------------------------------------${RESET}\n"
# ==============================================================================
# MAIN SCRIPT EXECUTION
# ==============================================================================
echo -e "${BOLD}${CYAN}[Orbit of Ops] Enabling required APIs (This takes ~1 minute)...${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] Task 1: Creating Cloud Storage Bucket in $REGION...${RESET}"
# Creating the bucket forces Google to generate the Cloud Storage Service Account
gsutil mb -l $REGION $BUCKET_URI 2>/dev/null || true
echo -e "\n${BOLD}${CYAN}[Orbit of Ops] Provisioning Eventarc & Pub/Sub Service Role Bindings...${RESET}"
# Robust loop to ensure the service account propagates before binding
while ! gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="$STORAGE_SA" \
--role="roles/pubsub.publisher" --quiet >/dev/null 2>&1; do
echo -e "${YELLOW}Waiting for Storage Service Account to initialize in backend...${RESET}"
sleep 5
done
echo -e "${GREEN}✅ Pub/Sub Publisher role granted to Storage Service Account.${RESET}"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:[email protected]" \
--role="roles/eventarc.eventReceiver" --quiet >/dev/null 2>&1
echo -e "${GREEN}✅ Eventarc Receiver role granted to Compute Service Account.${RESET}"
# FIX: Explicitly grant the Eventarc Service Agent its own role to bypass the permission denied error
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="$EVENTARC_SA" \
--role="roles/eventarc.serviceAgent" --quiet >/dev/null 2>&1
echo -e "${GREEN}✅ Eventarc Service Agent role granted to Eventarc SA.${RESET}"
echo -e "\n${BOLD}${CYAN}[Orbit of Ops] Task 2: Writing Source Code for Cloud Storage Function...${RESET}"
mkdir -p ~/storage_function_$STORAGE_FUNCTION && cd ~/storage_function_$STORAGE_FUNCTION
cat << EOF > index.js
const functions = require('@google-cloud/functions-framework');
functions.cloudEvent('$STORAGE_FUNCTION', (cloudevent) => {
console.log('A new event in your Cloud Storage bucket has been logged!');
console.log(cloudevent);
});
EOF
cat << 'EOF' > package.json
{
"name": "nodejs-functions-gen2-codelab",
"version": "0.0.1",
"main": "index.js",
"dependencies": {
"@google-cloud/functions-framework": "^2.0.0"
}
}
EOF
echo -e "\n${BOLD}${CYAN}[Orbit of Ops] Task 2: Deploying Cloud Storage Function ($STORAGE_FUNCTION)...${RESET}"
echo -e "${YELLOW}Note: If Eventarc IAM permissions are still propagating, this step will auto-retry until successful.${RESET}"
MAX_RETRIES=6
RETRY_COUNT=0
DEPLOY_SUCCESS=false
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if gcloud functions deploy $STORAGE_FUNCTION \
--gen2 \
--runtime=nodejs24 \
--entry-point=$STORAGE_FUNCTION \
--source=. \
--region=$REGION \
--trigger-bucket=$BUCKET_NAME \
--trigger-location=$REGION \
--max-instances=2 \
--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 2: Triggering the function to register activity...${RESET}"
echo "Triggering the function..." > test-event.txt
gsutil cp test-event.txt $BUCKET_URI/test-event.txt 2>/dev/null
echo -e "\n${BOLD}${CYAN}[Orbit of Ops] Task 3: Writing Source Code for HTTP Function...${RESET}"
mkdir -p ~/http_function_$HTTP_FUNCTION && cd ~/http_function_$HTTP_FUNCTION
cat << EOF > index.js
const functions = require('@google-cloud/functions-framework');
functions.http('$HTTP_FUNCTION', (req, res) => {
res.status(200).send('HTTP function (2nd gen) has been called!');
});
EOF
cat << 'EOF' > package.json
{
"name": "nodejs-functions-gen2-codelab",
"version": "0.0.1",
"main": "index.js",
"dependencies": {
"@google-cloud/functions-framework": "^2.0.0"
}
}
EOF
echo -e "\n${BOLD}${CYAN}[Orbit of Ops] Task 3: Deploying HTTP Function ($HTTP_FUNCTION) with Scale Settings...${RESET}"
RETRY_COUNT=0
HTTP_DEPLOY_SUCCESS=false
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if gcloud functions deploy $HTTP_FUNCTION \
--gen2 \
--runtime=nodejs24 \
--entry-point=$HTTP_FUNCTION \
--source=. \
--region=$REGION \
--trigger-http \
--allow-unauthenticated \
--min-instances=1 \
--max-instances=2 \
--quiet; then
HTTP_DEPLOY_SUCCESS=true
break
else
echo -e "${RED}⚠️ API glitch detected. Retrying deployment in 30 seconds... ($((RETRY_COUNT+1))/$MAX_RETRIES)${RESET}"
sleep 30
RETRY_COUNT=$((RETRY_COUNT+1))
fi
done
if [ "$HTTP_DEPLOY_SUCCESS" = false ]; then
echo -e "${RED}❌ HTTP Deployment failed after $MAX_RETRIES attempts. Please check Cloud Shell for details.${RESET}"
exit 1
fi
# ==============================================================================
# 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"