Create GCP Instance Templates

The gcp:compute/instanceTemplate:InstanceTemplate resource, part of the Pulumi GCP provider, defines reusable VM configurations for managed instance groups: machine type, disks, networking, and security settings. This guide focuses on three capabilities: disk and backup policy configuration, image selection strategies, and Confidential Computing setup.

Instance templates reference service accounts, networks, and optionally existing disks or resource policies that must exist separately. The examples are intentionally small. Combine them with your own IAM, networking, and storage infrastructure.

Define a template with disks, scheduling, and service accounts

Most deployments start by specifying the machine type, boot disk image, and service account that instances will use.

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

const _default = new gcp.serviceaccount.Account("default", {
    accountId: "service-account-id",
    displayName: "Service Account",
});
const myImage = gcp.compute.getImage({
    family: "debian-11",
    project: "debian-cloud",
});
const foobar = new gcp.compute.Disk("foobar", {
    name: "existing-disk",
    image: myImage.then(myImage => myImage.selfLink),
    size: 10,
    type: "pd-ssd",
    zone: "us-central1-a",
});
const dailyBackup = new gcp.compute.ResourcePolicy("daily_backup", {
    name: "every-day-4am",
    region: "us-central1",
    snapshotSchedulePolicy: {
        schedule: {
            dailySchedule: {
                daysInCycle: 1,
                startTime: "04:00",
            },
        },
    },
});
const defaultInstanceTemplate = new gcp.compute.InstanceTemplate("default", {
    name: "appserver-template",
    description: "This template is used to create app server instances.",
    tags: [
        "foo",
        "bar",
    ],
    labels: {
        environment: "dev",
    },
    instanceDescription: "description assigned to instances",
    machineType: "e2-medium",
    canIpForward: false,
    scheduling: {
        automaticRestart: true,
        onHostMaintenance: "MIGRATE",
    },
    disks: [
        {
            sourceImage: "debian-cloud/debian-11",
            autoDelete: true,
            boot: true,
            resourcePolicies: dailyBackup.id,
        },
        {
            source: foobar.name,
            autoDelete: false,
            boot: false,
        },
    ],
    networkInterfaces: [{
        network: "default",
    }],
    metadata: {
        foo: "bar",
    },
    serviceAccount: {
        email: _default.email,
        scopes: ["cloud-platform"],
    },
});
import pulumi
import pulumi_gcp as gcp

default = gcp.serviceaccount.Account("default",
    account_id="service-account-id",
    display_name="Service Account")
my_image = gcp.compute.get_image(family="debian-11",
    project="debian-cloud")
foobar = gcp.compute.Disk("foobar",
    name="existing-disk",
    image=my_image.self_link,
    size=10,
    type="pd-ssd",
    zone="us-central1-a")
daily_backup = gcp.compute.ResourcePolicy("daily_backup",
    name="every-day-4am",
    region="us-central1",
    snapshot_schedule_policy={
        "schedule": {
            "daily_schedule": {
                "days_in_cycle": 1,
                "start_time": "04:00",
            },
        },
    })
default_instance_template = gcp.compute.InstanceTemplate("default",
    name="appserver-template",
    description="This template is used to create app server instances.",
    tags=[
        "foo",
        "bar",
    ],
    labels={
        "environment": "dev",
    },
    instance_description="description assigned to instances",
    machine_type="e2-medium",
    can_ip_forward=False,
    scheduling={
        "automatic_restart": True,
        "on_host_maintenance": "MIGRATE",
    },
    disks=[
        {
            "source_image": "debian-cloud/debian-11",
            "auto_delete": True,
            "boot": True,
            "resource_policies": daily_backup.id,
        },
        {
            "source": foobar.name,
            "auto_delete": False,
            "boot": False,
        },
    ],
    network_interfaces=[{
        "network": "default",
    }],
    metadata={
        "foo": "bar",
    },
    service_account={
        "email": default.email,
        "scopes": ["cloud-platform"],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_default, err := serviceaccount.NewAccount(ctx, "default", &serviceaccount.AccountArgs{
			AccountId:   pulumi.String("service-account-id"),
			DisplayName: pulumi.String("Service Account"),
		})
		if err != nil {
			return err
		}
		myImage, err := compute.LookupImage(ctx, &compute.LookupImageArgs{
			Family:  pulumi.StringRef("debian-11"),
			Project: pulumi.StringRef("debian-cloud"),
		}, nil)
		if err != nil {
			return err
		}
		foobar, err := compute.NewDisk(ctx, "foobar", &compute.DiskArgs{
			Name:  pulumi.String("existing-disk"),
			Image: pulumi.String(myImage.SelfLink),
			Size:  pulumi.Int(10),
			Type:  pulumi.String("pd-ssd"),
			Zone:  pulumi.String("us-central1-a"),
		})
		if err != nil {
			return err
		}
		dailyBackup, err := compute.NewResourcePolicy(ctx, "daily_backup", &compute.ResourcePolicyArgs{
			Name:   pulumi.String("every-day-4am"),
			Region: pulumi.String("us-central1"),
			SnapshotSchedulePolicy: &compute.ResourcePolicySnapshotSchedulePolicyArgs{
				Schedule: &compute.ResourcePolicySnapshotSchedulePolicyScheduleArgs{
					DailySchedule: &compute.ResourcePolicySnapshotSchedulePolicyScheduleDailyScheduleArgs{
						DaysInCycle: pulumi.Int(1),
						StartTime:   pulumi.String("04:00"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		_, err = compute.NewInstanceTemplate(ctx, "default", &compute.InstanceTemplateArgs{
			Name:        pulumi.String("appserver-template"),
			Description: pulumi.String("This template is used to create app server instances."),
			Tags: pulumi.StringArray{
				pulumi.String("foo"),
				pulumi.String("bar"),
			},
			Labels: pulumi.StringMap{
				"environment": pulumi.String("dev"),
			},
			InstanceDescription: pulumi.String("description assigned to instances"),
			MachineType:         pulumi.String("e2-medium"),
			CanIpForward:        pulumi.Bool(false),
			Scheduling: &compute.InstanceTemplateSchedulingArgs{
				AutomaticRestart:  pulumi.Bool(true),
				OnHostMaintenance: pulumi.String("MIGRATE"),
			},
			Disks: compute.InstanceTemplateDiskArray{
				&compute.InstanceTemplateDiskArgs{
					SourceImage:      pulumi.String("debian-cloud/debian-11"),
					AutoDelete:       pulumi.Bool(true),
					Boot:             pulumi.Bool(true),
					ResourcePolicies: dailyBackup.ID(),
				},
				&compute.InstanceTemplateDiskArgs{
					Source:     foobar.Name,
					AutoDelete: pulumi.Bool(false),
					Boot:       pulumi.Bool(false),
				},
			},
			NetworkInterfaces: compute.InstanceTemplateNetworkInterfaceArray{
				&compute.InstanceTemplateNetworkInterfaceArgs{
					Network: pulumi.String("default"),
				},
			},
			Metadata: pulumi.StringMap{
				"foo": pulumi.String("bar"),
			},
			ServiceAccount: &compute.InstanceTemplateServiceAccountArgs{
				Email: _default.Email,
				Scopes: pulumi.StringArray{
					pulumi.String("cloud-platform"),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var @default = new Gcp.ServiceAccount.Account("default", new()
    {
        AccountId = "service-account-id",
        DisplayName = "Service Account",
    });

    var myImage = Gcp.Compute.GetImage.Invoke(new()
    {
        Family = "debian-11",
        Project = "debian-cloud",
    });

    var foobar = new Gcp.Compute.Disk("foobar", new()
    {
        Name = "existing-disk",
        Image = myImage.Apply(getImageResult => getImageResult.SelfLink),
        Size = 10,
        Type = "pd-ssd",
        Zone = "us-central1-a",
    });

    var dailyBackup = new Gcp.Compute.ResourcePolicy("daily_backup", new()
    {
        Name = "every-day-4am",
        Region = "us-central1",
        SnapshotSchedulePolicy = new Gcp.Compute.Inputs.ResourcePolicySnapshotSchedulePolicyArgs
        {
            Schedule = new Gcp.Compute.Inputs.ResourcePolicySnapshotSchedulePolicyScheduleArgs
            {
                DailySchedule = new Gcp.Compute.Inputs.ResourcePolicySnapshotSchedulePolicyScheduleDailyScheduleArgs
                {
                    DaysInCycle = 1,
                    StartTime = "04:00",
                },
            },
        },
    });

    var defaultInstanceTemplate = new Gcp.Compute.InstanceTemplate("default", new()
    {
        Name = "appserver-template",
        Description = "This template is used to create app server instances.",
        Tags = new[]
        {
            "foo",
            "bar",
        },
        Labels = 
        {
            { "environment", "dev" },
        },
        InstanceDescription = "description assigned to instances",
        MachineType = "e2-medium",
        CanIpForward = false,
        Scheduling = new Gcp.Compute.Inputs.InstanceTemplateSchedulingArgs
        {
            AutomaticRestart = true,
            OnHostMaintenance = "MIGRATE",
        },
        Disks = new[]
        {
            new Gcp.Compute.Inputs.InstanceTemplateDiskArgs
            {
                SourceImage = "debian-cloud/debian-11",
                AutoDelete = true,
                Boot = true,
                ResourcePolicies = dailyBackup.Id,
            },
            new Gcp.Compute.Inputs.InstanceTemplateDiskArgs
            {
                Source = foobar.Name,
                AutoDelete = false,
                Boot = false,
            },
        },
        NetworkInterfaces = new[]
        {
            new Gcp.Compute.Inputs.InstanceTemplateNetworkInterfaceArgs
            {
                Network = "default",
            },
        },
        Metadata = 
        {
            { "foo", "bar" },
        },
        ServiceAccount = new Gcp.Compute.Inputs.InstanceTemplateServiceAccountArgs
        {
            Email = @default.Email,
            Scopes = new[]
            {
                "cloud-platform",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.serviceaccount.Account;
import com.pulumi.gcp.serviceaccount.AccountArgs;
import com.pulumi.gcp.compute.ComputeFunctions;
import com.pulumi.gcp.compute.inputs.GetImageArgs;
import com.pulumi.gcp.compute.Disk;
import com.pulumi.gcp.compute.DiskArgs;
import com.pulumi.gcp.compute.ResourcePolicy;
import com.pulumi.gcp.compute.ResourcePolicyArgs;
import com.pulumi.gcp.compute.inputs.ResourcePolicySnapshotSchedulePolicyArgs;
import com.pulumi.gcp.compute.inputs.ResourcePolicySnapshotSchedulePolicyScheduleArgs;
import com.pulumi.gcp.compute.inputs.ResourcePolicySnapshotSchedulePolicyScheduleDailyScheduleArgs;
import com.pulumi.gcp.compute.InstanceTemplate;
import com.pulumi.gcp.compute.InstanceTemplateArgs;
import com.pulumi.gcp.compute.inputs.InstanceTemplateSchedulingArgs;
import com.pulumi.gcp.compute.inputs.InstanceTemplateDiskArgs;
import com.pulumi.gcp.compute.inputs.InstanceTemplateNetworkInterfaceArgs;
import com.pulumi.gcp.compute.inputs.InstanceTemplateServiceAccountArgs;
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 default_ = new Account("default", AccountArgs.builder()
            .accountId("service-account-id")
            .displayName("Service Account")
            .build());

        final var myImage = ComputeFunctions.getImage(GetImageArgs.builder()
            .family("debian-11")
            .project("debian-cloud")
            .build());

        var foobar = new Disk("foobar", DiskArgs.builder()
            .name("existing-disk")
            .image(myImage.selfLink())
            .size(10)
            .type("pd-ssd")
            .zone("us-central1-a")
            .build());

        var dailyBackup = new ResourcePolicy("dailyBackup", ResourcePolicyArgs.builder()
            .name("every-day-4am")
            .region("us-central1")
            .snapshotSchedulePolicy(ResourcePolicySnapshotSchedulePolicyArgs.builder()
                .schedule(ResourcePolicySnapshotSchedulePolicyScheduleArgs.builder()
                    .dailySchedule(ResourcePolicySnapshotSchedulePolicyScheduleDailyScheduleArgs.builder()
                        .daysInCycle(1)
                        .startTime("04:00")
                        .build())
                    .build())
                .build())
            .build());

        var defaultInstanceTemplate = new InstanceTemplate("defaultInstanceTemplate", InstanceTemplateArgs.builder()
            .name("appserver-template")
            .description("This template is used to create app server instances.")
            .tags(            
                "foo",
                "bar")
            .labels(Map.of("environment", "dev"))
            .instanceDescription("description assigned to instances")
            .machineType("e2-medium")
            .canIpForward(false)
            .scheduling(InstanceTemplateSchedulingArgs.builder()
                .automaticRestart(true)
                .onHostMaintenance("MIGRATE")
                .build())
            .disks(            
                InstanceTemplateDiskArgs.builder()
                    .sourceImage("debian-cloud/debian-11")
                    .autoDelete(true)
                    .boot(true)
                    .resourcePolicies(dailyBackup.id())
                    .build(),
                InstanceTemplateDiskArgs.builder()
                    .source(foobar.name())
                    .autoDelete(false)
                    .boot(false)
                    .build())
            .networkInterfaces(InstanceTemplateNetworkInterfaceArgs.builder()
                .network("default")
                .build())
            .metadata(Map.of("foo", "bar"))
            .serviceAccount(InstanceTemplateServiceAccountArgs.builder()
                .email(default_.email())
                .scopes("cloud-platform")
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:serviceaccount:Account
    properties:
      accountId: service-account-id
      displayName: Service Account
  defaultInstanceTemplate:
    type: gcp:compute:InstanceTemplate
    name: default
    properties:
      name: appserver-template
      description: This template is used to create app server instances.
      tags:
        - foo
        - bar
      labels:
        environment: dev
      instanceDescription: description assigned to instances
      machineType: e2-medium
      canIpForward: false
      scheduling:
        automaticRestart: true
        onHostMaintenance: MIGRATE
      disks:
        - sourceImage: debian-cloud/debian-11
          autoDelete: true
          boot: true
          resourcePolicies: ${dailyBackup.id}
        - source: ${foobar.name}
          autoDelete: false
          boot: false
      networkInterfaces:
        - network: default
      metadata:
        foo: bar
      serviceAccount:
        email: ${default.email}
        scopes:
          - cloud-platform
  foobar:
    type: gcp:compute:Disk
    properties:
      name: existing-disk
      image: ${myImage.selfLink}
      size: 10
      type: pd-ssd
      zone: us-central1-a
  dailyBackup:
    type: gcp:compute:ResourcePolicy
    name: daily_backup
    properties:
      name: every-day-4am
      region: us-central1
      snapshotSchedulePolicy:
        schedule:
          dailySchedule:
            daysInCycle: 1
            startTime: 04:00
variables:
  myImage:
    fn::invoke:
      function: gcp:compute:getImage
      arguments:
        family: debian-11
        project: debian-cloud

The machineType property sets the VM size (e.g., “e2-medium”). The disks array defines boot and data disks; sourceImage specifies the OS, and resourcePolicies attaches backup schedules. The scheduling block controls restart behavior during maintenance. The serviceAccount property grants instances permissions via scopes like “cloud-platform”.

Deploy the latest image using dynamic lookup

Teams often want all instances to use the same image version, determined when Pulumi runs.

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

const myImage = gcp.compute.getImage({
    family: "debian-11",
    project: "debian-cloud",
});
const instanceTemplate = new gcp.compute.InstanceTemplate("instance_template", {
    namePrefix: "instance-template-",
    machineType: "e2-medium",
    region: "us-central1",
    disks: [{
        sourceImage: myImage.then(myImage => myImage.selfLink),
    }],
});
import pulumi
import pulumi_gcp as gcp

my_image = gcp.compute.get_image(family="debian-11",
    project="debian-cloud")
instance_template = gcp.compute.InstanceTemplate("instance_template",
    name_prefix="instance-template-",
    machine_type="e2-medium",
    region="us-central1",
    disks=[{
        "source_image": my_image.self_link,
    }])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		myImage, err := compute.LookupImage(ctx, &compute.LookupImageArgs{
			Family:  pulumi.StringRef("debian-11"),
			Project: pulumi.StringRef("debian-cloud"),
		}, nil)
		if err != nil {
			return err
		}
		_, err = compute.NewInstanceTemplate(ctx, "instance_template", &compute.InstanceTemplateArgs{
			NamePrefix:  pulumi.String("instance-template-"),
			MachineType: pulumi.String("e2-medium"),
			Region:      pulumi.String("us-central1"),
			Disks: compute.InstanceTemplateDiskArray{
				&compute.InstanceTemplateDiskArgs{
					SourceImage: pulumi.String(myImage.SelfLink),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var myImage = Gcp.Compute.GetImage.Invoke(new()
    {
        Family = "debian-11",
        Project = "debian-cloud",
    });

    var instanceTemplate = new Gcp.Compute.InstanceTemplate("instance_template", new()
    {
        NamePrefix = "instance-template-",
        MachineType = "e2-medium",
        Region = "us-central1",
        Disks = new[]
        {
            new Gcp.Compute.Inputs.InstanceTemplateDiskArgs
            {
                SourceImage = myImage.Apply(getImageResult => getImageResult.SelfLink),
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.ComputeFunctions;
import com.pulumi.gcp.compute.inputs.GetImageArgs;
import com.pulumi.gcp.compute.InstanceTemplate;
import com.pulumi.gcp.compute.InstanceTemplateArgs;
import com.pulumi.gcp.compute.inputs.InstanceTemplateDiskArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

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

    public static void stack(Context ctx) {
        final var myImage = ComputeFunctions.getImage(GetImageArgs.builder()
            .family("debian-11")
            .project("debian-cloud")
            .build());

        var instanceTemplate = new InstanceTemplate("instanceTemplate", InstanceTemplateArgs.builder()
            .namePrefix("instance-template-")
            .machineType("e2-medium")
            .region("us-central1")
            .disks(InstanceTemplateDiskArgs.builder()
                .sourceImage(myImage.selfLink())
                .build())
            .build());

    }
}
resources:
  instanceTemplate:
    type: gcp:compute:InstanceTemplate
    name: instance_template
    properties:
      namePrefix: instance-template-
      machineType: e2-medium
      region: us-central1
      disks:
        - sourceImage: ${myImage.selfLink}
variables:
  myImage:
    fn::invoke:
      function: gcp:compute:getImage
      arguments:
        family: debian-11
        project: debian-cloud

The getImage function retrieves the latest image from a family at deployment time. Using selfLink in sourceImage locks all instances to that specific image version, ensuring consistency across the instance group. New deployments require a Pulumi update to pick up newer images.

Let instances fetch the latest image at creation

Some deployments prefer instances to check for the latest image when they’re created during scaling or rebuilds.

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

const instanceTemplate = new gcp.compute.InstanceTemplate("instance_template", {
    namePrefix: "instance-template-",
    machineType: "e2-medium",
    region: "us-central1",
    disks: [{
        sourceImage: "debian-cloud/debian-11",
    }],
});
import pulumi
import pulumi_gcp as gcp

instance_template = gcp.compute.InstanceTemplate("instance_template",
    name_prefix="instance-template-",
    machine_type="e2-medium",
    region="us-central1",
    disks=[{
        "source_image": "debian-cloud/debian-11",
    }])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewInstanceTemplate(ctx, "instance_template", &compute.InstanceTemplateArgs{
			NamePrefix:  pulumi.String("instance-template-"),
			MachineType: pulumi.String("e2-medium"),
			Region:      pulumi.String("us-central1"),
			Disks: compute.InstanceTemplateDiskArray{
				&compute.InstanceTemplateDiskArgs{
					SourceImage: pulumi.String("debian-cloud/debian-11"),
				},
			},
		})
		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 instanceTemplate = new Gcp.Compute.InstanceTemplate("instance_template", new()
    {
        NamePrefix = "instance-template-",
        MachineType = "e2-medium",
        Region = "us-central1",
        Disks = new[]
        {
            new Gcp.Compute.Inputs.InstanceTemplateDiskArgs
            {
                SourceImage = "debian-cloud/debian-11",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.InstanceTemplate;
import com.pulumi.gcp.compute.InstanceTemplateArgs;
import com.pulumi.gcp.compute.inputs.InstanceTemplateDiskArgs;
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 instanceTemplate = new InstanceTemplate("instanceTemplate", InstanceTemplateArgs.builder()
            .namePrefix("instance-template-")
            .machineType("e2-medium")
            .region("us-central1")
            .disks(InstanceTemplateDiskArgs.builder()
                .sourceImage("debian-cloud/debian-11")
                .build())
            .build());

    }
}
resources:
  instanceTemplate:
    type: gcp:compute:InstanceTemplate
    name: instance_template
    properties:
      namePrefix: instance-template-
      machineType: e2-medium
      region: us-central1
      disks:
        - sourceImage: debian-cloud/debian-11

Setting sourceImage to the family name (e.g., “debian-cloud/debian-11”) delegates image lookup to GCP. Each new instance checks for the latest image in that family at creation time, allowing automatic updates without Pulumi changes. This differs from EX4, where the image version is fixed at deployment.

Enable Confidential Computing for encrypted workloads

Workloads handling sensitive data can use Confidential Computing to encrypt data in memory.

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

const _default = new gcp.serviceaccount.Account("default", {
    accountId: "my-custom-sa",
    displayName: "Custom SA for VM Instance",
});
const confidentialInstanceTemplate = new gcp.compute.InstanceTemplate("confidential_instance_template", {
    networkInterfaces: [{
        accessConfigs: [{}],
        network: "default",
    }],
    name: "my-confidential-instance-template",
    region: "us-central1",
    machineType: "n2d-standard-2",
    minCpuPlatform: "AMD Milan",
    confidentialInstanceConfig: {
        enableConfidentialCompute: true,
        confidentialInstanceType: "SEV",
    },
    disks: [{
        sourceImage: "ubuntu-os-cloud/ubuntu-2204-lts",
    }],
    serviceAccount: {
        email: _default.email,
        scopes: ["cloud-platform"],
    },
});
import pulumi
import pulumi_gcp as gcp

default = gcp.serviceaccount.Account("default",
    account_id="my-custom-sa",
    display_name="Custom SA for VM Instance")
confidential_instance_template = gcp.compute.InstanceTemplate("confidential_instance_template",
    network_interfaces=[{
        "access_configs": [{}],
        "network": "default",
    }],
    name="my-confidential-instance-template",
    region="us-central1",
    machine_type="n2d-standard-2",
    min_cpu_platform="AMD Milan",
    confidential_instance_config={
        "enable_confidential_compute": True,
        "confidential_instance_type": "SEV",
    },
    disks=[{
        "source_image": "ubuntu-os-cloud/ubuntu-2204-lts",
    }],
    service_account={
        "email": default.email,
        "scopes": ["cloud-platform"],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_default, err := serviceaccount.NewAccount(ctx, "default", &serviceaccount.AccountArgs{
			AccountId:   pulumi.String("my-custom-sa"),
			DisplayName: pulumi.String("Custom SA for VM Instance"),
		})
		if err != nil {
			return err
		}
		_, err = compute.NewInstanceTemplate(ctx, "confidential_instance_template", &compute.InstanceTemplateArgs{
			NetworkInterfaces: compute.InstanceTemplateNetworkInterfaceArray{
				&compute.InstanceTemplateNetworkInterfaceArgs{
					AccessConfigs: compute.InstanceTemplateNetworkInterfaceAccessConfigArray{
						&compute.InstanceTemplateNetworkInterfaceAccessConfigArgs{},
					},
					Network: pulumi.String("default"),
				},
			},
			Name:           pulumi.String("my-confidential-instance-template"),
			Region:         pulumi.String("us-central1"),
			MachineType:    pulumi.String("n2d-standard-2"),
			MinCpuPlatform: pulumi.String("AMD Milan"),
			ConfidentialInstanceConfig: &compute.InstanceTemplateConfidentialInstanceConfigArgs{
				EnableConfidentialCompute: pulumi.Bool(true),
				ConfidentialInstanceType:  pulumi.String("SEV"),
			},
			Disks: compute.InstanceTemplateDiskArray{
				&compute.InstanceTemplateDiskArgs{
					SourceImage: pulumi.String("ubuntu-os-cloud/ubuntu-2204-lts"),
				},
			},
			ServiceAccount: &compute.InstanceTemplateServiceAccountArgs{
				Email: _default.Email,
				Scopes: pulumi.StringArray{
					pulumi.String("cloud-platform"),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var @default = new Gcp.ServiceAccount.Account("default", new()
    {
        AccountId = "my-custom-sa",
        DisplayName = "Custom SA for VM Instance",
    });

    var confidentialInstanceTemplate = new Gcp.Compute.InstanceTemplate("confidential_instance_template", new()
    {
        NetworkInterfaces = new[]
        {
            new Gcp.Compute.Inputs.InstanceTemplateNetworkInterfaceArgs
            {
                AccessConfigs = new[]
                {
                    null,
                },
                Network = "default",
            },
        },
        Name = "my-confidential-instance-template",
        Region = "us-central1",
        MachineType = "n2d-standard-2",
        MinCpuPlatform = "AMD Milan",
        ConfidentialInstanceConfig = new Gcp.Compute.Inputs.InstanceTemplateConfidentialInstanceConfigArgs
        {
            EnableConfidentialCompute = true,
            ConfidentialInstanceType = "SEV",
        },
        Disks = new[]
        {
            new Gcp.Compute.Inputs.InstanceTemplateDiskArgs
            {
                SourceImage = "ubuntu-os-cloud/ubuntu-2204-lts",
            },
        },
        ServiceAccount = new Gcp.Compute.Inputs.InstanceTemplateServiceAccountArgs
        {
            Email = @default.Email,
            Scopes = new[]
            {
                "cloud-platform",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.serviceaccount.Account;
import com.pulumi.gcp.serviceaccount.AccountArgs;
import com.pulumi.gcp.compute.InstanceTemplate;
import com.pulumi.gcp.compute.InstanceTemplateArgs;
import com.pulumi.gcp.compute.inputs.InstanceTemplateNetworkInterfaceArgs;
import com.pulumi.gcp.compute.inputs.InstanceTemplateConfidentialInstanceConfigArgs;
import com.pulumi.gcp.compute.inputs.InstanceTemplateDiskArgs;
import com.pulumi.gcp.compute.inputs.InstanceTemplateServiceAccountArgs;
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 default_ = new Account("default", AccountArgs.builder()
            .accountId("my-custom-sa")
            .displayName("Custom SA for VM Instance")
            .build());

        var confidentialInstanceTemplate = new InstanceTemplate("confidentialInstanceTemplate", InstanceTemplateArgs.builder()
            .networkInterfaces(InstanceTemplateNetworkInterfaceArgs.builder()
                .accessConfigs(InstanceTemplateNetworkInterfaceAccessConfigArgs.builder()
                    .build())
                .network("default")
                .build())
            .name("my-confidential-instance-template")
            .region("us-central1")
            .machineType("n2d-standard-2")
            .minCpuPlatform("AMD Milan")
            .confidentialInstanceConfig(InstanceTemplateConfidentialInstanceConfigArgs.builder()
                .enableConfidentialCompute(true)
                .confidentialInstanceType("SEV")
                .build())
            .disks(InstanceTemplateDiskArgs.builder()
                .sourceImage("ubuntu-os-cloud/ubuntu-2204-lts")
                .build())
            .serviceAccount(InstanceTemplateServiceAccountArgs.builder()
                .email(default_.email())
                .scopes("cloud-platform")
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:serviceaccount:Account
    properties:
      accountId: my-custom-sa
      displayName: Custom SA for VM Instance
  confidentialInstanceTemplate:
    type: gcp:compute:InstanceTemplate
    name: confidential_instance_template
    properties:
      networkInterfaces:
        - accessConfigs:
            - {}
          network: default
      name: my-confidential-instance-template
      region: us-central1
      machineType: n2d-standard-2
      minCpuPlatform: AMD Milan
      confidentialInstanceConfig:
        enableConfidentialCompute: true
        confidentialInstanceType: SEV
      disks:
        - sourceImage: ubuntu-os-cloud/ubuntu-2204-lts
      serviceAccount:
        email: ${default.email}
        scopes:
          - cloud-platform

The confidentialInstanceConfig block enables memory encryption via enableConfidentialCompute. This requires specific machine types (n2d-standard-2) and CPU platforms (AMD Milan) that support SEV (Secure Encrypted Virtualization). Confidential Computing protects against memory inspection and unauthorized access to data in use.

Beyond these examples

These snippets focus on specific instance template features: disk configuration and backup policies, image selection strategies, and Confidential Computing with memory encryption. They’re intentionally minimal rather than full VM deployment configurations.

The examples reference pre-existing infrastructure such as service accounts with appropriate scopes, VPC networks (default network in examples), and existing disks and resource policies. They focus on template configuration rather than provisioning the surrounding infrastructure.

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

  • VPC and subnet configuration (networkInterfaces details)
  • GPU accelerators (guestAccelerators)
  • Shielded VM settings (shieldedInstanceConfig)
  • Metadata and startup scripts
  • Network performance tuning (networkPerformanceConfig)
  • Reservation affinity and resource manager tags

These omissions are intentional: the goal is to illustrate how each template feature is wired, not provide drop-in instance group modules. See the Compute Instance Template resource reference for all available configuration options.

Let's create GCP Instance Templates

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Resource Scope & Regional Considerations
Should I use global or regional instance templates?
While global instance templates work in any region, regional templates (google_compute_region_instance_template) provide better isolation from outages outside your region and ensure data residency within your region.
Immutability & Resource Lifecycle
What properties can I change after creating an instance template?
Almost none. Nearly all properties (machineType, disks, networkInterfaces, scheduling, etc.) are immutable and require recreating the template to change. Only labels and partnerMetadata can be updated in place.
Naming & Identification
What's the difference between name and namePrefix?
name and namePrefix are mutually exclusive. Use name for explicit naming or namePrefix for auto-generated names with timestamps. Keep namePrefix to 37 characters or less to avoid collision-prone shortened UUIDs.
Machine Types & Custom Configurations
How do I create a custom machine type?
Use the format custom-VCPUS-MEM_IN_MB for machineType. For example, custom-6-20480 creates a machine with 6 vCPUs and 20GB of RAM.
Why can't Pulumi manage disks created by z3 machine types?
Advanced machine types like z3 create disks that cannot be managed by Terraform/Pulumi by default. Use lifecycle.ignore_changes or explicitly add these disks to your configuration.
Security Features
What are the requirements for enabling Confidential Computing?
You need three things: (1) a supported machine type like n2d-standard-2, (2) minCpuPlatform set to AMD Milan, and (3) confidentialInstanceConfig with enableConfidentialCompute: true.
Can I use Shielded VM with any boot image?
No. shieldedInstanceConfig only works with boot images that have shielded VM support. Check the shielded images list before enabling this feature.
Image Deployment Strategies
How do I deploy the latest image from a family?

You have two options:

  1. Data source approach - Use gcp.compute.getImage to lock to the latest image at pulumi up time
  2. Family string approach - Use the family directly (e.g., debian-cloud/debian-11) to get the latest at instance creation time
What's the difference between the two image deployment approaches?
The data source approach ensures all instances use the same image and only updates on pulumi up. The family string approach lets each instance check for the latest image at creation or scaling time, which can result in different instances running different images.
Networking & Performance
What are the requirements for high bandwidth networking?
networkPerformanceConfig requires three things: (1) machineType must be a supported type, (2) the image must include GVNIC in guest-os-features, and (3) network_interface.0.nic-type must be GVNIC.
Metadata & Startup Configuration
Can I use both metadataStartupScript and startup-script metadata?
No. metadataStartupScript replaces the startup-script metadata key, so the two mechanisms cannot be used simultaneously. Choose one approach.
What's the difference between labels and effectiveLabels?
labels is non-authoritative and only manages labels in your configuration. effectiveLabels shows all labels on the resource, including those added by other clients and services.

Using a different cloud?

Explore compute guides for other cloud providers: