Create GCP Cloud Scheduler Jobs

The gcp:cloudscheduler/job:Job resource, part of the Pulumi GCP provider, defines a Cloud Scheduler job that executes on a cron schedule by publishing to Pub/Sub, calling HTTP endpoints, or invoking App Engine services. This guide focuses on four capabilities: Pub/Sub message publishing, HTTP endpoint invocation, App Engine service routing, and OAuth/OIDC authentication.

Cloud Scheduler jobs reference Pub/Sub topics, service accounts, or App Engine services that must exist separately. The examples are intentionally small. Combine them with your own topics, authentication, and target infrastructure.

Publish messages to Pub/Sub on a schedule

Event-driven architectures often trigger workflows at regular intervals by publishing messages to Pub/Sub topics, which then fan out to multiple subscribers.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as std from "@pulumi/std";

const topic = new gcp.pubsub.Topic("topic", {name: "job-topic"});
const job = new gcp.cloudscheduler.Job("job", {
    name: "test-job",
    description: "test job",
    schedule: "*/2 * * * *",
    pubsubTarget: {
        topicName: topic.id,
        data: std.base64encode({
            input: "test",
        }).then(invoke => invoke.result),
    },
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

topic = gcp.pubsub.Topic("topic", name="job-topic")
job = gcp.cloudscheduler.Job("job",
    name="test-job",
    description="test job",
    schedule="*/2 * * * *",
    pubsub_target={
        "topic_name": topic.id,
        "data": std.base64encode(input="test").result,
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudscheduler"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/pubsub"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		topic, err := pubsub.NewTopic(ctx, "topic", &pubsub.TopicArgs{
			Name: pulumi.String("job-topic"),
		})
		if err != nil {
			return err
		}
		invokeBase64encode, err := std.Base64encode(ctx, &std.Base64encodeArgs{
			Input: "test",
		}, nil)
		if err != nil {
			return err
		}
		_, err = cloudscheduler.NewJob(ctx, "job", &cloudscheduler.JobArgs{
			Name:        pulumi.String("test-job"),
			Description: pulumi.String("test job"),
			Schedule:    pulumi.String("*/2 * * * *"),
			PubsubTarget: &cloudscheduler.JobPubsubTargetArgs{
				TopicName: topic.ID(),
				Data:      pulumi.String(invokeBase64encode.Result),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var topic = new Gcp.PubSub.Topic("topic", new()
    {
        Name = "job-topic",
    });

    var job = new Gcp.CloudScheduler.Job("job", new()
    {
        Name = "test-job",
        Description = "test job",
        Schedule = "*/2 * * * *",
        PubsubTarget = new Gcp.CloudScheduler.Inputs.JobPubsubTargetArgs
        {
            TopicName = topic.Id,
            Data = Std.Base64encode.Invoke(new()
            {
                Input = "test",
            }).Apply(invoke => invoke.Result),
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.pubsub.Topic;
import com.pulumi.gcp.pubsub.TopicArgs;
import com.pulumi.gcp.cloudscheduler.Job;
import com.pulumi.gcp.cloudscheduler.JobArgs;
import com.pulumi.gcp.cloudscheduler.inputs.JobPubsubTargetArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.Base64encodeArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var topic = new Topic("topic", TopicArgs.builder()
            .name("job-topic")
            .build());

        var job = new Job("job", JobArgs.builder()
            .name("test-job")
            .description("test job")
            .schedule("*/2 * * * *")
            .pubsubTarget(JobPubsubTargetArgs.builder()
                .topicName(topic.id())
                .data(StdFunctions.base64encode(Base64encodeArgs.builder()
                    .input("test")
                    .build()).result())
                .build())
            .build());

    }
}
resources:
  topic:
    type: gcp:pubsub:Topic
    properties:
      name: job-topic
  job:
    type: gcp:cloudscheduler:Job
    properties:
      name: test-job
      description: test job
      schedule: '*/2 * * * *'
      pubsubTarget:
        topicName: ${topic.id}
        data:
          fn::invoke:
            function: std:base64encode
            arguments:
              input: test
            return: result

The schedule property uses cron syntax to define execution frequency. The pubsubTarget specifies the topic to publish to, and data contains the base64-encoded message payload. Each execution publishes one message to the topic.

Send HTTP requests to external endpoints

Many scheduled tasks call HTTP APIs directly to trigger webhooks, refresh caches, or invoke serverless functions.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as std from "@pulumi/std";

const job = new gcp.cloudscheduler.Job("job", {
    name: "test-job",
    description: "test http job",
    schedule: "*/8 * * * *",
    timeZone: "America/New_York",
    attemptDeadline: "320s",
    retryConfig: {
        retryCount: 1,
    },
    httpTarget: {
        httpMethod: "POST",
        uri: "https://example.com/",
        body: std.base64encode({
            input: "{\"foo\":\"bar\"}",
        }).then(invoke => invoke.result),
        headers: {
            "Content-Type": "application/json",
        },
    },
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

job = gcp.cloudscheduler.Job("job",
    name="test-job",
    description="test http job",
    schedule="*/8 * * * *",
    time_zone="America/New_York",
    attempt_deadline="320s",
    retry_config={
        "retry_count": 1,
    },
    http_target={
        "http_method": "POST",
        "uri": "https://example.com/",
        "body": std.base64encode(input="{\"foo\":\"bar\"}").result,
        "headers": {
            "Content-Type": "application/json",
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudscheduler"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		invokeBase64encode, err := std.Base64encode(ctx, &std.Base64encodeArgs{
			Input: "{\"foo\":\"bar\"}",
		}, nil)
		if err != nil {
			return err
		}
		_, err = cloudscheduler.NewJob(ctx, "job", &cloudscheduler.JobArgs{
			Name:            pulumi.String("test-job"),
			Description:     pulumi.String("test http job"),
			Schedule:        pulumi.String("*/8 * * * *"),
			TimeZone:        pulumi.String("America/New_York"),
			AttemptDeadline: pulumi.String("320s"),
			RetryConfig: &cloudscheduler.JobRetryConfigArgs{
				RetryCount: pulumi.Int(1),
			},
			HttpTarget: &cloudscheduler.JobHttpTargetArgs{
				HttpMethod: pulumi.String("POST"),
				Uri:        pulumi.String("https://example.com/"),
				Body:       pulumi.String(invokeBase64encode.Result),
				Headers: pulumi.StringMap{
					"Content-Type": pulumi.String("application/json"),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var job = new Gcp.CloudScheduler.Job("job", new()
    {
        Name = "test-job",
        Description = "test http job",
        Schedule = "*/8 * * * *",
        TimeZone = "America/New_York",
        AttemptDeadline = "320s",
        RetryConfig = new Gcp.CloudScheduler.Inputs.JobRetryConfigArgs
        {
            RetryCount = 1,
        },
        HttpTarget = new Gcp.CloudScheduler.Inputs.JobHttpTargetArgs
        {
            HttpMethod = "POST",
            Uri = "https://example.com/",
            Body = Std.Base64encode.Invoke(new()
            {
                Input = "{\"foo\":\"bar\"}",
            }).Apply(invoke => invoke.Result),
            Headers = 
            {
                { "Content-Type", "application/json" },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.cloudscheduler.Job;
import com.pulumi.gcp.cloudscheduler.JobArgs;
import com.pulumi.gcp.cloudscheduler.inputs.JobRetryConfigArgs;
import com.pulumi.gcp.cloudscheduler.inputs.JobHttpTargetArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.Base64encodeArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var job = new Job("job", JobArgs.builder()
            .name("test-job")
            .description("test http job")
            .schedule("*/8 * * * *")
            .timeZone("America/New_York")
            .attemptDeadline("320s")
            .retryConfig(JobRetryConfigArgs.builder()
                .retryCount(1)
                .build())
            .httpTarget(JobHttpTargetArgs.builder()
                .httpMethod("POST")
                .uri("https://example.com/")
                .body(StdFunctions.base64encode(Base64encodeArgs.builder()
                    .input("{\"foo\":\"bar\"}")
                    .build()).result())
                .headers(Map.of("Content-Type", "application/json"))
                .build())
            .build());

    }
}
resources:
  job:
    type: gcp:cloudscheduler:Job
    properties:
      name: test-job
      description: test http job
      schedule: '*/8 * * * *'
      timeZone: America/New_York
      attemptDeadline: 320s
      retryConfig:
        retryCount: 1
      httpTarget:
        httpMethod: POST
        uri: https://example.com/
        body:
          fn::invoke:
            function: std:base64encode
            arguments:
              input: '{"foo":"bar"}'
            return: result
        headers:
          Content-Type: application/json

The httpTarget property defines the endpoint, method, and request body. The retryConfig controls how Cloud Scheduler handles failures, and attemptDeadline sets the maximum time to wait for a response. The timeZone property interprets the cron schedule in the specified time zone.

Create jobs in a paused state

During deployment or maintenance, you may need to define jobs without immediately activating them.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as std from "@pulumi/std";

const job = new gcp.cloudscheduler.Job("job", {
    paused: true,
    name: "test-job",
    description: "test http job with updated fields",
    schedule: "*/8 * * * *",
    timeZone: "America/New_York",
    attemptDeadline: "320s",
    retryConfig: {
        retryCount: 1,
    },
    httpTarget: {
        httpMethod: "POST",
        uri: "https://example.com/ping",
        body: std.base64encode({
            input: "{\"foo\":\"bar\"}",
        }).then(invoke => invoke.result),
        headers: {
            "Content-Type": "application/json",
        },
    },
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

job = gcp.cloudscheduler.Job("job",
    paused=True,
    name="test-job",
    description="test http job with updated fields",
    schedule="*/8 * * * *",
    time_zone="America/New_York",
    attempt_deadline="320s",
    retry_config={
        "retry_count": 1,
    },
    http_target={
        "http_method": "POST",
        "uri": "https://example.com/ping",
        "body": std.base64encode(input="{\"foo\":\"bar\"}").result,
        "headers": {
            "Content-Type": "application/json",
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudscheduler"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		invokeBase64encode, err := std.Base64encode(ctx, &std.Base64encodeArgs{
			Input: "{\"foo\":\"bar\"}",
		}, nil)
		if err != nil {
			return err
		}
		_, err = cloudscheduler.NewJob(ctx, "job", &cloudscheduler.JobArgs{
			Paused:          pulumi.Bool(true),
			Name:            pulumi.String("test-job"),
			Description:     pulumi.String("test http job with updated fields"),
			Schedule:        pulumi.String("*/8 * * * *"),
			TimeZone:        pulumi.String("America/New_York"),
			AttemptDeadline: pulumi.String("320s"),
			RetryConfig: &cloudscheduler.JobRetryConfigArgs{
				RetryCount: pulumi.Int(1),
			},
			HttpTarget: &cloudscheduler.JobHttpTargetArgs{
				HttpMethod: pulumi.String("POST"),
				Uri:        pulumi.String("https://example.com/ping"),
				Body:       pulumi.String(invokeBase64encode.Result),
				Headers: pulumi.StringMap{
					"Content-Type": pulumi.String("application/json"),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var job = new Gcp.CloudScheduler.Job("job", new()
    {
        Paused = true,
        Name = "test-job",
        Description = "test http job with updated fields",
        Schedule = "*/8 * * * *",
        TimeZone = "America/New_York",
        AttemptDeadline = "320s",
        RetryConfig = new Gcp.CloudScheduler.Inputs.JobRetryConfigArgs
        {
            RetryCount = 1,
        },
        HttpTarget = new Gcp.CloudScheduler.Inputs.JobHttpTargetArgs
        {
            HttpMethod = "POST",
            Uri = "https://example.com/ping",
            Body = Std.Base64encode.Invoke(new()
            {
                Input = "{\"foo\":\"bar\"}",
            }).Apply(invoke => invoke.Result),
            Headers = 
            {
                { "Content-Type", "application/json" },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.cloudscheduler.Job;
import com.pulumi.gcp.cloudscheduler.JobArgs;
import com.pulumi.gcp.cloudscheduler.inputs.JobRetryConfigArgs;
import com.pulumi.gcp.cloudscheduler.inputs.JobHttpTargetArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.Base64encodeArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var job = new Job("job", JobArgs.builder()
            .paused(true)
            .name("test-job")
            .description("test http job with updated fields")
            .schedule("*/8 * * * *")
            .timeZone("America/New_York")
            .attemptDeadline("320s")
            .retryConfig(JobRetryConfigArgs.builder()
                .retryCount(1)
                .build())
            .httpTarget(JobHttpTargetArgs.builder()
                .httpMethod("POST")
                .uri("https://example.com/ping")
                .body(StdFunctions.base64encode(Base64encodeArgs.builder()
                    .input("{\"foo\":\"bar\"}")
                    .build()).result())
                .headers(Map.of("Content-Type", "application/json"))
                .build())
            .build());

    }
}
resources:
  job:
    type: gcp:cloudscheduler:Job
    properties:
      paused: true
      name: test-job
      description: test http job with updated fields
      schedule: '*/8 * * * *'
      timeZone: America/New_York
      attemptDeadline: 320s
      retryConfig:
        retryCount: 1
      httpTarget:
        httpMethod: POST
        uri: https://example.com/ping
        body:
          fn::invoke:
            function: std:base64encode
            arguments:
              input: '{"foo":"bar"}'
            return: result
        headers:
          Content-Type: application/json

Setting paused to true creates the job in an inactive state. The job won’t execute until you set paused to false. This extends the HTTP configuration pattern with pause control.

Route requests to App Engine services

App Engine applications can receive scheduled requests routed to specific services, versions, or instances.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const job = new gcp.cloudscheduler.Job("job", {
    name: "test-job",
    schedule: "*/4 * * * *",
    description: "test app engine job",
    timeZone: "Europe/London",
    attemptDeadline: "320s",
    retryConfig: {
        minBackoffDuration: "1s",
        maxRetryDuration: "10s",
        maxDoublings: 2,
        retryCount: 3,
    },
    appEngineHttpTarget: {
        httpMethod: "POST",
        appEngineRouting: {
            service: "web",
            version: "prod",
            instance: "my-instance-001",
        },
        relativeUri: "/ping",
    },
});
import pulumi
import pulumi_gcp as gcp

job = gcp.cloudscheduler.Job("job",
    name="test-job",
    schedule="*/4 * * * *",
    description="test app engine job",
    time_zone="Europe/London",
    attempt_deadline="320s",
    retry_config={
        "min_backoff_duration": "1s",
        "max_retry_duration": "10s",
        "max_doublings": 2,
        "retry_count": 3,
    },
    app_engine_http_target={
        "http_method": "POST",
        "app_engine_routing": {
            "service": "web",
            "version": "prod",
            "instance": "my-instance-001",
        },
        "relative_uri": "/ping",
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudscheduler"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := cloudscheduler.NewJob(ctx, "job", &cloudscheduler.JobArgs{
			Name:            pulumi.String("test-job"),
			Schedule:        pulumi.String("*/4 * * * *"),
			Description:     pulumi.String("test app engine job"),
			TimeZone:        pulumi.String("Europe/London"),
			AttemptDeadline: pulumi.String("320s"),
			RetryConfig: &cloudscheduler.JobRetryConfigArgs{
				MinBackoffDuration: pulumi.String("1s"),
				MaxRetryDuration:   pulumi.String("10s"),
				MaxDoublings:       pulumi.Int(2),
				RetryCount:         pulumi.Int(3),
			},
			AppEngineHttpTarget: &cloudscheduler.JobAppEngineHttpTargetArgs{
				HttpMethod: pulumi.String("POST"),
				AppEngineRouting: &cloudscheduler.JobAppEngineHttpTargetAppEngineRoutingArgs{
					Service:  pulumi.String("web"),
					Version:  pulumi.String("prod"),
					Instance: pulumi.String("my-instance-001"),
				},
				RelativeUri: pulumi.String("/ping"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var job = new Gcp.CloudScheduler.Job("job", new()
    {
        Name = "test-job",
        Schedule = "*/4 * * * *",
        Description = "test app engine job",
        TimeZone = "Europe/London",
        AttemptDeadline = "320s",
        RetryConfig = new Gcp.CloudScheduler.Inputs.JobRetryConfigArgs
        {
            MinBackoffDuration = "1s",
            MaxRetryDuration = "10s",
            MaxDoublings = 2,
            RetryCount = 3,
        },
        AppEngineHttpTarget = new Gcp.CloudScheduler.Inputs.JobAppEngineHttpTargetArgs
        {
            HttpMethod = "POST",
            AppEngineRouting = new Gcp.CloudScheduler.Inputs.JobAppEngineHttpTargetAppEngineRoutingArgs
            {
                Service = "web",
                Version = "prod",
                Instance = "my-instance-001",
            },
            RelativeUri = "/ping",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.cloudscheduler.Job;
import com.pulumi.gcp.cloudscheduler.JobArgs;
import com.pulumi.gcp.cloudscheduler.inputs.JobRetryConfigArgs;
import com.pulumi.gcp.cloudscheduler.inputs.JobAppEngineHttpTargetArgs;
import com.pulumi.gcp.cloudscheduler.inputs.JobAppEngineHttpTargetAppEngineRoutingArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var job = new Job("job", JobArgs.builder()
            .name("test-job")
            .schedule("*/4 * * * *")
            .description("test app engine job")
            .timeZone("Europe/London")
            .attemptDeadline("320s")
            .retryConfig(JobRetryConfigArgs.builder()
                .minBackoffDuration("1s")
                .maxRetryDuration("10s")
                .maxDoublings(2)
                .retryCount(3)
                .build())
            .appEngineHttpTarget(JobAppEngineHttpTargetArgs.builder()
                .httpMethod("POST")
                .appEngineRouting(JobAppEngineHttpTargetAppEngineRoutingArgs.builder()
                    .service("web")
                    .version("prod")
                    .instance("my-instance-001")
                    .build())
                .relativeUri("/ping")
                .build())
            .build());

    }
}
resources:
  job:
    type: gcp:cloudscheduler:Job
    properties:
      name: test-job
      schedule: '*/4 * * * *'
      description: test app engine job
      timeZone: Europe/London
      attemptDeadline: 320s
      retryConfig:
        minBackoffDuration: 1s
        maxRetryDuration: 10s
        maxDoublings: 2
        retryCount: 3
      appEngineHttpTarget:
        httpMethod: POST
        appEngineRouting:
          service: web
          version: prod
          instance: my-instance-001
        relativeUri: /ping

The appEngineHttpTarget property routes requests to App Engine. The appEngineRouting block specifies the service, version, and instance to target. The relativeUri defines the path within the App Engine service. Cloud Scheduler automatically handles authentication to App Engine.

Authenticate HTTP requests with OAuth tokens

When calling Google Cloud APIs or services requiring OAuth authentication, Cloud Scheduler generates tokens using a service account.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const _default = gcp.compute.getDefaultServiceAccount({});
const job = new gcp.cloudscheduler.Job("job", {
    name: "test-job",
    description: "test http job",
    schedule: "*/8 * * * *",
    timeZone: "America/New_York",
    attemptDeadline: "320s",
    httpTarget: {
        httpMethod: "GET",
        uri: "https://cloudscheduler.googleapis.com/v1/projects/my-project-name/locations/us-west1/jobs",
        oauthToken: {
            serviceAccountEmail: _default.then(_default => _default.email),
        },
    },
});
import pulumi
import pulumi_gcp as gcp

default = gcp.compute.get_default_service_account()
job = gcp.cloudscheduler.Job("job",
    name="test-job",
    description="test http job",
    schedule="*/8 * * * *",
    time_zone="America/New_York",
    attempt_deadline="320s",
    http_target={
        "http_method": "GET",
        "uri": "https://cloudscheduler.googleapis.com/v1/projects/my-project-name/locations/us-west1/jobs",
        "oauth_token": {
            "service_account_email": default.email,
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudscheduler"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_default, err := compute.GetDefaultServiceAccount(ctx, &compute.GetDefaultServiceAccountArgs{}, nil)
		if err != nil {
			return err
		}
		_, err = cloudscheduler.NewJob(ctx, "job", &cloudscheduler.JobArgs{
			Name:            pulumi.String("test-job"),
			Description:     pulumi.String("test http job"),
			Schedule:        pulumi.String("*/8 * * * *"),
			TimeZone:        pulumi.String("America/New_York"),
			AttemptDeadline: pulumi.String("320s"),
			HttpTarget: &cloudscheduler.JobHttpTargetArgs{
				HttpMethod: pulumi.String("GET"),
				Uri:        pulumi.String("https://cloudscheduler.googleapis.com/v1/projects/my-project-name/locations/us-west1/jobs"),
				OauthToken: &cloudscheduler.JobHttpTargetOauthTokenArgs{
					ServiceAccountEmail: pulumi.String(_default.Email),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var @default = Gcp.Compute.GetDefaultServiceAccount.Invoke();

    var job = new Gcp.CloudScheduler.Job("job", new()
    {
        Name = "test-job",
        Description = "test http job",
        Schedule = "*/8 * * * *",
        TimeZone = "America/New_York",
        AttemptDeadline = "320s",
        HttpTarget = new Gcp.CloudScheduler.Inputs.JobHttpTargetArgs
        {
            HttpMethod = "GET",
            Uri = "https://cloudscheduler.googleapis.com/v1/projects/my-project-name/locations/us-west1/jobs",
            OauthToken = new Gcp.CloudScheduler.Inputs.JobHttpTargetOauthTokenArgs
            {
                ServiceAccountEmail = @default.Apply(@default => @default.Apply(getDefaultServiceAccountResult => getDefaultServiceAccountResult.Email)),
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.ComputeFunctions;
import com.pulumi.gcp.compute.inputs.GetDefaultServiceAccountArgs;
import com.pulumi.gcp.cloudscheduler.Job;
import com.pulumi.gcp.cloudscheduler.JobArgs;
import com.pulumi.gcp.cloudscheduler.inputs.JobHttpTargetArgs;
import com.pulumi.gcp.cloudscheduler.inputs.JobHttpTargetOauthTokenArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var default = ComputeFunctions.getDefaultServiceAccount(GetDefaultServiceAccountArgs.builder()
            .build());

        var job = new Job("job", JobArgs.builder()
            .name("test-job")
            .description("test http job")
            .schedule("*/8 * * * *")
            .timeZone("America/New_York")
            .attemptDeadline("320s")
            .httpTarget(JobHttpTargetArgs.builder()
                .httpMethod("GET")
                .uri("https://cloudscheduler.googleapis.com/v1/projects/my-project-name/locations/us-west1/jobs")
                .oauthToken(JobHttpTargetOauthTokenArgs.builder()
                    .serviceAccountEmail(default_.email())
                    .build())
                .build())
            .build());

    }
}
resources:
  job:
    type: gcp:cloudscheduler:Job
    properties:
      name: test-job
      description: test http job
      schedule: '*/8 * * * *'
      timeZone: America/New_York
      attemptDeadline: 320s
      httpTarget:
        httpMethod: GET
        uri: https://cloudscheduler.googleapis.com/v1/projects/my-project-name/locations/us-west1/jobs
        oauthToken:
          serviceAccountEmail: ${default.email}
variables:
  default:
    fn::invoke:
      function: gcp:compute:getDefaultServiceAccount
      arguments: {}

The oauthToken property enables OAuth authentication. Cloud Scheduler generates an access token using the specified service account and includes it in the Authorization header. This allows jobs to call authenticated Google Cloud APIs.

Authenticate HTTP requests with OIDC tokens

OpenID Connect tokens provide identity verification for calling Cloud Run, Cloud Functions, or other services that validate OIDC tokens.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const _default = gcp.compute.getDefaultServiceAccount({});
const job = new gcp.cloudscheduler.Job("job", {
    name: "test-job",
    description: "test http job",
    schedule: "*/8 * * * *",
    timeZone: "America/New_York",
    attemptDeadline: "320s",
    httpTarget: {
        httpMethod: "GET",
        uri: "https://example.com/ping",
        oidcToken: {
            serviceAccountEmail: _default.then(_default => _default.email),
        },
    },
});
import pulumi
import pulumi_gcp as gcp

default = gcp.compute.get_default_service_account()
job = gcp.cloudscheduler.Job("job",
    name="test-job",
    description="test http job",
    schedule="*/8 * * * *",
    time_zone="America/New_York",
    attempt_deadline="320s",
    http_target={
        "http_method": "GET",
        "uri": "https://example.com/ping",
        "oidc_token": {
            "service_account_email": default.email,
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudscheduler"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_default, err := compute.GetDefaultServiceAccount(ctx, &compute.GetDefaultServiceAccountArgs{}, nil)
		if err != nil {
			return err
		}
		_, err = cloudscheduler.NewJob(ctx, "job", &cloudscheduler.JobArgs{
			Name:            pulumi.String("test-job"),
			Description:     pulumi.String("test http job"),
			Schedule:        pulumi.String("*/8 * * * *"),
			TimeZone:        pulumi.String("America/New_York"),
			AttemptDeadline: pulumi.String("320s"),
			HttpTarget: &cloudscheduler.JobHttpTargetArgs{
				HttpMethod: pulumi.String("GET"),
				Uri:        pulumi.String("https://example.com/ping"),
				OidcToken: &cloudscheduler.JobHttpTargetOidcTokenArgs{
					ServiceAccountEmail: pulumi.String(_default.Email),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var @default = Gcp.Compute.GetDefaultServiceAccount.Invoke();

    var job = new Gcp.CloudScheduler.Job("job", new()
    {
        Name = "test-job",
        Description = "test http job",
        Schedule = "*/8 * * * *",
        TimeZone = "America/New_York",
        AttemptDeadline = "320s",
        HttpTarget = new Gcp.CloudScheduler.Inputs.JobHttpTargetArgs
        {
            HttpMethod = "GET",
            Uri = "https://example.com/ping",
            OidcToken = new Gcp.CloudScheduler.Inputs.JobHttpTargetOidcTokenArgs
            {
                ServiceAccountEmail = @default.Apply(@default => @default.Apply(getDefaultServiceAccountResult => getDefaultServiceAccountResult.Email)),
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.ComputeFunctions;
import com.pulumi.gcp.compute.inputs.GetDefaultServiceAccountArgs;
import com.pulumi.gcp.cloudscheduler.Job;
import com.pulumi.gcp.cloudscheduler.JobArgs;
import com.pulumi.gcp.cloudscheduler.inputs.JobHttpTargetArgs;
import com.pulumi.gcp.cloudscheduler.inputs.JobHttpTargetOidcTokenArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var default = ComputeFunctions.getDefaultServiceAccount(GetDefaultServiceAccountArgs.builder()
            .build());

        var job = new Job("job", JobArgs.builder()
            .name("test-job")
            .description("test http job")
            .schedule("*/8 * * * *")
            .timeZone("America/New_York")
            .attemptDeadline("320s")
            .httpTarget(JobHttpTargetArgs.builder()
                .httpMethod("GET")
                .uri("https://example.com/ping")
                .oidcToken(JobHttpTargetOidcTokenArgs.builder()
                    .serviceAccountEmail(default_.email())
                    .build())
                .build())
            .build());

    }
}
resources:
  job:
    type: gcp:cloudscheduler:Job
    properties:
      name: test-job
      description: test http job
      schedule: '*/8 * * * *'
      timeZone: America/New_York
      attemptDeadline: 320s
      httpTarget:
        httpMethod: GET
        uri: https://example.com/ping
        oidcToken:
          serviceAccountEmail: ${default.email}
variables:
  default:
    fn::invoke:
      function: gcp:compute:getDefaultServiceAccount
      arguments: {}

The oidcToken property enables OIDC authentication. Cloud Scheduler generates an identity token using the specified service account. This is the preferred method for calling Cloud Run and Cloud Functions endpoints.

Beyond these examples

These snippets focus on specific job-level features: Pub/Sub, HTTP, and App Engine targets, OAuth and OIDC authentication, and retry configuration and pause control. They’re intentionally minimal rather than full scheduling solutions.

The examples may reference pre-existing infrastructure such as Pub/Sub topics (for Pub/Sub targets), service accounts (for OAuth/OIDC authentication), and App Engine services (for App Engine targets). They focus on configuring the job rather than provisioning everything around it.

To keep things focused, common job patterns are omitted, including:

  • Custom retry backoff tuning (minBackoffDuration, maxDoublings)
  • Time zone configuration for non-UTC schedules
  • Request body encoding and header customization
  • Deadline configuration for different target types

These omissions are intentional: the goal is to illustrate how each job feature is wired, not provide drop-in scheduling modules. See the Cloud Scheduler Job resource reference for all available configuration options.

Let's create GCP Cloud Scheduler Jobs

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Common Issues & Limitations
Why am I seeing an unresolvable diff when using PubSub targets?
The attemptDeadline field is ignored for PubSub targets. Setting it will introduce an unresolvable diff, so omit this property when using pubsubTarget.
Why do I need to base64-encode HTTP body and PubSub data?
Both httpTarget.body and pubsubTarget.data require base64-encoded strings. Use std.base64encode() as shown in the examples to avoid errors.
What properties can't I change after creation?
The name, region, and project properties are immutable. Changing them requires recreating the resource.
Target Types & Configuration
What target types can I use for scheduled jobs?
You can use three mutually exclusive targets: pubsubTarget (publish to Pub/Sub), httpTarget (make HTTP requests), or appEngineHttpTarget (target App Engine services).
How do I publish messages to Pub/Sub on a schedule?
Configure pubsubTarget with topicName pointing to your topic ID and base64-encoded data.
How do I make HTTP requests on a schedule?
Configure httpTarget with httpMethod, uri, and optionally base64-encoded body and headers.
How do I target an App Engine service?
Use appEngineHttpTarget with appEngineRouting to specify service, version, and instance.
Scheduling & Timing
What format does the schedule use?
The schedule property uses crontab format (e.g., */2 * * * * runs every 2 minutes). Specify timeZone using a name from the tz database.
What are the attemptDeadline limits for different targets?
HTTP targets allow 15 seconds to 30 minutes. App Engine targets allow 15 seconds to 24 hours. PubSub targets ignore this field entirely.
Authentication & Control
How do I authenticate HTTP requests with service accounts?
For OAuth, use httpTarget.oauthToken with serviceAccountEmail. For OIDC, use httpTarget.oidcToken with serviceAccountEmail.
How do I pause a scheduled job?
Set paused to true. Jobs default to enabled when this property is not set.

Using a different cloud?

Explore integration guides for other cloud providers: