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: core template configuration, confidential computing for encrypted workloads, and image versioning strategies.

Instance templates reference service accounts, VPC networks, and optionally existing disks or resource policies. The examples are intentionally small. Combine them with your own IAM roles, networking, and managed instance groups.

Define a template with disks, scheduling, and service accounts

Most deployments start by defining the core compute configuration: machine type, boot disk, and service account permissions.

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. The disks array defines boot and data disks; sourceImage specifies the OS image, while autoDelete controls whether disks are removed when instances terminate. The scheduling block configures restart behavior and maintenance policies. The serviceAccount property grants the VM permissions to access GCP services; the “cloud-platform” scope provides full API access.

Enable confidential computing for encrypted workloads

Workloads handling sensitive data can use confidential computing to encrypt memory contents during execution.

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. This requires specific machine types (n2d-standard series) and CPU platforms (AMD Milan for SEV). The enableConfidentialCompute property activates the feature, while confidentialInstanceType specifies the encryption technology.

Deploy templates with the latest image at apply time

When you want all instances to use the same image version, determined when Pulumi runs, use a data source to fetch the latest image.

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 data source retrieves the latest image in a family at apply time. Using selfLink as the sourceImage locks all instances to that specific image version, preventing unexpected updates during scaling events.

Let instances fetch the latest image at launch time

Alternatively, you can have each instance automatically use the newest image when it launches.

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

Specifying the family name directly (e.g., “debian-cloud/debian-11”) as sourceImage tells GCP to resolve the latest image at instance creation time. This allows automatic updates during scaling without redeploying the template.

Beyond these examples

These snippets focus on specific template-level features: disk configuration and image selection, confidential computing and security features, and image versioning strategies. They’re intentionally minimal rather than full VM deployment configurations.

The examples may reference pre-existing infrastructure such as service accounts with appropriate IAM permissions, VPC networks and subnets, and existing disks and snapshot policies for some examples. They focus on configuring the template rather than provisioning everything around it.

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

  • VPC networking details (subnetworks, IP addressing)
  • GPU accelerators and specialized hardware (guestAccelerators)
  • Metadata and startup scripts (metadata, metadataStartupScript)
  • Reservation affinity and capacity planning
  • Network performance tuning (networkPerformanceConfig)
  • Shielded VM configuration (shieldedInstanceConfig)

These omissions are intentional: the goal is to illustrate how each template feature is wired, not provide drop-in VM modules. See the 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?
Global instance templates work in any region, but regional templates (google_compute_region_instance_template) provide better data residency and reduce the impact of outages outside your region.
What happens if I specify regional resources in a global template?
The template becomes restricted to the region where that resource resides. For example, a custom subnetwork ties the template to a specific region.
Naming & Immutability
What's the difference between name and namePrefix?
name sets an explicit name, while namePrefix auto-generates unique names with timestamps and counters. They conflict with each other, and both are immutable.
Why is my namePrefix causing name collisions?
Prefixes longer than 37 characters use a shortened UUID that’s more prone to collisions. Keep namePrefix to 37 characters or less for better uniqueness.
What properties are immutable after creation?
Most properties are immutable, including name, namePrefix, machineType, disks, confidentialInstanceConfig, shieldedInstanceConfig, networkInterfaces, metadata, and region. Changes force resource replacement.
Security & Confidential Computing
What's required to enable Confidential Computing?
Set confidentialInstanceConfig.enableConfidentialCompute to true, use a supported machine type like n2d-standard-2, and specify minCpuPlatform (e.g., AMD Milan).
Can I use Shielded VM with any boot image?
No, shieldedInstanceConfig only works with boot images that have shielded VM support. Check the list of supported images before enabling.
Image Deployment Strategies
How do I deploy the latest image in my instance template?

You have two options:

  1. Lock at deployment - Use gcp.compute.getImage data source to deploy the latest image available when Pulumi runs (all instances use same image)
  2. Check at instance creation - Use image family directly (e.g., debian-cloud/debian-11) so each instance checks for latest at creation time
How do I create a custom machine type?
Format machineType as custom-VCPUS-MEM_IN_MB. For example, custom-6-20480 creates a machine with 6 vCPUs and 20GB of RAM.
Configuration Gotchas
Can I use both metadata and metadataStartupScript?
No, metadataStartupScript replaces the startup-script metadata key, so you can’t use both simultaneously. Choose one approach.
Why aren't all my labels showing in the labels field?
The labels field is non-authoritative and only manages labels in your configuration. Use effectiveLabels to see all labels present on the resource.
What's required for networkPerformanceConfig to work?
Three requirements must be met: machineType must be a supported type, the image must include GVNIC in guest-os-features, and network_interface.0.nic-type must be GVNIC.
How many resource policies can I attach?
Currently, a maximum of 1 resource policy is supported. Modifying the resourcePolicies list causes instance recreation.

Using a different cloud?

Explore compute guides for other cloud providers: