A complete guide for deploying internal-only Cloud Run functions on Google Cloud Platform with automated scheduling and optional Apps Script integration.
This repository demonstrates how to:
- Deploy a secure, internal-only Cloud Run function (Gen 2)
- Automate execution with Cloud Scheduler
- Grant Google Drive/Workspace API access
- Trigger functions on-demand from Google Apps Script
- Google Cloud Project with billing enabled
gcloudCLI installed and configured- Basic familiarity with Python and Google Cloud Platform
.
├── main.py # Your Python function code
├── requirements.txt # Python dependencies
└── README.md # This file
Define your Python dependencies:
functions-framework==3.*
google-api-python-client==2.*
google-auth==2.*
Your function must include the @functions_framework.http decorator:
import os
import functions_framework
import google.auth
from googleapiclient.discovery import build
@functions_framework.http
def main(request):
"""
HTTP Cloud Function entry point.
Args:
request (flask.Request): The request object.
Returns:
The response text, or any set of values that can be turned into a
Response object using `make_response`.
"""
try:
# Get default credentials
creds, project_id = google.auth.default()
# Your business logic here
# Example: Interact with Google Drive API
# service = build('drive', 'v3', credentials=creds)
return {"status": "success", "message": "Function executed successfully"}, 200
except Exception as e:
return {"status": "error", "message": str(e)}, 500Deploy as a Gen 2 Cloud Function with internal-only access:
gcloud functions deploy your-function-name \
--gen2 \
--runtime=python310 \
--region=europe-west1 \
--source=. \
--entry-point=main \
--trigger-http \
--ingress-settings=internal-onlyKey Parameters:
--gen2: Uses Cloud Run (Gen 2) infrastructure--ingress-settings=internal-only: Blocks all public internet traffic--entry-point=main: Points to the function name in your code
Since the function is internal-only, you need a service account to invoke it.
gcloud iam service-accounts create function-scheduler-sa \
--display-name="Function Scheduler Service Account"gcloud run services add-iam-policy-binding your-function-name \
--region=europe-west1 \
--member="serviceAccount:function-scheduler-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/run.invoker"If your function needs to access Google Drive or other Workspace APIs:
gcloud run services describe your-function-name \
--region=europe-west1 \
--format="value(template.serviceAccount)"Copy the service account email and share your Drive folder/Shared Drive with it, granting appropriate permissions (e.g., Content Manager, Editor).
Set up automatic execution using Cloud Scheduler:
gcloud scheduler jobs create http scheduled-function-job \
--location=europe-west1 \
--schedule="0 9 * * 1-5" \
--time-zone="Europe/Berlin" \
--uri="https://YOUR-CLOUD-RUN-URL.run.app/" \
--http-method=GET \
--oidc-service-account-email="function-scheduler-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com"Common Cron Schedules:
0 9 * * 1-5: Every weekday at 9 AM0 */6 * * *: Every 6 hours*/30 * * * *: Every 30 minutes0 0 * * 0: Every Sunday at midnight
gcloud scheduler jobs run scheduled-function-job --location=europe-west1To invoke your internal function from a Google Sheet or Apps Script:
Enable "Show manifest file" in Apps Script settings, then edit appsscript.json:
{
"oauthScopes": [
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/script.external_request"
]
}function triggerCloudFunction() {
const projectId = 'YOUR_PROJECT_ID';
const location = 'europe-west1';
const jobId = 'scheduled-function-job';
const url = `https://cloudscheduler.googleapis.com/v1/projects/${projectId}/locations/${location}/jobs/${jobId}:run`;
const options = {
method: 'post',
headers: {
Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
},
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(url, options);
Logger.log(response.getContentText());
return JSON.parse(response.getContentText());
}To update an existing deployment:
# Create project directory
mkdir my-cloud-function
# Move files
mv main.py requirements.txt my-cloud-function/
# Navigate to directory
cd my-cloud-function
# Redeploy (uses same function name)
gcloud functions deploy your-function-name \
--gen2 \
--runtime=python310 \
--region=europe-west1 \
--source=. \
--entry-point=main \
--trigger-http \
--ingress-settings=internal-only# Update code in place and redeploy
gcloud functions deploy your-function-name \
--gen2 \
--runtime=python310 \
--region=europe-west1 \
--source=. \
--entry-point=main \
--trigger-http \
--ingress-settings=internal-onlyNote: Updates deploy with zero downtime. The old version continues serving requests while the new version builds.
gcloud functions logs read your-function-name \
--region=europe-west1 \
--limit=50gcloud scheduler jobs describe scheduled-function-job \
--location=europe-west1Visit the Cloud Functions Console for:
- Execution metrics and graphs
- Real-time logs
- Error tracking
- Invocation history
- Never expose internal functions publicly - Always use
--ingress-settings=internal-only - Use service accounts - Create dedicated service accounts for each function/purpose
- Principle of least privilege - Grant only necessary IAM roles
- Rotate credentials - Regularly review and update service account permissions
- Enable Cloud Audit Logs - Track who accesses your functions
- Verify service account has
roles/run.invokerpermission - Check Cloud Scheduler job is enabled:
gcloud scheduler jobs describe JOB_NAME --location=LOCATION - Review Cloud Scheduler logs for authentication errors
- Confirm the runtime service account has access to required resources (Drive, etc.)
- Verify API services are enabled:
gcloud services list --enabled
- Increase timeout: Add
--timeout=540sto deployment command (max 540s for Gen 2) - Optimize your code for faster execution
- Consider using Cloud Tasks for longer-running jobs
- Cloud Functions: Free tier includes 2M invocations/month
- Cloud Scheduler: First 3 jobs per month are free
- Cloud Run (Gen 2 backend): Pay only for execution time
- See Google Cloud Pricing Calculator for estimates
- Cloud Functions Documentation
- Cloud Scheduler Documentation
- Service Account Best Practices
- Cron Schedule Format
MIT License - See LICENSE file for details
Contributions are welcome! Please open an issue or submit a pull request.