Configure GCP GKE Backup Restore Plans

The gcp:gkebackup/restorePlan:RestorePlan resource, part of the Pulumi GCP provider, defines restore plans that specify how GKE cluster backups are restored: which resources to include, how to handle conflicts, and what transformations to apply. This guide focuses on four capabilities: namespace and application selection strategies, resource transformation during restore, conflict resolution modes, and dependency ordering.

Restore plans reference existing BackupPlans and target GKE clusters that must have the Backup Agent addon enabled. The examples are intentionally small. Combine them with your own backup plans, cluster infrastructure, and operational workflows.

Restore all namespaces with conflict handling

Most restore operations recover all namespaces from a backup, providing complete cluster state restoration for disaster recovery.

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

const primary = new gcp.container.Cluster("primary", {
    name: "restore-all-ns-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "restore-all-ns",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const allNs = new gcp.gkebackup.RestorePlan("all_ns", {
    name: "restore-all-ns",
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        allNamespaces: true,
        namespacedResourceRestoreMode: "FAIL_ON_CONFLICT",
        volumeDataRestorePolicy: "RESTORE_VOLUME_DATA_FROM_BACKUP",
        clusterResourceRestoreScope: {
            allGroupKinds: true,
        },
        clusterResourceConflictPolicy: "USE_EXISTING_VERSION",
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="restore-all-ns-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="restore-all-ns",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
all_ns = gcp.gkebackup.RestorePlan("all_ns",
    name="restore-all-ns",
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "all_namespaces": True,
        "namespaced_resource_restore_mode": "FAIL_ON_CONFLICT",
        "volume_data_restore_policy": "RESTORE_VOLUME_DATA_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "all_group_kinds": True,
        },
        "cluster_resource_conflict_policy": "USE_EXISTING_VERSION",
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("restore-all-ns-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("restore-all-ns"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "all_ns", &gkebackup.RestorePlanArgs{
			Name:       pulumi.String("restore-all-ns"),
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				AllNamespaces:                 pulumi.Bool(true),
				NamespacedResourceRestoreMode: pulumi.String("FAIL_ON_CONFLICT"),
				VolumeDataRestorePolicy:       pulumi.String("RESTORE_VOLUME_DATA_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					AllGroupKinds: pulumi.Bool(true),
				},
				ClusterResourceConflictPolicy: pulumi.String("USE_EXISTING_VERSION"),
			},
		})
		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 primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "restore-all-ns-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "restore-all-ns",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var allNs = new Gcp.GkeBackup.RestorePlan("all_ns", new()
    {
        Name = "restore-all-ns",
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            AllNamespaces = true,
            NamespacedResourceRestoreMode = "FAIL_ON_CONFLICT",
            VolumeDataRestorePolicy = "RESTORE_VOLUME_DATA_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                AllGroupKinds = true,
            },
            ClusterResourceConflictPolicy = "USE_EXISTING_VERSION",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
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 primary = new Cluster("primary", ClusterArgs.builder()
            .name("restore-all-ns-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("restore-all-ns")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var allNs = new RestorePlan("allNs", RestorePlanArgs.builder()
            .name("restore-all-ns")
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .allNamespaces(true)
                .namespacedResourceRestoreMode("FAIL_ON_CONFLICT")
                .volumeDataRestorePolicy("RESTORE_VOLUME_DATA_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .allGroupKinds(true)
                    .build())
                .clusterResourceConflictPolicy("USE_EXISTING_VERSION")
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: restore-all-ns-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: restore-all-ns
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  allNs:
    type: gcp:gkebackup:RestorePlan
    name: all_ns
    properties:
      name: restore-all-ns
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        allNamespaces: true
        namespacedResourceRestoreMode: FAIL_ON_CONFLICT
        volumeDataRestorePolicy: RESTORE_VOLUME_DATA_FROM_BACKUP
        clusterResourceRestoreScope:
          allGroupKinds: true
        clusterResourceConflictPolicy: USE_EXISTING_VERSION

When restoreConfig.allNamespaces is true, the restore includes every namespace from the backup. The namespacedResourceRestoreMode controls what happens when resources already exist: FAIL_ON_CONFLICT stops the restore if conflicts are detected. The volumeDataRestorePolicy determines whether volume data is restored from backup or left empty. The clusterResourceRestoreScope.allGroupKinds includes all cluster-level resources like CustomResourceDefinitions and StorageClasses.

Roll back specific namespaces with selective resources

When applications fail, teams often restore specific namespaces while controlling which cluster-level resources are included.

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

const primary = new gcp.container.Cluster("primary", {
    name: "rollback-ns-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "rollback-ns",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const rollbackNs = new gcp.gkebackup.RestorePlan("rollback_ns", {
    name: "rollback-ns-rp",
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        selectedNamespaces: {
            namespaces: ["my-ns"],
        },
        namespacedResourceRestoreMode: "DELETE_AND_RESTORE",
        volumeDataRestorePolicy: "RESTORE_VOLUME_DATA_FROM_BACKUP",
        clusterResourceRestoreScope: {
            selectedGroupKinds: [
                {
                    resourceGroup: "apiextension.k8s.io",
                    resourceKind: "CustomResourceDefinition",
                },
                {
                    resourceGroup: "storage.k8s.io",
                    resourceKind: "StorageClass",
                },
            ],
        },
        clusterResourceConflictPolicy: "USE_EXISTING_VERSION",
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="rollback-ns-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="rollback-ns",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
rollback_ns = gcp.gkebackup.RestorePlan("rollback_ns",
    name="rollback-ns-rp",
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "selected_namespaces": {
            "namespaces": ["my-ns"],
        },
        "namespaced_resource_restore_mode": "DELETE_AND_RESTORE",
        "volume_data_restore_policy": "RESTORE_VOLUME_DATA_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "selected_group_kinds": [
                {
                    "resource_group": "apiextension.k8s.io",
                    "resource_kind": "CustomResourceDefinition",
                },
                {
                    "resource_group": "storage.k8s.io",
                    "resource_kind": "StorageClass",
                },
            ],
        },
        "cluster_resource_conflict_policy": "USE_EXISTING_VERSION",
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("rollback-ns-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("rollback-ns"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "rollback_ns", &gkebackup.RestorePlanArgs{
			Name:       pulumi.String("rollback-ns-rp"),
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				SelectedNamespaces: &gkebackup.RestorePlanRestoreConfigSelectedNamespacesArgs{
					Namespaces: pulumi.StringArray{
						pulumi.String("my-ns"),
					},
				},
				NamespacedResourceRestoreMode: pulumi.String("DELETE_AND_RESTORE"),
				VolumeDataRestorePolicy:       pulumi.String("RESTORE_VOLUME_DATA_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					SelectedGroupKinds: gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArray{
						&gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArgs{
							ResourceGroup: pulumi.String("apiextension.k8s.io"),
							ResourceKind:  pulumi.String("CustomResourceDefinition"),
						},
						&gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArgs{
							ResourceGroup: pulumi.String("storage.k8s.io"),
							ResourceKind:  pulumi.String("StorageClass"),
						},
					},
				},
				ClusterResourceConflictPolicy: pulumi.String("USE_EXISTING_VERSION"),
			},
		})
		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 primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "rollback-ns-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "rollback-ns",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var rollbackNs = new Gcp.GkeBackup.RestorePlan("rollback_ns", new()
    {
        Name = "rollback-ns-rp",
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            SelectedNamespaces = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigSelectedNamespacesArgs
            {
                Namespaces = new[]
                {
                    "my-ns",
                },
            },
            NamespacedResourceRestoreMode = "DELETE_AND_RESTORE",
            VolumeDataRestorePolicy = "RESTORE_VOLUME_DATA_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                SelectedGroupKinds = new[]
                {
                    new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArgs
                    {
                        ResourceGroup = "apiextension.k8s.io",
                        ResourceKind = "CustomResourceDefinition",
                    },
                    new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArgs
                    {
                        ResourceGroup = "storage.k8s.io",
                        ResourceKind = "StorageClass",
                    },
                },
            },
            ClusterResourceConflictPolicy = "USE_EXISTING_VERSION",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigSelectedNamespacesArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
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 primary = new Cluster("primary", ClusterArgs.builder()
            .name("rollback-ns-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("rollback-ns")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var rollbackNs = new RestorePlan("rollbackNs", RestorePlanArgs.builder()
            .name("rollback-ns-rp")
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .selectedNamespaces(RestorePlanRestoreConfigSelectedNamespacesArgs.builder()
                    .namespaces("my-ns")
                    .build())
                .namespacedResourceRestoreMode("DELETE_AND_RESTORE")
                .volumeDataRestorePolicy("RESTORE_VOLUME_DATA_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .selectedGroupKinds(                    
                        RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArgs.builder()
                            .resourceGroup("apiextension.k8s.io")
                            .resourceKind("CustomResourceDefinition")
                            .build(),
                        RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArgs.builder()
                            .resourceGroup("storage.k8s.io")
                            .resourceKind("StorageClass")
                            .build())
                    .build())
                .clusterResourceConflictPolicy("USE_EXISTING_VERSION")
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: rollback-ns-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: rollback-ns
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  rollbackNs:
    type: gcp:gkebackup:RestorePlan
    name: rollback_ns
    properties:
      name: rollback-ns-rp
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        selectedNamespaces:
          namespaces:
            - my-ns
        namespacedResourceRestoreMode: DELETE_AND_RESTORE
        volumeDataRestorePolicy: RESTORE_VOLUME_DATA_FROM_BACKUP
        clusterResourceRestoreScope:
          selectedGroupKinds:
            - resourceGroup: apiextension.k8s.io
              resourceKind: CustomResourceDefinition
            - resourceGroup: storage.k8s.io
              resourceKind: StorageClass
        clusterResourceConflictPolicy: USE_EXISTING_VERSION

The selectedNamespaces property limits the restore to specific namespaces by name. Setting namespacedResourceRestoreMode to DELETE_AND_RESTORE removes existing resources before restoring, ensuring a clean state. The selectedGroupKinds array specifies which cluster-level resource types to restore, allowing you to include only CustomResourceDefinitions and StorageClasses while excluding others.

Restore individual applications by name

Application-level restores recover specific workloads without affecting other applications in the cluster.

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

const primary = new gcp.container.Cluster("primary", {
    name: "rollback-app-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "rollback-app",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const rollbackApp = new gcp.gkebackup.RestorePlan("rollback_app", {
    name: "rollback-app-rp",
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        selectedApplications: {
            namespacedNames: [{
                name: "my-app",
                namespace: "my-ns",
            }],
        },
        namespacedResourceRestoreMode: "DELETE_AND_RESTORE",
        volumeDataRestorePolicy: "REUSE_VOLUME_HANDLE_FROM_BACKUP",
        clusterResourceRestoreScope: {
            noGroupKinds: true,
        },
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="rollback-app-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="rollback-app",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
rollback_app = gcp.gkebackup.RestorePlan("rollback_app",
    name="rollback-app-rp",
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "selected_applications": {
            "namespaced_names": [{
                "name": "my-app",
                "namespace": "my-ns",
            }],
        },
        "namespaced_resource_restore_mode": "DELETE_AND_RESTORE",
        "volume_data_restore_policy": "REUSE_VOLUME_HANDLE_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "no_group_kinds": True,
        },
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("rollback-app-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("rollback-app"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "rollback_app", &gkebackup.RestorePlanArgs{
			Name:       pulumi.String("rollback-app-rp"),
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				SelectedApplications: &gkebackup.RestorePlanRestoreConfigSelectedApplicationsArgs{
					NamespacedNames: gkebackup.RestorePlanRestoreConfigSelectedApplicationsNamespacedNameArray{
						&gkebackup.RestorePlanRestoreConfigSelectedApplicationsNamespacedNameArgs{
							Name:      pulumi.String("my-app"),
							Namespace: pulumi.String("my-ns"),
						},
					},
				},
				NamespacedResourceRestoreMode: pulumi.String("DELETE_AND_RESTORE"),
				VolumeDataRestorePolicy:       pulumi.String("REUSE_VOLUME_HANDLE_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					NoGroupKinds: pulumi.Bool(true),
				},
			},
		})
		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 primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "rollback-app-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "rollback-app",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var rollbackApp = new Gcp.GkeBackup.RestorePlan("rollback_app", new()
    {
        Name = "rollback-app-rp",
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            SelectedApplications = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigSelectedApplicationsArgs
            {
                NamespacedNames = new[]
                {
                    new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigSelectedApplicationsNamespacedNameArgs
                    {
                        Name = "my-app",
                        Namespace = "my-ns",
                    },
                },
            },
            NamespacedResourceRestoreMode = "DELETE_AND_RESTORE",
            VolumeDataRestorePolicy = "REUSE_VOLUME_HANDLE_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                NoGroupKinds = true,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigSelectedApplicationsArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
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 primary = new Cluster("primary", ClusterArgs.builder()
            .name("rollback-app-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("rollback-app")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var rollbackApp = new RestorePlan("rollbackApp", RestorePlanArgs.builder()
            .name("rollback-app-rp")
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .selectedApplications(RestorePlanRestoreConfigSelectedApplicationsArgs.builder()
                    .namespacedNames(RestorePlanRestoreConfigSelectedApplicationsNamespacedNameArgs.builder()
                        .name("my-app")
                        .namespace("my-ns")
                        .build())
                    .build())
                .namespacedResourceRestoreMode("DELETE_AND_RESTORE")
                .volumeDataRestorePolicy("REUSE_VOLUME_HANDLE_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .noGroupKinds(true)
                    .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: rollback-app-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: rollback-app
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  rollbackApp:
    type: gcp:gkebackup:RestorePlan
    name: rollback_app
    properties:
      name: rollback-app-rp
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        selectedApplications:
          namespacedNames:
            - name: my-app
              namespace: my-ns
        namespacedResourceRestoreMode: DELETE_AND_RESTORE
        volumeDataRestorePolicy: REUSE_VOLUME_HANDLE_FROM_BACKUP
        clusterResourceRestoreScope:
          noGroupKinds: true

The selectedApplications property identifies applications by name and namespace. Setting volumeDataRestorePolicy to REUSE_VOLUME_HANDLE_FROM_BACKUP preserves volume handles from the backup, useful when volumes still exist. The noGroupKinds option excludes all cluster-level resources, focusing the restore on application-specific resources only.

Transform namespace names during restore

Teams migrating workloads or testing disaster recovery often restore resources into different namespaces to avoid conflicts.

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

const primary = new gcp.container.Cluster("primary", {
    name: "rename-ns-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "rename-ns",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const renameNs = new gcp.gkebackup.RestorePlan("rename_ns", {
    name: "rename-ns-rp",
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        selectedNamespaces: {
            namespaces: ["ns1"],
        },
        namespacedResourceRestoreMode: "FAIL_ON_CONFLICT",
        volumeDataRestorePolicy: "REUSE_VOLUME_HANDLE_FROM_BACKUP",
        clusterResourceRestoreScope: {
            noGroupKinds: true,
        },
        transformationRules: [
            {
                description: "rename namespace from ns1 to ns2",
                resourceFilter: {
                    groupKinds: [{
                        resourceKind: "Namespace",
                    }],
                    jsonPath: ".metadata[?(@.name == 'ns1')]",
                },
                fieldActions: [{
                    op: "REPLACE",
                    path: "/metadata/name",
                    value: "ns2",
                }],
            },
            {
                description: "move all resources from ns1 to ns2",
                resourceFilter: {
                    namespaces: ["ns1"],
                },
                fieldActions: [{
                    op: "REPLACE",
                    path: "/metadata/namespace",
                    value: "ns2",
                }],
            },
        ],
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="rename-ns-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="rename-ns",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
rename_ns = gcp.gkebackup.RestorePlan("rename_ns",
    name="rename-ns-rp",
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "selected_namespaces": {
            "namespaces": ["ns1"],
        },
        "namespaced_resource_restore_mode": "FAIL_ON_CONFLICT",
        "volume_data_restore_policy": "REUSE_VOLUME_HANDLE_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "no_group_kinds": True,
        },
        "transformation_rules": [
            {
                "description": "rename namespace from ns1 to ns2",
                "resource_filter": {
                    "group_kinds": [{
                        "resource_kind": "Namespace",
                    }],
                    "json_path": ".metadata[?(@.name == 'ns1')]",
                },
                "field_actions": [{
                    "op": "REPLACE",
                    "path": "/metadata/name",
                    "value": "ns2",
                }],
            },
            {
                "description": "move all resources from ns1 to ns2",
                "resource_filter": {
                    "namespaces": ["ns1"],
                },
                "field_actions": [{
                    "op": "REPLACE",
                    "path": "/metadata/namespace",
                    "value": "ns2",
                }],
            },
        ],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("rename-ns-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("rename-ns"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "rename_ns", &gkebackup.RestorePlanArgs{
			Name:       pulumi.String("rename-ns-rp"),
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				SelectedNamespaces: &gkebackup.RestorePlanRestoreConfigSelectedNamespacesArgs{
					Namespaces: pulumi.StringArray{
						pulumi.String("ns1"),
					},
				},
				NamespacedResourceRestoreMode: pulumi.String("FAIL_ON_CONFLICT"),
				VolumeDataRestorePolicy:       pulumi.String("REUSE_VOLUME_HANDLE_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					NoGroupKinds: pulumi.Bool(true),
				},
				TransformationRules: gkebackup.RestorePlanRestoreConfigTransformationRuleArray{
					&gkebackup.RestorePlanRestoreConfigTransformationRuleArgs{
						Description: pulumi.String("rename namespace from ns1 to ns2"),
						ResourceFilter: &gkebackup.RestorePlanRestoreConfigTransformationRuleResourceFilterArgs{
							GroupKinds: gkebackup.RestorePlanRestoreConfigTransformationRuleResourceFilterGroupKindArray{
								&gkebackup.RestorePlanRestoreConfigTransformationRuleResourceFilterGroupKindArgs{
									ResourceKind: pulumi.String("Namespace"),
								},
							},
							JsonPath: pulumi.String(".metadata[?(@.name == 'ns1')]"),
						},
						FieldActions: gkebackup.RestorePlanRestoreConfigTransformationRuleFieldActionArray{
							&gkebackup.RestorePlanRestoreConfigTransformationRuleFieldActionArgs{
								Op:    pulumi.String("REPLACE"),
								Path:  pulumi.String("/metadata/name"),
								Value: pulumi.String("ns2"),
							},
						},
					},
					&gkebackup.RestorePlanRestoreConfigTransformationRuleArgs{
						Description: pulumi.String("move all resources from ns1 to ns2"),
						ResourceFilter: &gkebackup.RestorePlanRestoreConfigTransformationRuleResourceFilterArgs{
							Namespaces: pulumi.StringArray{
								pulumi.String("ns1"),
							},
						},
						FieldActions: gkebackup.RestorePlanRestoreConfigTransformationRuleFieldActionArray{
							&gkebackup.RestorePlanRestoreConfigTransformationRuleFieldActionArgs{
								Op:    pulumi.String("REPLACE"),
								Path:  pulumi.String("/metadata/namespace"),
								Value: pulumi.String("ns2"),
							},
						},
					},
				},
			},
		})
		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 primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "rename-ns-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "rename-ns",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var renameNs = new Gcp.GkeBackup.RestorePlan("rename_ns", new()
    {
        Name = "rename-ns-rp",
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            SelectedNamespaces = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigSelectedNamespacesArgs
            {
                Namespaces = new[]
                {
                    "ns1",
                },
            },
            NamespacedResourceRestoreMode = "FAIL_ON_CONFLICT",
            VolumeDataRestorePolicy = "REUSE_VOLUME_HANDLE_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                NoGroupKinds = true,
            },
            TransformationRules = new[]
            {
                new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleArgs
                {
                    Description = "rename namespace from ns1 to ns2",
                    ResourceFilter = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleResourceFilterArgs
                    {
                        GroupKinds = new[]
                        {
                            new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleResourceFilterGroupKindArgs
                            {
                                ResourceKind = "Namespace",
                            },
                        },
                        JsonPath = ".metadata[?(@.name == 'ns1')]",
                    },
                    FieldActions = new[]
                    {
                        new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleFieldActionArgs
                        {
                            Op = "REPLACE",
                            Path = "/metadata/name",
                            Value = "ns2",
                        },
                    },
                },
                new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleArgs
                {
                    Description = "move all resources from ns1 to ns2",
                    ResourceFilter = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleResourceFilterArgs
                    {
                        Namespaces = new[]
                        {
                            "ns1",
                        },
                    },
                    FieldActions = new[]
                    {
                        new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleFieldActionArgs
                        {
                            Op = "REPLACE",
                            Path = "/metadata/namespace",
                            Value = "ns2",
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigSelectedNamespacesArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
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 primary = new Cluster("primary", ClusterArgs.builder()
            .name("rename-ns-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("rename-ns")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var renameNs = new RestorePlan("renameNs", RestorePlanArgs.builder()
            .name("rename-ns-rp")
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .selectedNamespaces(RestorePlanRestoreConfigSelectedNamespacesArgs.builder()
                    .namespaces("ns1")
                    .build())
                .namespacedResourceRestoreMode("FAIL_ON_CONFLICT")
                .volumeDataRestorePolicy("REUSE_VOLUME_HANDLE_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .noGroupKinds(true)
                    .build())
                .transformationRules(                
                    RestorePlanRestoreConfigTransformationRuleArgs.builder()
                        .description("rename namespace from ns1 to ns2")
                        .resourceFilter(RestorePlanRestoreConfigTransformationRuleResourceFilterArgs.builder()
                            .groupKinds(RestorePlanRestoreConfigTransformationRuleResourceFilterGroupKindArgs.builder()
                                .resourceKind("Namespace")
                                .build())
                            .jsonPath(".metadata[?(@.name == 'ns1')]")
                            .build())
                        .fieldActions(RestorePlanRestoreConfigTransformationRuleFieldActionArgs.builder()
                            .op("REPLACE")
                            .path("/metadata/name")
                            .value("ns2")
                            .build())
                        .build(),
                    RestorePlanRestoreConfigTransformationRuleArgs.builder()
                        .description("move all resources from ns1 to ns2")
                        .resourceFilter(RestorePlanRestoreConfigTransformationRuleResourceFilterArgs.builder()
                            .namespaces("ns1")
                            .build())
                        .fieldActions(RestorePlanRestoreConfigTransformationRuleFieldActionArgs.builder()
                            .op("REPLACE")
                            .path("/metadata/namespace")
                            .value("ns2")
                            .build())
                        .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: rename-ns-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: rename-ns
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  renameNs:
    type: gcp:gkebackup:RestorePlan
    name: rename_ns
    properties:
      name: rename-ns-rp
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        selectedNamespaces:
          namespaces:
            - ns1
        namespacedResourceRestoreMode: FAIL_ON_CONFLICT
        volumeDataRestorePolicy: REUSE_VOLUME_HANDLE_FROM_BACKUP
        clusterResourceRestoreScope:
          noGroupKinds: true
        transformationRules:
          - description: rename namespace from ns1 to ns2
            resourceFilter:
              groupKinds:
                - resourceKind: Namespace
              jsonPath: .metadata[?(@.name == 'ns1')]
            fieldActions:
              - op: REPLACE
                path: /metadata/name
                value: ns2
          - description: move all resources from ns1 to ns2
            resourceFilter:
              namespaces:
                - ns1
            fieldActions:
              - op: REPLACE
                path: /metadata/namespace
                value: ns2

Transformation rules modify resources during restore. The resourceFilter identifies which resources to transform using groupKinds and jsonPath queries. The fieldActions array defines modifications: REPLACE operations change field values. Here, two rules work together: the first renames the namespace resource itself from ns1 to ns2, and the second moves all resources from ns1 into ns2 by updating their metadata.namespace field.

Copy configuration between containers during restore

Complex applications sometimes require configuration adjustments during restore, such as copying environment variables between containers.

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

const primary = new gcp.container.Cluster("primary", {
    name: "transform-rule-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "transform-rule",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const transformRule = new gcp.gkebackup.RestorePlan("transform_rule", {
    name: "transform-rule-rp",
    description: "copy nginx env variables",
    labels: {
        app: "nginx",
    },
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        excludedNamespaces: {
            namespaces: ["my-ns"],
        },
        namespacedResourceRestoreMode: "DELETE_AND_RESTORE",
        volumeDataRestorePolicy: "RESTORE_VOLUME_DATA_FROM_BACKUP",
        clusterResourceRestoreScope: {
            excludedGroupKinds: [{
                resourceGroup: "apiextension.k8s.io",
                resourceKind: "CustomResourceDefinition",
            }],
        },
        clusterResourceConflictPolicy: "USE_EXISTING_VERSION",
        transformationRules: [{
            description: "Copy environment variables from the nginx container to the install init container.",
            resourceFilter: {
                groupKinds: [{
                    resourceKind: "Pod",
                    resourceGroup: "",
                }],
                jsonPath: ".metadata[?(@.name == 'nginx')]",
            },
            fieldActions: [{
                op: "COPY",
                path: "/spec/initContainers/0/env",
                fromPath: "/spec/containers/0/env",
            }],
        }],
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="transform-rule-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="transform-rule",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
transform_rule = gcp.gkebackup.RestorePlan("transform_rule",
    name="transform-rule-rp",
    description="copy nginx env variables",
    labels={
        "app": "nginx",
    },
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "excluded_namespaces": {
            "namespaces": ["my-ns"],
        },
        "namespaced_resource_restore_mode": "DELETE_AND_RESTORE",
        "volume_data_restore_policy": "RESTORE_VOLUME_DATA_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "excluded_group_kinds": [{
                "resource_group": "apiextension.k8s.io",
                "resource_kind": "CustomResourceDefinition",
            }],
        },
        "cluster_resource_conflict_policy": "USE_EXISTING_VERSION",
        "transformation_rules": [{
            "description": "Copy environment variables from the nginx container to the install init container.",
            "resource_filter": {
                "group_kinds": [{
                    "resource_kind": "Pod",
                    "resource_group": "",
                }],
                "json_path": ".metadata[?(@.name == 'nginx')]",
            },
            "field_actions": [{
                "op": "COPY",
                "path": "/spec/initContainers/0/env",
                "from_path": "/spec/containers/0/env",
            }],
        }],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("transform-rule-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("transform-rule"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "transform_rule", &gkebackup.RestorePlanArgs{
			Name:        pulumi.String("transform-rule-rp"),
			Description: pulumi.String("copy nginx env variables"),
			Labels: pulumi.StringMap{
				"app": pulumi.String("nginx"),
			},
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				ExcludedNamespaces: &gkebackup.RestorePlanRestoreConfigExcludedNamespacesArgs{
					Namespaces: pulumi.StringArray{
						pulumi.String("my-ns"),
					},
				},
				NamespacedResourceRestoreMode: pulumi.String("DELETE_AND_RESTORE"),
				VolumeDataRestorePolicy:       pulumi.String("RESTORE_VOLUME_DATA_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					ExcludedGroupKinds: gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeExcludedGroupKindArray{
						&gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeExcludedGroupKindArgs{
							ResourceGroup: pulumi.String("apiextension.k8s.io"),
							ResourceKind:  pulumi.String("CustomResourceDefinition"),
						},
					},
				},
				ClusterResourceConflictPolicy: pulumi.String("USE_EXISTING_VERSION"),
				TransformationRules: gkebackup.RestorePlanRestoreConfigTransformationRuleArray{
					&gkebackup.RestorePlanRestoreConfigTransformationRuleArgs{
						Description: pulumi.String("Copy environment variables from the nginx container to the install init container."),
						ResourceFilter: &gkebackup.RestorePlanRestoreConfigTransformationRuleResourceFilterArgs{
							GroupKinds: gkebackup.RestorePlanRestoreConfigTransformationRuleResourceFilterGroupKindArray{
								&gkebackup.RestorePlanRestoreConfigTransformationRuleResourceFilterGroupKindArgs{
									ResourceKind:  pulumi.String("Pod"),
									ResourceGroup: pulumi.String(""),
								},
							},
							JsonPath: pulumi.String(".metadata[?(@.name == 'nginx')]"),
						},
						FieldActions: gkebackup.RestorePlanRestoreConfigTransformationRuleFieldActionArray{
							&gkebackup.RestorePlanRestoreConfigTransformationRuleFieldActionArgs{
								Op:       pulumi.String("COPY"),
								Path:     pulumi.String("/spec/initContainers/0/env"),
								FromPath: pulumi.String("/spec/containers/0/env"),
							},
						},
					},
				},
			},
		})
		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 primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "transform-rule-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "transform-rule",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var transformRule = new Gcp.GkeBackup.RestorePlan("transform_rule", new()
    {
        Name = "transform-rule-rp",
        Description = "copy nginx env variables",
        Labels = 
        {
            { "app", "nginx" },
        },
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            ExcludedNamespaces = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigExcludedNamespacesArgs
            {
                Namespaces = new[]
                {
                    "my-ns",
                },
            },
            NamespacedResourceRestoreMode = "DELETE_AND_RESTORE",
            VolumeDataRestorePolicy = "RESTORE_VOLUME_DATA_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                ExcludedGroupKinds = new[]
                {
                    new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeExcludedGroupKindArgs
                    {
                        ResourceGroup = "apiextension.k8s.io",
                        ResourceKind = "CustomResourceDefinition",
                    },
                },
            },
            ClusterResourceConflictPolicy = "USE_EXISTING_VERSION",
            TransformationRules = new[]
            {
                new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleArgs
                {
                    Description = "Copy environment variables from the nginx container to the install init container.",
                    ResourceFilter = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleResourceFilterArgs
                    {
                        GroupKinds = new[]
                        {
                            new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleResourceFilterGroupKindArgs
                            {
                                ResourceKind = "Pod",
                                ResourceGroup = "",
                            },
                        },
                        JsonPath = ".metadata[?(@.name == 'nginx')]",
                    },
                    FieldActions = new[]
                    {
                        new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleFieldActionArgs
                        {
                            Op = "COPY",
                            Path = "/spec/initContainers/0/env",
                            FromPath = "/spec/containers/0/env",
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigExcludedNamespacesArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
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 primary = new Cluster("primary", ClusterArgs.builder()
            .name("transform-rule-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("transform-rule")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var transformRule = new RestorePlan("transformRule", RestorePlanArgs.builder()
            .name("transform-rule-rp")
            .description("copy nginx env variables")
            .labels(Map.of("app", "nginx"))
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .excludedNamespaces(RestorePlanRestoreConfigExcludedNamespacesArgs.builder()
                    .namespaces("my-ns")
                    .build())
                .namespacedResourceRestoreMode("DELETE_AND_RESTORE")
                .volumeDataRestorePolicy("RESTORE_VOLUME_DATA_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .excludedGroupKinds(RestorePlanRestoreConfigClusterResourceRestoreScopeExcludedGroupKindArgs.builder()
                        .resourceGroup("apiextension.k8s.io")
                        .resourceKind("CustomResourceDefinition")
                        .build())
                    .build())
                .clusterResourceConflictPolicy("USE_EXISTING_VERSION")
                .transformationRules(RestorePlanRestoreConfigTransformationRuleArgs.builder()
                    .description("Copy environment variables from the nginx container to the install init container.")
                    .resourceFilter(RestorePlanRestoreConfigTransformationRuleResourceFilterArgs.builder()
                        .groupKinds(RestorePlanRestoreConfigTransformationRuleResourceFilterGroupKindArgs.builder()
                            .resourceKind("Pod")
                            .resourceGroup("")
                            .build())
                        .jsonPath(".metadata[?(@.name == 'nginx')]")
                        .build())
                    .fieldActions(RestorePlanRestoreConfigTransformationRuleFieldActionArgs.builder()
                        .op("COPY")
                        .path("/spec/initContainers/0/env")
                        .fromPath("/spec/containers/0/env")
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: transform-rule-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: transform-rule
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  transformRule:
    type: gcp:gkebackup:RestorePlan
    name: transform_rule
    properties:
      name: transform-rule-rp
      description: copy nginx env variables
      labels:
        app: nginx
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        excludedNamespaces:
          namespaces:
            - my-ns
        namespacedResourceRestoreMode: DELETE_AND_RESTORE
        volumeDataRestorePolicy: RESTORE_VOLUME_DATA_FROM_BACKUP
        clusterResourceRestoreScope:
          excludedGroupKinds:
            - resourceGroup: apiextension.k8s.io
              resourceKind: CustomResourceDefinition
        clusterResourceConflictPolicy: USE_EXISTING_VERSION
        transformationRules:
          - description: Copy environment variables from the nginx container to the install init container.
            resourceFilter:
              groupKinds:
                - resourceKind: Pod
                  resourceGroup: ""
              jsonPath: .metadata[?(@.name == 'nginx')]
            fieldActions:
              - op: COPY
                path: /spec/initContainers/0/env
                fromPath: /spec/containers/0/env

The COPY operation in fieldActions copies values from one path to another within the same resource. The fromPath specifies the source location, and path specifies the destination. This example copies environment variables from the nginx container to an init container. The excludedNamespaces property prevents specific namespaces from being restored, useful when you want to restore most of the cluster but skip certain areas.

Merge resources without overwriting existing state

GitOps workflows need to restore resources while preserving changes made by continuous deployment systems.

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

const primary = new gcp.container.Cluster("primary", {
    name: "gitops-mode-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "gitops-mode",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const gitopsMode = new gcp.gkebackup.RestorePlan("gitops_mode", {
    name: "gitops-mode",
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        allNamespaces: true,
        namespacedResourceRestoreMode: "MERGE_SKIP_ON_CONFLICT",
        volumeDataRestorePolicy: "RESTORE_VOLUME_DATA_FROM_BACKUP",
        clusterResourceRestoreScope: {
            allGroupKinds: true,
        },
        clusterResourceConflictPolicy: "USE_EXISTING_VERSION",
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="gitops-mode-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="gitops-mode",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
gitops_mode = gcp.gkebackup.RestorePlan("gitops_mode",
    name="gitops-mode",
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "all_namespaces": True,
        "namespaced_resource_restore_mode": "MERGE_SKIP_ON_CONFLICT",
        "volume_data_restore_policy": "RESTORE_VOLUME_DATA_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "all_group_kinds": True,
        },
        "cluster_resource_conflict_policy": "USE_EXISTING_VERSION",
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("gitops-mode-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("gitops-mode"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "gitops_mode", &gkebackup.RestorePlanArgs{
			Name:       pulumi.String("gitops-mode"),
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				AllNamespaces:                 pulumi.Bool(true),
				NamespacedResourceRestoreMode: pulumi.String("MERGE_SKIP_ON_CONFLICT"),
				VolumeDataRestorePolicy:       pulumi.String("RESTORE_VOLUME_DATA_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					AllGroupKinds: pulumi.Bool(true),
				},
				ClusterResourceConflictPolicy: pulumi.String("USE_EXISTING_VERSION"),
			},
		})
		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 primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "gitops-mode-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "gitops-mode",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var gitopsMode = new Gcp.GkeBackup.RestorePlan("gitops_mode", new()
    {
        Name = "gitops-mode",
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            AllNamespaces = true,
            NamespacedResourceRestoreMode = "MERGE_SKIP_ON_CONFLICT",
            VolumeDataRestorePolicy = "RESTORE_VOLUME_DATA_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                AllGroupKinds = true,
            },
            ClusterResourceConflictPolicy = "USE_EXISTING_VERSION",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
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 primary = new Cluster("primary", ClusterArgs.builder()
            .name("gitops-mode-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("gitops-mode")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var gitopsMode = new RestorePlan("gitopsMode", RestorePlanArgs.builder()
            .name("gitops-mode")
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .allNamespaces(true)
                .namespacedResourceRestoreMode("MERGE_SKIP_ON_CONFLICT")
                .volumeDataRestorePolicy("RESTORE_VOLUME_DATA_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .allGroupKinds(true)
                    .build())
                .clusterResourceConflictPolicy("USE_EXISTING_VERSION")
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: gitops-mode-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: gitops-mode
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  gitopsMode:
    type: gcp:gkebackup:RestorePlan
    name: gitops_mode
    properties:
      name: gitops-mode
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        allNamespaces: true
        namespacedResourceRestoreMode: MERGE_SKIP_ON_CONFLICT
        volumeDataRestorePolicy: RESTORE_VOLUME_DATA_FROM_BACKUP
        clusterResourceRestoreScope:
          allGroupKinds: true
        clusterResourceConflictPolicy: USE_EXISTING_VERSION

Setting namespacedResourceRestoreMode to MERGE_SKIP_ON_CONFLICT merges restored resources with existing ones, skipping resources that would conflict. This allows GitOps controllers to continue managing resources without the restore overwriting their changes. The restore applies only non-conflicting resources, making it safe to run alongside active deployment pipelines.

Control resource restoration order with dependencies

Applications with complex dependencies require specific restoration order to ensure prerequisites exist before dependent resources are created.

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

const primary = new gcp.container.Cluster("primary", {
    name: "restore-order-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "restore-order",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const restoreOrder = new gcp.gkebackup.RestorePlan("restore_order", {
    name: "restore-order",
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        allNamespaces: true,
        namespacedResourceRestoreMode: "FAIL_ON_CONFLICT",
        volumeDataRestorePolicy: "RESTORE_VOLUME_DATA_FROM_BACKUP",
        clusterResourceRestoreScope: {
            allGroupKinds: true,
        },
        clusterResourceConflictPolicy: "USE_EXISTING_VERSION",
        restoreOrder: {
            groupKindDependencies: [
                {
                    satisfying: {
                        resourceGroup: "stable.example.com",
                        resourceKind: "kindA",
                    },
                    requiring: {
                        resourceGroup: "stable.example.com",
                        resourceKind: "kindB",
                    },
                },
                {
                    satisfying: {
                        resourceGroup: "stable.example.com",
                        resourceKind: "kindB",
                    },
                    requiring: {
                        resourceGroup: "stable.example.com",
                        resourceKind: "kindC",
                    },
                },
            ],
        },
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="restore-order-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="restore-order",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
restore_order = gcp.gkebackup.RestorePlan("restore_order",
    name="restore-order",
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "all_namespaces": True,
        "namespaced_resource_restore_mode": "FAIL_ON_CONFLICT",
        "volume_data_restore_policy": "RESTORE_VOLUME_DATA_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "all_group_kinds": True,
        },
        "cluster_resource_conflict_policy": "USE_EXISTING_VERSION",
        "restore_order": {
            "group_kind_dependencies": [
                {
                    "satisfying": {
                        "resource_group": "stable.example.com",
                        "resource_kind": "kindA",
                    },
                    "requiring": {
                        "resource_group": "stable.example.com",
                        "resource_kind": "kindB",
                    },
                },
                {
                    "satisfying": {
                        "resource_group": "stable.example.com",
                        "resource_kind": "kindB",
                    },
                    "requiring": {
                        "resource_group": "stable.example.com",
                        "resource_kind": "kindC",
                    },
                },
            ],
        },
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("restore-order-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("restore-order"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "restore_order", &gkebackup.RestorePlanArgs{
			Name:       pulumi.String("restore-order"),
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				AllNamespaces:                 pulumi.Bool(true),
				NamespacedResourceRestoreMode: pulumi.String("FAIL_ON_CONFLICT"),
				VolumeDataRestorePolicy:       pulumi.String("RESTORE_VOLUME_DATA_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					AllGroupKinds: pulumi.Bool(true),
				},
				ClusterResourceConflictPolicy: pulumi.String("USE_EXISTING_VERSION"),
				RestoreOrder: &gkebackup.RestorePlanRestoreConfigRestoreOrderArgs{
					GroupKindDependencies: gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArray{
						&gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArgs{
							Satisfying: &gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencySatisfyingArgs{
								ResourceGroup: pulumi.String("stable.example.com"),
								ResourceKind:  pulumi.String("kindA"),
							},
							Requiring: &gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyRequiringArgs{
								ResourceGroup: pulumi.String("stable.example.com"),
								ResourceKind:  pulumi.String("kindB"),
							},
						},
						&gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArgs{
							Satisfying: &gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencySatisfyingArgs{
								ResourceGroup: pulumi.String("stable.example.com"),
								ResourceKind:  pulumi.String("kindB"),
							},
							Requiring: &gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyRequiringArgs{
								ResourceGroup: pulumi.String("stable.example.com"),
								ResourceKind:  pulumi.String("kindC"),
							},
						},
					},
				},
			},
		})
		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 primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "restore-order-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "restore-order",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var restoreOrder = new Gcp.GkeBackup.RestorePlan("restore_order", new()
    {
        Name = "restore-order",
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            AllNamespaces = true,
            NamespacedResourceRestoreMode = "FAIL_ON_CONFLICT",
            VolumeDataRestorePolicy = "RESTORE_VOLUME_DATA_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                AllGroupKinds = true,
            },
            ClusterResourceConflictPolicy = "USE_EXISTING_VERSION",
            RestoreOrder = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderArgs
            {
                GroupKindDependencies = new[]
                {
                    new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArgs
                    {
                        Satisfying = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderGroupKindDependencySatisfyingArgs
                        {
                            ResourceGroup = "stable.example.com",
                            ResourceKind = "kindA",
                        },
                        Requiring = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyRequiringArgs
                        {
                            ResourceGroup = "stable.example.com",
                            ResourceKind = "kindB",
                        },
                    },
                    new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArgs
                    {
                        Satisfying = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderGroupKindDependencySatisfyingArgs
                        {
                            ResourceGroup = "stable.example.com",
                            ResourceKind = "kindB",
                        },
                        Requiring = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyRequiringArgs
                        {
                            ResourceGroup = "stable.example.com",
                            ResourceKind = "kindC",
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigRestoreOrderArgs;
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 primary = new Cluster("primary", ClusterArgs.builder()
            .name("restore-order-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("restore-order")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var restoreOrder = new RestorePlan("restoreOrder", RestorePlanArgs.builder()
            .name("restore-order")
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .allNamespaces(true)
                .namespacedResourceRestoreMode("FAIL_ON_CONFLICT")
                .volumeDataRestorePolicy("RESTORE_VOLUME_DATA_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .allGroupKinds(true)
                    .build())
                .clusterResourceConflictPolicy("USE_EXISTING_VERSION")
                .restoreOrder(RestorePlanRestoreConfigRestoreOrderArgs.builder()
                    .groupKindDependencies(                    
                        RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArgs.builder()
                            .satisfying(RestorePlanRestoreConfigRestoreOrderGroupKindDependencySatisfyingArgs.builder()
                                .resourceGroup("stable.example.com")
                                .resourceKind("kindA")
                                .build())
                            .requiring(RestorePlanRestoreConfigRestoreOrderGroupKindDependencyRequiringArgs.builder()
                                .resourceGroup("stable.example.com")
                                .resourceKind("kindB")
                                .build())
                            .build(),
                        RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArgs.builder()
                            .satisfying(RestorePlanRestoreConfigRestoreOrderGroupKindDependencySatisfyingArgs.builder()
                                .resourceGroup("stable.example.com")
                                .resourceKind("kindB")
                                .build())
                            .requiring(RestorePlanRestoreConfigRestoreOrderGroupKindDependencyRequiringArgs.builder()
                                .resourceGroup("stable.example.com")
                                .resourceKind("kindC")
                                .build())
                            .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: restore-order-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: restore-order
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  restoreOrder:
    type: gcp:gkebackup:RestorePlan
    name: restore_order
    properties:
      name: restore-order
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        allNamespaces: true
        namespacedResourceRestoreMode: FAIL_ON_CONFLICT
        volumeDataRestorePolicy: RESTORE_VOLUME_DATA_FROM_BACKUP
        clusterResourceRestoreScope:
          allGroupKinds: true
        clusterResourceConflictPolicy: USE_EXISTING_VERSION
        restoreOrder:
          groupKindDependencies:
            - satisfying:
                resourceGroup: stable.example.com
                resourceKind: kindA
              requiring:
                resourceGroup: stable.example.com
                resourceKind: kindB
            - satisfying:
                resourceGroup: stable.example.com
                resourceKind: kindB
              requiring:
                resourceGroup: stable.example.com
                resourceKind: kindC

The restoreOrder property defines dependency chains through groupKindDependencies. Each dependency specifies a satisfying resource type that must be restored before the requiring resource type. Here, kindA must be restored before kindB, and kindB before kindC. This ensures resources are created in the correct order, preventing failures from missing dependencies.

Beyond these examples

These snippets focus on specific restore plan features: namespace and application selection, resource transformation and field manipulation, conflict resolution and merge strategies, and dependency ordering and volume handling. They’re intentionally minimal rather than full disaster recovery solutions.

The examples reference pre-existing infrastructure such as GKE clusters with Backup Agent addon enabled, BackupPlans with completed backups, and VPC networks and subnets for cluster connectivity. They focus on configuring the restore plan rather than provisioning the surrounding infrastructure.

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

  • Backup scheduling and retention policies (BackupPlan concern)
  • IAM permissions and service account configuration
  • Cluster-level encryption and security settings
  • Cross-project or cross-region restore scenarios
  • Restore validation and testing workflows
  • Monitoring and alerting for restore operations

These omissions are intentional: the goal is to illustrate how each restore plan feature is wired, not provide drop-in disaster recovery modules. See the RestorePlan resource reference for all available configuration options.

Let's configure GCP GKE Backup Restore Plans

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & Immutability
What properties can't I change after creating a restore plan?
The backupPlan, cluster, location, name, and project properties are immutable. Changing any of these requires recreating the RestorePlan.
Why don't I see all labels in my configuration?
The labels field is non-authoritative and only manages labels defined in your configuration. Use the effectiveLabels output property to see all labels present on the resource in GCP.
Namespace & Application Selection
What are my options for selecting what to restore?

You can restore using:

  1. All namespaces - Set allNamespaces: true
  2. Specific namespaces - Use selectedNamespaces with a list of namespace names
  3. Exclude namespaces - Use excludedNamespaces to restore all except specified ones
  4. Single application - Use selectedApplications with app name and namespace
  5. Cluster resources only - Set noNamespaces: true
How do I restore only specific namespaces?
Configure selectedNamespaces in restoreConfig with a list of namespace names you want to restore.
How do I restore a single application instead of entire namespaces?
Use selectedApplications with namespacedNames specifying the application name and namespace.
Can I restore cluster resources without any namespaced resources?
Yes, set noNamespaces: true and configure clusterResourceRestoreScope with allGroupKinds: true to restore only cluster-scoped resources.
Restore Behavior & Conflict Handling
What are the namespace restore modes and when should I use each?

Three modes control how namespaced resources are restored:

  1. FAIL_ON_CONFLICT - Fails if resources already exist (safest, default in examples)
  2. DELETE_AND_RESTORE - Deletes existing resources before restoring (for rollbacks)
  3. MERGE_SKIP_ON_CONFLICT - Merges with existing resources, skipping conflicts (for GitOps workflows)
What's the difference between DELETE_AND_RESTORE and FAIL_ON_CONFLICT?
DELETE_AND_RESTORE deletes existing resources before restoring from backup, useful for rollbacks. FAIL_ON_CONFLICT fails the restore if resources already exist, preventing accidental overwrites.
When should I use MERGE_SKIP_ON_CONFLICT mode?
Use MERGE_SKIP_ON_CONFLICT for GitOps workflows where you want to restore resources but skip any that already exist, allowing declarative configuration to take precedence.
Volume Data Restoration
What are the volume data restore policies?

Three policies control volume data restoration:

  1. RESTORE_VOLUME_DATA_FROM_BACKUP - Restores volume data from the backup
  2. REUSE_VOLUME_HANDLE_FROM_BACKUP - Reuses the original volume handle without copying data
  3. NO_VOLUME_DATA_RESTORATION - Doesn’t restore volume data at all
How do I apply different volume policies to different volume types?
Use volumeDataRestorePolicyBindings to specify different policies per volumeType (e.g., GCE_PERSISTENT_DISK), overriding the default volumeDataRestorePolicy.
Advanced Features
How do I rename a namespace during restore?
Use transformationRules with two rules: one to rename the Namespace resource itself, and another to update the namespace field in all resources being moved.
How do I control the order resources are restored?
Configure restoreOrder with groupKindDependencies to specify which resource kinds must be restored before others (e.g., StorageClass before PersistentVolumeClaim).
What can I do with transformation rules?
Transformation rules let you modify resources during restore using operations like REPLACE (change field values) and COPY (copy values between fields). Use resourceFilter to target specific resources by kind, namespace, or JSONPath.

Using a different cloud?

Explore containers guides for other cloud providers: