The gcp:osconfig/patchDeployment:PatchDeployment resource, part of the Pulumi GCP provider, defines OS Config patch deployments that schedule and configure automated patching for Compute Engine VM instances. This guide focuses on three capabilities: one-time and recurring schedules, instance filtering, and package exclusions with execution hooks.
Patch deployments target existing Compute Engine instances and may reference GCS buckets for pre/post patch scripts. The examples are intentionally small. Combine them with your own instance filters, patch policies, and maintenance windows.
Schedule a one-time patch deployment
Teams often need to patch systems once for a specific maintenance window or emergency security update.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const patch = new gcp.osconfig.PatchDeployment("patch", {
patchDeploymentId: "patch-deploy",
instanceFilter: {
all: true,
},
oneTimeSchedule: {
executeTime: "2999-10-10T10:10:10.045123456Z",
},
});
import pulumi
import pulumi_gcp as gcp
patch = gcp.osconfig.PatchDeployment("patch",
patch_deployment_id="patch-deploy",
instance_filter={
"all": True,
},
one_time_schedule={
"execute_time": "2999-10-10T10:10:10.045123456Z",
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/osconfig"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := osconfig.NewPatchDeployment(ctx, "patch", &osconfig.PatchDeploymentArgs{
PatchDeploymentId: pulumi.String("patch-deploy"),
InstanceFilter: &osconfig.PatchDeploymentInstanceFilterArgs{
All: pulumi.Bool(true),
},
OneTimeSchedule: &osconfig.PatchDeploymentOneTimeScheduleArgs{
ExecuteTime: pulumi.String("2999-10-10T10:10:10.045123456Z"),
},
})
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 patch = new Gcp.OsConfig.PatchDeployment("patch", new()
{
PatchDeploymentId = "patch-deploy",
InstanceFilter = new Gcp.OsConfig.Inputs.PatchDeploymentInstanceFilterArgs
{
All = true,
},
OneTimeSchedule = new Gcp.OsConfig.Inputs.PatchDeploymentOneTimeScheduleArgs
{
ExecuteTime = "2999-10-10T10:10:10.045123456Z",
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.osconfig.PatchDeployment;
import com.pulumi.gcp.osconfig.PatchDeploymentArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentInstanceFilterArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentOneTimeScheduleArgs;
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 patch = new PatchDeployment("patch", PatchDeploymentArgs.builder()
.patchDeploymentId("patch-deploy")
.instanceFilter(PatchDeploymentInstanceFilterArgs.builder()
.all(true)
.build())
.oneTimeSchedule(PatchDeploymentOneTimeScheduleArgs.builder()
.executeTime("2999-10-10T10:10:10.045123456Z")
.build())
.build());
}
}
resources:
patch:
type: gcp:osconfig:PatchDeployment
properties:
patchDeploymentId: patch-deploy
instanceFilter:
all: true
oneTimeSchedule:
executeTime: 2999-10-10T10:10:10.045123456Z
The oneTimeSchedule property defines a single execution time in RFC3339 format. The instanceFilter with all: true targets every VM instance in the project. After execution, the deployment remains defined but won’t run again unless you update the schedule.
Run patches daily at a specific time
Production environments often patch systems during off-peak hours to minimize disruption.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const patch = new gcp.osconfig.PatchDeployment("patch", {
patchDeploymentId: "patch-deploy",
instanceFilter: {
all: true,
},
recurringSchedule: {
timeZone: {
id: "America/New_York",
},
timeOfDay: {
hours: 0,
minutes: 30,
seconds: 30,
nanos: 20,
},
},
});
import pulumi
import pulumi_gcp as gcp
patch = gcp.osconfig.PatchDeployment("patch",
patch_deployment_id="patch-deploy",
instance_filter={
"all": True,
},
recurring_schedule={
"time_zone": {
"id": "America/New_York",
},
"time_of_day": {
"hours": 0,
"minutes": 30,
"seconds": 30,
"nanos": 20,
},
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/osconfig"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := osconfig.NewPatchDeployment(ctx, "patch", &osconfig.PatchDeploymentArgs{
PatchDeploymentId: pulumi.String("patch-deploy"),
InstanceFilter: &osconfig.PatchDeploymentInstanceFilterArgs{
All: pulumi.Bool(true),
},
RecurringSchedule: &osconfig.PatchDeploymentRecurringScheduleArgs{
TimeZone: &osconfig.PatchDeploymentRecurringScheduleTimeZoneArgs{
Id: pulumi.String("America/New_York"),
},
TimeOfDay: &osconfig.PatchDeploymentRecurringScheduleTimeOfDayArgs{
Hours: pulumi.Int(0),
Minutes: pulumi.Int(30),
Seconds: pulumi.Int(30),
Nanos: pulumi.Int(20),
},
},
})
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 patch = new Gcp.OsConfig.PatchDeployment("patch", new()
{
PatchDeploymentId = "patch-deploy",
InstanceFilter = new Gcp.OsConfig.Inputs.PatchDeploymentInstanceFilterArgs
{
All = true,
},
RecurringSchedule = new Gcp.OsConfig.Inputs.PatchDeploymentRecurringScheduleArgs
{
TimeZone = new Gcp.OsConfig.Inputs.PatchDeploymentRecurringScheduleTimeZoneArgs
{
Id = "America/New_York",
},
TimeOfDay = new Gcp.OsConfig.Inputs.PatchDeploymentRecurringScheduleTimeOfDayArgs
{
Hours = 0,
Minutes = 30,
Seconds = 30,
Nanos = 20,
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.osconfig.PatchDeployment;
import com.pulumi.gcp.osconfig.PatchDeploymentArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentInstanceFilterArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRecurringScheduleArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRecurringScheduleTimeZoneArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRecurringScheduleTimeOfDayArgs;
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 patch = new PatchDeployment("patch", PatchDeploymentArgs.builder()
.patchDeploymentId("patch-deploy")
.instanceFilter(PatchDeploymentInstanceFilterArgs.builder()
.all(true)
.build())
.recurringSchedule(PatchDeploymentRecurringScheduleArgs.builder()
.timeZone(PatchDeploymentRecurringScheduleTimeZoneArgs.builder()
.id("America/New_York")
.build())
.timeOfDay(PatchDeploymentRecurringScheduleTimeOfDayArgs.builder()
.hours(0)
.minutes(30)
.seconds(30)
.nanos(20)
.build())
.build())
.build());
}
}
resources:
patch:
type: gcp:osconfig:PatchDeployment
properties:
patchDeploymentId: patch-deploy
instanceFilter:
all: true
recurringSchedule:
timeZone:
id: America/New_York
timeOfDay:
hours: 0
minutes: 30
seconds: 30
nanos: 20
The recurringSchedule property automates patch execution at consistent times. The timeZone property ensures patches run at the correct local time regardless of where instances are located. The timeOfDay property specifies hours, minutes, seconds, and nanoseconds for precise scheduling. Without a monthly or weekly property, this schedule runs daily.
Target specific instances with package filters
Applications with strict change control requirements need to patch only designated instances and exclude specific packages.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const myImage = gcp.compute.getImage({
family: "debian-11",
project: "debian-cloud",
});
const foobar = new gcp.compute.Instance("foobar", {
name: "patch-deploy-inst",
machineType: "e2-medium",
zone: "us-central1-a",
canIpForward: false,
tags: [
"foo",
"bar",
],
bootDisk: {
initializeParams: {
image: myImage.then(myImage => myImage.selfLink),
},
},
networkInterfaces: [{
network: "default",
}],
metadata: {
foo: "bar",
},
});
const patch = new gcp.osconfig.PatchDeployment("patch", {
patchDeploymentId: "patch-deploy",
instanceFilter: {
instances: [foobar.id],
},
patchConfig: {
yum: {
security: true,
minimal: true,
excludes: ["bash"],
},
},
recurringSchedule: {
timeZone: {
id: "America/New_York",
},
timeOfDay: {
hours: 0,
minutes: 30,
seconds: 30,
nanos: 20,
},
monthly: {
monthDay: 1,
},
},
});
import pulumi
import pulumi_gcp as gcp
my_image = gcp.compute.get_image(family="debian-11",
project="debian-cloud")
foobar = gcp.compute.Instance("foobar",
name="patch-deploy-inst",
machine_type="e2-medium",
zone="us-central1-a",
can_ip_forward=False,
tags=[
"foo",
"bar",
],
boot_disk={
"initialize_params": {
"image": my_image.self_link,
},
},
network_interfaces=[{
"network": "default",
}],
metadata={
"foo": "bar",
})
patch = gcp.osconfig.PatchDeployment("patch",
patch_deployment_id="patch-deploy",
instance_filter={
"instances": [foobar.id],
},
patch_config={
"yum": {
"security": True,
"minimal": True,
"excludes": ["bash"],
},
},
recurring_schedule={
"time_zone": {
"id": "America/New_York",
},
"time_of_day": {
"hours": 0,
"minutes": 30,
"seconds": 30,
"nanos": 20,
},
"monthly": {
"month_day": 1,
},
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/osconfig"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
myImage, err := compute.LookupImage(ctx, &compute.LookupImageArgs{
Family: pulumi.StringRef("debian-11"),
Project: pulumi.StringRef("debian-cloud"),
}, nil)
if err != nil {
return err
}
foobar, err := compute.NewInstance(ctx, "foobar", &compute.InstanceArgs{
Name: pulumi.String("patch-deploy-inst"),
MachineType: pulumi.String("e2-medium"),
Zone: pulumi.String("us-central1-a"),
CanIpForward: pulumi.Bool(false),
Tags: pulumi.StringArray{
pulumi.String("foo"),
pulumi.String("bar"),
},
BootDisk: &compute.InstanceBootDiskArgs{
InitializeParams: &compute.InstanceBootDiskInitializeParamsArgs{
Image: pulumi.String(myImage.SelfLink),
},
},
NetworkInterfaces: compute.InstanceNetworkInterfaceArray{
&compute.InstanceNetworkInterfaceArgs{
Network: pulumi.String("default"),
},
},
Metadata: pulumi.StringMap{
"foo": pulumi.String("bar"),
},
})
if err != nil {
return err
}
_, err = osconfig.NewPatchDeployment(ctx, "patch", &osconfig.PatchDeploymentArgs{
PatchDeploymentId: pulumi.String("patch-deploy"),
InstanceFilter: &osconfig.PatchDeploymentInstanceFilterArgs{
Instances: pulumi.StringArray{
foobar.ID(),
},
},
PatchConfig: &osconfig.PatchDeploymentPatchConfigArgs{
Yum: &osconfig.PatchDeploymentPatchConfigYumArgs{
Security: pulumi.Bool(true),
Minimal: pulumi.Bool(true),
Excludes: pulumi.StringArray{
pulumi.String("bash"),
},
},
},
RecurringSchedule: &osconfig.PatchDeploymentRecurringScheduleArgs{
TimeZone: &osconfig.PatchDeploymentRecurringScheduleTimeZoneArgs{
Id: pulumi.String("America/New_York"),
},
TimeOfDay: &osconfig.PatchDeploymentRecurringScheduleTimeOfDayArgs{
Hours: pulumi.Int(0),
Minutes: pulumi.Int(30),
Seconds: pulumi.Int(30),
Nanos: pulumi.Int(20),
},
Monthly: &osconfig.PatchDeploymentRecurringScheduleMonthlyArgs{
MonthDay: pulumi.Int(1),
},
},
})
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 myImage = Gcp.Compute.GetImage.Invoke(new()
{
Family = "debian-11",
Project = "debian-cloud",
});
var foobar = new Gcp.Compute.Instance("foobar", new()
{
Name = "patch-deploy-inst",
MachineType = "e2-medium",
Zone = "us-central1-a",
CanIpForward = false,
Tags = new[]
{
"foo",
"bar",
},
BootDisk = new Gcp.Compute.Inputs.InstanceBootDiskArgs
{
InitializeParams = new Gcp.Compute.Inputs.InstanceBootDiskInitializeParamsArgs
{
Image = myImage.Apply(getImageResult => getImageResult.SelfLink),
},
},
NetworkInterfaces = new[]
{
new Gcp.Compute.Inputs.InstanceNetworkInterfaceArgs
{
Network = "default",
},
},
Metadata =
{
{ "foo", "bar" },
},
});
var patch = new Gcp.OsConfig.PatchDeployment("patch", new()
{
PatchDeploymentId = "patch-deploy",
InstanceFilter = new Gcp.OsConfig.Inputs.PatchDeploymentInstanceFilterArgs
{
Instances = new[]
{
foobar.Id,
},
},
PatchConfig = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigArgs
{
Yum = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigYumArgs
{
Security = true,
Minimal = true,
Excludes = new[]
{
"bash",
},
},
},
RecurringSchedule = new Gcp.OsConfig.Inputs.PatchDeploymentRecurringScheduleArgs
{
TimeZone = new Gcp.OsConfig.Inputs.PatchDeploymentRecurringScheduleTimeZoneArgs
{
Id = "America/New_York",
},
TimeOfDay = new Gcp.OsConfig.Inputs.PatchDeploymentRecurringScheduleTimeOfDayArgs
{
Hours = 0,
Minutes = 30,
Seconds = 30,
Nanos = 20,
},
Monthly = new Gcp.OsConfig.Inputs.PatchDeploymentRecurringScheduleMonthlyArgs
{
MonthDay = 1,
},
},
});
});
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.GetImageArgs;
import com.pulumi.gcp.compute.Instance;
import com.pulumi.gcp.compute.InstanceArgs;
import com.pulumi.gcp.compute.inputs.InstanceBootDiskArgs;
import com.pulumi.gcp.compute.inputs.InstanceBootDiskInitializeParamsArgs;
import com.pulumi.gcp.compute.inputs.InstanceNetworkInterfaceArgs;
import com.pulumi.gcp.osconfig.PatchDeployment;
import com.pulumi.gcp.osconfig.PatchDeploymentArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentInstanceFilterArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigYumArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRecurringScheduleArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRecurringScheduleTimeZoneArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRecurringScheduleTimeOfDayArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRecurringScheduleMonthlyArgs;
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 myImage = ComputeFunctions.getImage(GetImageArgs.builder()
.family("debian-11")
.project("debian-cloud")
.build());
var foobar = new Instance("foobar", InstanceArgs.builder()
.name("patch-deploy-inst")
.machineType("e2-medium")
.zone("us-central1-a")
.canIpForward(false)
.tags(
"foo",
"bar")
.bootDisk(InstanceBootDiskArgs.builder()
.initializeParams(InstanceBootDiskInitializeParamsArgs.builder()
.image(myImage.selfLink())
.build())
.build())
.networkInterfaces(InstanceNetworkInterfaceArgs.builder()
.network("default")
.build())
.metadata(Map.of("foo", "bar"))
.build());
var patch = new PatchDeployment("patch", PatchDeploymentArgs.builder()
.patchDeploymentId("patch-deploy")
.instanceFilter(PatchDeploymentInstanceFilterArgs.builder()
.instances(foobar.id())
.build())
.patchConfig(PatchDeploymentPatchConfigArgs.builder()
.yum(PatchDeploymentPatchConfigYumArgs.builder()
.security(true)
.minimal(true)
.excludes("bash")
.build())
.build())
.recurringSchedule(PatchDeploymentRecurringScheduleArgs.builder()
.timeZone(PatchDeploymentRecurringScheduleTimeZoneArgs.builder()
.id("America/New_York")
.build())
.timeOfDay(PatchDeploymentRecurringScheduleTimeOfDayArgs.builder()
.hours(0)
.minutes(30)
.seconds(30)
.nanos(20)
.build())
.monthly(PatchDeploymentRecurringScheduleMonthlyArgs.builder()
.monthDay(1)
.build())
.build())
.build());
}
}
resources:
foobar:
type: gcp:compute:Instance
properties:
name: patch-deploy-inst
machineType: e2-medium
zone: us-central1-a
canIpForward: false
tags:
- foo
- bar
bootDisk:
initializeParams:
image: ${myImage.selfLink}
networkInterfaces:
- network: default
metadata:
foo: bar
patch:
type: gcp:osconfig:PatchDeployment
properties:
patchDeploymentId: patch-deploy
instanceFilter:
instances:
- ${foobar.id}
patchConfig:
yum:
security: true
minimal: true
excludes:
- bash
recurringSchedule:
timeZone:
id: America/New_York
timeOfDay:
hours: 0
minutes: 30
seconds: 30
nanos: 20
monthly:
monthDay: 1
variables:
myImage:
fn::invoke:
function: gcp:compute:getImage
arguments:
family: debian-11
project: debian-cloud
The instanceFilter.instances property accepts a list of instance IDs to target. The patchConfig.yum block controls Red Hat-based patching: security: true limits updates to security patches, minimal: true applies only critical fixes, and excludes prevents specific packages from updating. The monthly.monthDay property schedules execution on the first day of each month.
Configure advanced filtering and execution hooks
Enterprise deployments require fine-grained control over which instances receive patches, what packages are updated, and what scripts run before and after patching.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const patch = new gcp.osconfig.PatchDeployment("patch", {
patchDeploymentId: "patch-deploy",
instanceFilter: {
groupLabels: [{
labels: {
env: "dev",
app: "web",
},
}],
instanceNamePrefixes: ["test-"],
zones: [
"us-central1-a",
"us-central-1c",
],
},
patchConfig: {
migInstancesAllowed: true,
rebootConfig: "ALWAYS",
apt: {
type: "DIST",
excludes: ["python"],
},
yum: {
security: true,
minimal: true,
excludes: ["bash"],
},
goo: {
enabled: true,
},
zypper: {
categories: ["security"],
},
windowsUpdate: {
classifications: [
"CRITICAL",
"SECURITY",
"UPDATE",
],
excludes: ["5012170"],
},
preStep: {
linuxExecStepConfig: {
allowedSuccessCodes: [
0,
3,
],
localPath: "/tmp/pre_patch_script.sh",
},
windowsExecStepConfig: {
interpreter: "SHELL",
allowedSuccessCodes: [
0,
2,
],
localPath: "C:\\Users\\user\\pre-patch-script.cmd",
},
},
postStep: {
linuxExecStepConfig: {
gcsObject: {
bucket: "my-patch-scripts",
generationNumber: "1523477886880",
object: "linux/post_patch_script",
},
},
windowsExecStepConfig: {
interpreter: "POWERSHELL",
gcsObject: {
bucket: "my-patch-scripts",
generationNumber: "135920493447",
object: "windows/post_patch_script.ps1",
},
},
},
},
duration: "10s",
recurringSchedule: {
timeZone: {
id: "America/New_York",
},
timeOfDay: {
hours: 0,
minutes: 30,
seconds: 30,
nanos: 20,
},
monthly: {
weekDayOfMonth: {
weekOrdinal: -1,
dayOfWeek: "TUESDAY",
dayOffset: 3,
},
},
},
rollout: {
mode: "ZONE_BY_ZONE",
disruptionBudget: {
fixed: 1,
},
},
});
import pulumi
import pulumi_gcp as gcp
patch = gcp.osconfig.PatchDeployment("patch",
patch_deployment_id="patch-deploy",
instance_filter={
"group_labels": [{
"labels": {
"env": "dev",
"app": "web",
},
}],
"instance_name_prefixes": ["test-"],
"zones": [
"us-central1-a",
"us-central-1c",
],
},
patch_config={
"mig_instances_allowed": True,
"reboot_config": "ALWAYS",
"apt": {
"type": "DIST",
"excludes": ["python"],
},
"yum": {
"security": True,
"minimal": True,
"excludes": ["bash"],
},
"goo": {
"enabled": True,
},
"zypper": {
"categories": ["security"],
},
"windows_update": {
"classifications": [
"CRITICAL",
"SECURITY",
"UPDATE",
],
"excludes": ["5012170"],
},
"pre_step": {
"linux_exec_step_config": {
"allowed_success_codes": [
0,
3,
],
"local_path": "/tmp/pre_patch_script.sh",
},
"windows_exec_step_config": {
"interpreter": "SHELL",
"allowed_success_codes": [
0,
2,
],
"local_path": "C:\\Users\\user\\pre-patch-script.cmd",
},
},
"post_step": {
"linux_exec_step_config": {
"gcs_object": {
"bucket": "my-patch-scripts",
"generation_number": "1523477886880",
"object": "linux/post_patch_script",
},
},
"windows_exec_step_config": {
"interpreter": "POWERSHELL",
"gcs_object": {
"bucket": "my-patch-scripts",
"generation_number": "135920493447",
"object": "windows/post_patch_script.ps1",
},
},
},
},
duration="10s",
recurring_schedule={
"time_zone": {
"id": "America/New_York",
},
"time_of_day": {
"hours": 0,
"minutes": 30,
"seconds": 30,
"nanos": 20,
},
"monthly": {
"week_day_of_month": {
"week_ordinal": -1,
"day_of_week": "TUESDAY",
"day_offset": 3,
},
},
},
rollout={
"mode": "ZONE_BY_ZONE",
"disruption_budget": {
"fixed": 1,
},
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/osconfig"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := osconfig.NewPatchDeployment(ctx, "patch", &osconfig.PatchDeploymentArgs{
PatchDeploymentId: pulumi.String("patch-deploy"),
InstanceFilter: &osconfig.PatchDeploymentInstanceFilterArgs{
GroupLabels: osconfig.PatchDeploymentInstanceFilterGroupLabelArray{
&osconfig.PatchDeploymentInstanceFilterGroupLabelArgs{
Labels: pulumi.StringMap{
"env": pulumi.String("dev"),
"app": pulumi.String("web"),
},
},
},
InstanceNamePrefixes: pulumi.StringArray{
pulumi.String("test-"),
},
Zones: pulumi.StringArray{
pulumi.String("us-central1-a"),
pulumi.String("us-central-1c"),
},
},
PatchConfig: &osconfig.PatchDeploymentPatchConfigArgs{
MigInstancesAllowed: pulumi.Bool(true),
RebootConfig: pulumi.String("ALWAYS"),
Apt: &osconfig.PatchDeploymentPatchConfigAptArgs{
Type: pulumi.String("DIST"),
Excludes: pulumi.StringArray{
pulumi.String("python"),
},
},
Yum: &osconfig.PatchDeploymentPatchConfigYumArgs{
Security: pulumi.Bool(true),
Minimal: pulumi.Bool(true),
Excludes: pulumi.StringArray{
pulumi.String("bash"),
},
},
Goo: &osconfig.PatchDeploymentPatchConfigGooArgs{
Enabled: pulumi.Bool(true),
},
Zypper: &osconfig.PatchDeploymentPatchConfigZypperArgs{
Categories: pulumi.StringArray{
pulumi.String("security"),
},
},
WindowsUpdate: &osconfig.PatchDeploymentPatchConfigWindowsUpdateArgs{
Classifications: pulumi.StringArray{
pulumi.String("CRITICAL"),
pulumi.String("SECURITY"),
pulumi.String("UPDATE"),
},
Excludes: pulumi.StringArray{
pulumi.String("5012170"),
},
},
PreStep: &osconfig.PatchDeploymentPatchConfigPreStepArgs{
LinuxExecStepConfig: &osconfig.PatchDeploymentPatchConfigPreStepLinuxExecStepConfigArgs{
AllowedSuccessCodes: pulumi.IntArray{
pulumi.Int(0),
pulumi.Int(3),
},
LocalPath: pulumi.String("/tmp/pre_patch_script.sh"),
},
WindowsExecStepConfig: &osconfig.PatchDeploymentPatchConfigPreStepWindowsExecStepConfigArgs{
Interpreter: pulumi.String("SHELL"),
AllowedSuccessCodes: pulumi.IntArray{
pulumi.Int(0),
pulumi.Int(2),
},
LocalPath: pulumi.String("C:\\Users\\user\\pre-patch-script.cmd"),
},
},
PostStep: &osconfig.PatchDeploymentPatchConfigPostStepArgs{
LinuxExecStepConfig: &osconfig.PatchDeploymentPatchConfigPostStepLinuxExecStepConfigArgs{
GcsObject: &osconfig.PatchDeploymentPatchConfigPostStepLinuxExecStepConfigGcsObjectArgs{
Bucket: pulumi.String("my-patch-scripts"),
GenerationNumber: pulumi.String("1523477886880"),
Object: pulumi.String("linux/post_patch_script"),
},
},
WindowsExecStepConfig: &osconfig.PatchDeploymentPatchConfigPostStepWindowsExecStepConfigArgs{
Interpreter: pulumi.String("POWERSHELL"),
GcsObject: &osconfig.PatchDeploymentPatchConfigPostStepWindowsExecStepConfigGcsObjectArgs{
Bucket: pulumi.String("my-patch-scripts"),
GenerationNumber: pulumi.String("135920493447"),
Object: pulumi.String("windows/post_patch_script.ps1"),
},
},
},
},
Duration: pulumi.String("10s"),
RecurringSchedule: &osconfig.PatchDeploymentRecurringScheduleArgs{
TimeZone: &osconfig.PatchDeploymentRecurringScheduleTimeZoneArgs{
Id: pulumi.String("America/New_York"),
},
TimeOfDay: &osconfig.PatchDeploymentRecurringScheduleTimeOfDayArgs{
Hours: pulumi.Int(0),
Minutes: pulumi.Int(30),
Seconds: pulumi.Int(30),
Nanos: pulumi.Int(20),
},
Monthly: &osconfig.PatchDeploymentRecurringScheduleMonthlyArgs{
WeekDayOfMonth: &osconfig.PatchDeploymentRecurringScheduleMonthlyWeekDayOfMonthArgs{
WeekOrdinal: pulumi.Int(-1),
DayOfWeek: pulumi.String("TUESDAY"),
DayOffset: pulumi.Int(3),
},
},
},
Rollout: &osconfig.PatchDeploymentRolloutArgs{
Mode: pulumi.String("ZONE_BY_ZONE"),
DisruptionBudget: &osconfig.PatchDeploymentRolloutDisruptionBudgetArgs{
Fixed: pulumi.Int(1),
},
},
})
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 patch = new Gcp.OsConfig.PatchDeployment("patch", new()
{
PatchDeploymentId = "patch-deploy",
InstanceFilter = new Gcp.OsConfig.Inputs.PatchDeploymentInstanceFilterArgs
{
GroupLabels = new[]
{
new Gcp.OsConfig.Inputs.PatchDeploymentInstanceFilterGroupLabelArgs
{
Labels =
{
{ "env", "dev" },
{ "app", "web" },
},
},
},
InstanceNamePrefixes = new[]
{
"test-",
},
Zones = new[]
{
"us-central1-a",
"us-central-1c",
},
},
PatchConfig = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigArgs
{
MigInstancesAllowed = true,
RebootConfig = "ALWAYS",
Apt = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigAptArgs
{
Type = "DIST",
Excludes = new[]
{
"python",
},
},
Yum = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigYumArgs
{
Security = true,
Minimal = true,
Excludes = new[]
{
"bash",
},
},
Goo = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigGooArgs
{
Enabled = true,
},
Zypper = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigZypperArgs
{
Categories = new[]
{
"security",
},
},
WindowsUpdate = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigWindowsUpdateArgs
{
Classifications = new[]
{
"CRITICAL",
"SECURITY",
"UPDATE",
},
Excludes = new[]
{
"5012170",
},
},
PreStep = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigPreStepArgs
{
LinuxExecStepConfig = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigPreStepLinuxExecStepConfigArgs
{
AllowedSuccessCodes = new[]
{
0,
3,
},
LocalPath = "/tmp/pre_patch_script.sh",
},
WindowsExecStepConfig = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigPreStepWindowsExecStepConfigArgs
{
Interpreter = "SHELL",
AllowedSuccessCodes = new[]
{
0,
2,
},
LocalPath = "C:\\Users\\user\\pre-patch-script.cmd",
},
},
PostStep = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigPostStepArgs
{
LinuxExecStepConfig = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigPostStepLinuxExecStepConfigArgs
{
GcsObject = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigPostStepLinuxExecStepConfigGcsObjectArgs
{
Bucket = "my-patch-scripts",
GenerationNumber = "1523477886880",
Object = "linux/post_patch_script",
},
},
WindowsExecStepConfig = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigPostStepWindowsExecStepConfigArgs
{
Interpreter = "POWERSHELL",
GcsObject = new Gcp.OsConfig.Inputs.PatchDeploymentPatchConfigPostStepWindowsExecStepConfigGcsObjectArgs
{
Bucket = "my-patch-scripts",
GenerationNumber = "135920493447",
Object = "windows/post_patch_script.ps1",
},
},
},
},
Duration = "10s",
RecurringSchedule = new Gcp.OsConfig.Inputs.PatchDeploymentRecurringScheduleArgs
{
TimeZone = new Gcp.OsConfig.Inputs.PatchDeploymentRecurringScheduleTimeZoneArgs
{
Id = "America/New_York",
},
TimeOfDay = new Gcp.OsConfig.Inputs.PatchDeploymentRecurringScheduleTimeOfDayArgs
{
Hours = 0,
Minutes = 30,
Seconds = 30,
Nanos = 20,
},
Monthly = new Gcp.OsConfig.Inputs.PatchDeploymentRecurringScheduleMonthlyArgs
{
WeekDayOfMonth = new Gcp.OsConfig.Inputs.PatchDeploymentRecurringScheduleMonthlyWeekDayOfMonthArgs
{
WeekOrdinal = -1,
DayOfWeek = "TUESDAY",
DayOffset = 3,
},
},
},
Rollout = new Gcp.OsConfig.Inputs.PatchDeploymentRolloutArgs
{
Mode = "ZONE_BY_ZONE",
DisruptionBudget = new Gcp.OsConfig.Inputs.PatchDeploymentRolloutDisruptionBudgetArgs
{
Fixed = 1,
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.osconfig.PatchDeployment;
import com.pulumi.gcp.osconfig.PatchDeploymentArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentInstanceFilterArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigAptArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigYumArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigGooArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigZypperArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigWindowsUpdateArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigPreStepArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigPreStepLinuxExecStepConfigArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigPreStepWindowsExecStepConfigArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigPostStepArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigPostStepLinuxExecStepConfigArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigPostStepLinuxExecStepConfigGcsObjectArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigPostStepWindowsExecStepConfigArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentPatchConfigPostStepWindowsExecStepConfigGcsObjectArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRecurringScheduleArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRecurringScheduleTimeZoneArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRecurringScheduleTimeOfDayArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRecurringScheduleMonthlyArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRecurringScheduleMonthlyWeekDayOfMonthArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRolloutArgs;
import com.pulumi.gcp.osconfig.inputs.PatchDeploymentRolloutDisruptionBudgetArgs;
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 patch = new PatchDeployment("patch", PatchDeploymentArgs.builder()
.patchDeploymentId("patch-deploy")
.instanceFilter(PatchDeploymentInstanceFilterArgs.builder()
.groupLabels(PatchDeploymentInstanceFilterGroupLabelArgs.builder()
.labels(Map.ofEntries(
Map.entry("env", "dev"),
Map.entry("app", "web")
))
.build())
.instanceNamePrefixes("test-")
.zones(
"us-central1-a",
"us-central-1c")
.build())
.patchConfig(PatchDeploymentPatchConfigArgs.builder()
.migInstancesAllowed(true)
.rebootConfig("ALWAYS")
.apt(PatchDeploymentPatchConfigAptArgs.builder()
.type("DIST")
.excludes("python")
.build())
.yum(PatchDeploymentPatchConfigYumArgs.builder()
.security(true)
.minimal(true)
.excludes("bash")
.build())
.goo(PatchDeploymentPatchConfigGooArgs.builder()
.enabled(true)
.build())
.zypper(PatchDeploymentPatchConfigZypperArgs.builder()
.categories("security")
.build())
.windowsUpdate(PatchDeploymentPatchConfigWindowsUpdateArgs.builder()
.classifications(
"CRITICAL",
"SECURITY",
"UPDATE")
.excludes("5012170")
.build())
.preStep(PatchDeploymentPatchConfigPreStepArgs.builder()
.linuxExecStepConfig(PatchDeploymentPatchConfigPreStepLinuxExecStepConfigArgs.builder()
.allowedSuccessCodes(
0,
3)
.localPath("/tmp/pre_patch_script.sh")
.build())
.windowsExecStepConfig(PatchDeploymentPatchConfigPreStepWindowsExecStepConfigArgs.builder()
.interpreter("SHELL")
.allowedSuccessCodes(
0,
2)
.localPath("C:\\Users\\user\\pre-patch-script.cmd")
.build())
.build())
.postStep(PatchDeploymentPatchConfigPostStepArgs.builder()
.linuxExecStepConfig(PatchDeploymentPatchConfigPostStepLinuxExecStepConfigArgs.builder()
.gcsObject(PatchDeploymentPatchConfigPostStepLinuxExecStepConfigGcsObjectArgs.builder()
.bucket("my-patch-scripts")
.generationNumber("1523477886880")
.object("linux/post_patch_script")
.build())
.build())
.windowsExecStepConfig(PatchDeploymentPatchConfigPostStepWindowsExecStepConfigArgs.builder()
.interpreter("POWERSHELL")
.gcsObject(PatchDeploymentPatchConfigPostStepWindowsExecStepConfigGcsObjectArgs.builder()
.bucket("my-patch-scripts")
.generationNumber("135920493447")
.object("windows/post_patch_script.ps1")
.build())
.build())
.build())
.build())
.duration("10s")
.recurringSchedule(PatchDeploymentRecurringScheduleArgs.builder()
.timeZone(PatchDeploymentRecurringScheduleTimeZoneArgs.builder()
.id("America/New_York")
.build())
.timeOfDay(PatchDeploymentRecurringScheduleTimeOfDayArgs.builder()
.hours(0)
.minutes(30)
.seconds(30)
.nanos(20)
.build())
.monthly(PatchDeploymentRecurringScheduleMonthlyArgs.builder()
.weekDayOfMonth(PatchDeploymentRecurringScheduleMonthlyWeekDayOfMonthArgs.builder()
.weekOrdinal(-1)
.dayOfWeek("TUESDAY")
.dayOffset(3)
.build())
.build())
.build())
.rollout(PatchDeploymentRolloutArgs.builder()
.mode("ZONE_BY_ZONE")
.disruptionBudget(PatchDeploymentRolloutDisruptionBudgetArgs.builder()
.fixed(1)
.build())
.build())
.build());
}
}
resources:
patch:
type: gcp:osconfig:PatchDeployment
properties:
patchDeploymentId: patch-deploy
instanceFilter:
groupLabels:
- labels:
env: dev
app: web
instanceNamePrefixes:
- test-
zones:
- us-central1-a
- us-central-1c
patchConfig:
migInstancesAllowed: true
rebootConfig: ALWAYS
apt:
type: DIST
excludes:
- python
yum:
security: true
minimal: true
excludes:
- bash
goo:
enabled: true
zypper:
categories:
- security
windowsUpdate:
classifications:
- CRITICAL
- SECURITY
- UPDATE
excludes:
- '5012170'
preStep:
linuxExecStepConfig:
allowedSuccessCodes:
- 0
- 3
localPath: /tmp/pre_patch_script.sh
windowsExecStepConfig:
interpreter: SHELL
allowedSuccessCodes:
- 0
- 2
localPath: C:\Users\user\pre-patch-script.cmd
postStep:
linuxExecStepConfig:
gcsObject:
bucket: my-patch-scripts
generationNumber: '1523477886880'
object: linux/post_patch_script
windowsExecStepConfig:
interpreter: POWERSHELL
gcsObject:
bucket: my-patch-scripts
generationNumber: '135920493447'
object: windows/post_patch_script.ps1
duration: 10s
recurringSchedule:
timeZone:
id: America/New_York
timeOfDay:
hours: 0
minutes: 30
seconds: 30
nanos: 20
monthly:
weekDayOfMonth:
weekOrdinal: -1
dayOfWeek: TUESDAY
dayOffset: 3
rollout:
mode: ZONE_BY_ZONE
disruptionBudget:
fixed: 1
The instanceFilter combines multiple criteria: groupLabels matches instances with specific label combinations, instanceNamePrefixes targets instances by name pattern, and zones restricts patching to specific locations. The preStep and postStep properties define scripts that run before and after patching, either from local paths or GCS objects. The rollout property controls how patches spread across instances: mode: “ZONE_BY_ZONE” patches one zone at a time, and disruptionBudget.fixed limits how many instances can be disrupted simultaneously.
Beyond these examples
These snippets focus on specific patch deployment features: one-time and recurring schedules, instance filtering and package exclusions, and pre/post execution hooks and rollout strategies. They’re intentionally minimal rather than full patch management solutions.
The examples may reference pre-existing infrastructure such as Compute Engine VM instances and GCS buckets for patch scripts (in advanced example). They focus on configuring the patch deployment rather than provisioning the underlying compute infrastructure.
To keep things focused, common patch deployment patterns are omitted, including:
- Weekly schedules (recurringSchedule.weekly)
- Percentage-based disruption budgets (rollout.disruptionBudget.percentage)
- Apt and Zypper configuration for non-RHEL systems
- Windows Update classifications beyond the full example
These omissions are intentional: the goal is to illustrate how each patch deployment feature is wired, not provide drop-in patch management modules. See the OS Config PatchDeployment resource reference for all available configuration options.
Let's configure GCP OS Config Patch Deployments
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration & Immutability
instanceFilter, patchConfig, oneTimeSchedule, recurringSchedule, rollout, duration, and description. To modify these, you must delete and recreate the deployment.patchDeploymentId must contain only lowercase letters, numbers, and hyphens; start with a letter; be 1-63 characters long; end with a letter or number; and be unique within the project.Scheduling
oneTimeSchedule with executeTime for a single execution, or recurringSchedule with timeZone and timeOfDay for repeated executions. These options are mutually exclusive.recurringSchedule.timeOfDay with hours: 0, minutes: 0, seconds: 0, and nanos: 0.recurringSchedule.monthly.monthDay for a specific date (e.g., monthDay: 1 for the first of each month), or weekDayOfMonth for patterns like “last Tuesday” with weekOrdinal, dayOfWeek, and optional dayOffset.Instance Targeting
Configure instanceFilter with one or more options:
all: true- All instances in the projectinstances- List of specific instance IDsgroupLabels- Instances matching label criteriazones- Instances in specific zonesinstanceNamePrefixes- Instances with matching name prefixes
Patch Configuration
patchConfig with OS-specific blocks: apt for Debian/Ubuntu, yum for RHEL/CentOS, zypper for SUSE, goo for Google-provided packages, and windowsUpdate for Windows. Each supports options like excludes, security-only updates, and classifications.patchConfig.preStep and patchConfig.postStep with linuxExecStepConfig or windowsExecStepConfig. Scripts can be local paths or GCS objects, with configurable success codes and interpreters.duration property with a value in seconds and up to nine fractional digits, terminated by ’s’ (e.g., "10s" or "3.5s"). After this duration, the patch times out.Rollout & Execution
rollout with a mode (e.g., "ZONE_BY_ZONE") and disruptionBudget to limit concurrent patches. The disruption budget can be fixed (number of instances) or percentage.