Cloud Scheduler can trigger any HTTP endpoint on a cron schedule. When the target endpoint requires authentication — which it should, for anything connected to production data infrastructure — Scheduler needs a way to prove its identity. The standard mechanism on GCP is OIDC tokens.
This comes up most often when scheduling Cloud Functions or Cloud Run Jobs. Both services can require that callers present a valid identity token, and Cloud Scheduler can be configured to generate and attach one automatically.
How OIDC Auth Works Here
OIDC (OpenID Connect) tokens are short-lived, signed JWT tokens that assert an identity. When Cloud Scheduler makes an HTTP request with OIDC auth, it:
- Generates a token on behalf of the configured service account
- Signs it with Google’s infrastructure
- Attaches it as an
Authorization: Bearer <token>header - The receiving service validates the token against Google’s public keys
The receiving service (Cloud Functions 2nd gen, Cloud Run, or any endpoint that validates GCP identity tokens) checks that the token is valid and that the identity has the correct IAM role to invoke the endpoint.
For this to work, the service account used by Cloud Scheduler needs two permissions:
roles/iam.serviceAccountTokenCreatoron itself — so it can generate tokens on its own behalfroles/cloudfunctions.invoker(orroles/run.invoker) on the target resource
The Setup Script
This script sets up a Cloud Scheduler job with OIDC authentication pointing at a Cloud Function HTTP trigger:
# VariablesFUNCTION_URL="YOUR_CLOUD_FUNCTION_TRIGGER_URL"SCHEDULER_JOB_NAME="daily-dbt-run-job"PROJECT_ID="YOUR_PROJECT_ID"LOCATION="YOUR_LOCATION_ID"SERVICE_ACCOUNT_EMAIL="YOUR_SERVICE_ACCOUNT_EMAIL"TIME_ZONE="YOUR_TIME_ZONE"CRON_SCHEDULE="0 7 * * *" # daily at 7 AM by default
# Step 1: Ensure the service account has the "Service Account Token Creator" rolegcloud iam service-accounts add-iam-policy-binding $SERVICE_ACCOUNT_EMAIL \ --member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \ --role="roles/iam.serviceAccountTokenCreator" \ --project=$PROJECT_IDecho "Added Service Account Token Creator role to $SERVICE_ACCOUNT_EMAIL."
# Step 2: Create the Cloud Scheduler jobgcloud scheduler jobs create http $SCHEDULER_JOB_NAME \ --location=$LOCATION \ --schedule="$CRON_SCHEDULE" \ --http-method=GET \ --uri=$FUNCTION_URL \ --oidc-service-account-email=$SERVICE_ACCOUNT_EMAIL \ --oidc-token-audience=$FUNCTION_URL \ --time-zone="$TIME_ZONE" \ --project=$PROJECT_IDecho "Cloud Scheduler job created: $SCHEDULER_JOB_NAME"The --oidc-token-audience parameter should match the URL of the Cloud Function exactly. The OIDC token is scoped to this audience — a token generated for one URL won’t be accepted by a different URL.
The LOCATION vs REGION Gotcha
Cloud Scheduler jobs are regional, and the LOCATION parameter needs to match the region where the Scheduler service is available. This is a superset of Cloud Function regions — most regions that support Cloud Functions also support Cloud Scheduler, but check the supported regions if you’re using a less common one.
For Cloud Functions, the FUNCTION_URL comes from the deployment output. After gcloud functions deploy, the URL is printed as httpsTriggerUrl. You can also retrieve it with:
gcloud functions describe dbt_run --region=europe-west1 --format="value(serviceConfig.uri)"CRON Format and Time Zones
Cloud Scheduler uses standard Unix cron format:
minute hour day-of-month month day-of-week0 7 * * * = daily at 7 AM0 */4 * * * = every 4 hours30 6 * * 1-5 = weekdays at 6:30 AMThe --time-zone parameter accepts tz database format (e.g., Europe/Paris, America/New_York, UTC). This matters if your data pipeline has time-of-day dependencies — a “7 AM run” means different things in different time zones, and the default is UTC if you don’t specify.
You can find valid time zone identifiers at Wikipedia’s tz database list or by running timedatectl list-timezones on any Linux machine.
Cloud Run Jobs vs. Cloud Functions
The OIDC pattern applies to both, but the Scheduler job configuration differs slightly:
For Cloud Functions (HTTP trigger):
gcloud scheduler jobs create http my-scheduler-job \ --uri=$FUNCTION_URL \ --http-method=GET \ --oidc-service-account-email=$SA_EMAIL \ --oidc-token-audience=$FUNCTION_URL \ --schedule="0 7 * * *" \ --location=$REGIONFor Cloud Run Jobs:
gcloud scheduler jobs create http my-scheduler-job \ --uri="https://$REGION-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/$PROJECT_ID/jobs/$JOB_NAME:run" \ --http-method=POST \ --oauth-service-account-email=$SA_EMAIL \ --schedule="0 7 * * *" \ --location=$REGIONNotice the difference: Cloud Run Jobs uses --oauth-service-account-email (OAuth 2.0 token), while Cloud Functions uses --oidc-service-account-email (OIDC token). They’re similar mechanisms but the flags are different and they’re not interchangeable. The Cloud Run Jobs Deployment Script Pattern has the full Cloud Run version.
Also note that for Cloud Run, the URI is the Cloud Run Admin API endpoint with :run appended to the job name, not the job’s own URL. This is a common point of confusion.
Testing the Scheduler
After setup, trigger the scheduler manually to verify the full chain without waiting for the scheduled time:
gcloud scheduler jobs run daily-dbt-run-job --location=$LOCATIONThen check the Cloud Function execution logs to confirm it received the request and ran successfully:
gcloud functions logs read dbt_run --region=europe-west1 --limit=50If the scheduler fires but the function returns a 403, the service account is missing roles/cloudfunctions.invoker. If the function returns a 401, the OIDC token audience doesn’t match the function URL. Both are common first-deployment issues and both are easy to fix once you know what to look for.