The gcp:compute/instance:Instance resource, part of the Pulumi GCP provider, defines a GCE VM instance: its machine type, boot disk, network placement, and security features. This guide focuses on two capabilities: basic instance creation with service accounts and Confidential Computing configuration.
Instances require service accounts for Google Cloud API access and run in VPC networks. The examples are intentionally small. Combine them with your own VPC configuration, IAM roles, and security policies.
Launch an instance with a service account and startup script
Most deployments create an instance with a boot disk image, machine type, and network configuration, then attach a service account for Google Cloud API access.
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 defaultInstance = new gcp.compute.Instance("default", {
networkInterfaces: [{
accessConfigs: [{}],
network: "default",
}],
name: "my-instance",
machineType: "n2-standard-2",
zone: "us-central1-a",
tags: [
"foo",
"bar",
],
bootDisk: {
initializeParams: {
image: "debian-cloud/debian-11",
labels: {
my_label: "value",
},
},
},
scratchDisks: [{
"interface": "NVME",
}],
metadata: {
foo: "bar",
},
metadataStartupScript: "echo hi > /test.txt",
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")
default_instance = gcp.compute.Instance("default",
network_interfaces=[{
"access_configs": [{}],
"network": "default",
}],
name="my-instance",
machine_type="n2-standard-2",
zone="us-central1-a",
tags=[
"foo",
"bar",
],
boot_disk={
"initialize_params": {
"image": "debian-cloud/debian-11",
"labels": {
"my_label": "value",
},
},
},
scratch_disks=[{
"interface": "NVME",
}],
metadata={
"foo": "bar",
},
metadata_startup_script="echo hi > /test.txt",
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.NewInstance(ctx, "default", &compute.InstanceArgs{
NetworkInterfaces: compute.InstanceNetworkInterfaceArray{
&compute.InstanceNetworkInterfaceArgs{
AccessConfigs: compute.InstanceNetworkInterfaceAccessConfigArray{
&compute.InstanceNetworkInterfaceAccessConfigArgs{},
},
Network: pulumi.String("default"),
},
},
Name: pulumi.String("my-instance"),
MachineType: pulumi.String("n2-standard-2"),
Zone: pulumi.String("us-central1-a"),
Tags: pulumi.StringArray{
pulumi.String("foo"),
pulumi.String("bar"),
},
BootDisk: &compute.InstanceBootDiskArgs{
InitializeParams: &compute.InstanceBootDiskInitializeParamsArgs{
Image: pulumi.String("debian-cloud/debian-11"),
Labels: pulumi.StringMap{
"my_label": pulumi.String("value"),
},
},
},
ScratchDisks: compute.InstanceScratchDiskArray{
&compute.InstanceScratchDiskArgs{
Interface: pulumi.String("NVME"),
},
},
Metadata: pulumi.StringMap{
"foo": pulumi.String("bar"),
},
MetadataStartupScript: pulumi.String("echo hi > /test.txt"),
ServiceAccount: &compute.InstanceServiceAccountArgs{
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 defaultInstance = new Gcp.Compute.Instance("default", new()
{
NetworkInterfaces = new[]
{
new Gcp.Compute.Inputs.InstanceNetworkInterfaceArgs
{
AccessConfigs = new[]
{
null,
},
Network = "default",
},
},
Name = "my-instance",
MachineType = "n2-standard-2",
Zone = "us-central1-a",
Tags = new[]
{
"foo",
"bar",
},
BootDisk = new Gcp.Compute.Inputs.InstanceBootDiskArgs
{
InitializeParams = new Gcp.Compute.Inputs.InstanceBootDiskInitializeParamsArgs
{
Image = "debian-cloud/debian-11",
Labels =
{
{ "my_label", "value" },
},
},
},
ScratchDisks = new[]
{
new Gcp.Compute.Inputs.InstanceScratchDiskArgs
{
Interface = "NVME",
},
},
Metadata =
{
{ "foo", "bar" },
},
MetadataStartupScript = "echo hi > /test.txt",
ServiceAccount = new Gcp.Compute.Inputs.InstanceServiceAccountArgs
{
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.Instance;
import com.pulumi.gcp.compute.InstanceArgs;
import com.pulumi.gcp.compute.inputs.InstanceNetworkInterfaceArgs;
import com.pulumi.gcp.compute.inputs.InstanceBootDiskArgs;
import com.pulumi.gcp.compute.inputs.InstanceBootDiskInitializeParamsArgs;
import com.pulumi.gcp.compute.inputs.InstanceScratchDiskArgs;
import com.pulumi.gcp.compute.inputs.InstanceServiceAccountArgs;
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 defaultInstance = new Instance("defaultInstance", InstanceArgs.builder()
.networkInterfaces(InstanceNetworkInterfaceArgs.builder()
.accessConfigs(InstanceNetworkInterfaceAccessConfigArgs.builder()
.build())
.network("default")
.build())
.name("my-instance")
.machineType("n2-standard-2")
.zone("us-central1-a")
.tags(
"foo",
"bar")
.bootDisk(InstanceBootDiskArgs.builder()
.initializeParams(InstanceBootDiskInitializeParamsArgs.builder()
.image("debian-cloud/debian-11")
.labels(Map.of("my_label", "value"))
.build())
.build())
.scratchDisks(InstanceScratchDiskArgs.builder()
.interface_("NVME")
.build())
.metadata(Map.of("foo", "bar"))
.metadataStartupScript("echo hi > /test.txt")
.serviceAccount(InstanceServiceAccountArgs.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
defaultInstance:
type: gcp:compute:Instance
name: default
properties:
networkInterfaces:
- accessConfigs:
- {}
network: default
name: my-instance
machineType: n2-standard-2
zone: us-central1-a
tags:
- foo
- bar
bootDisk:
initializeParams:
image: debian-cloud/debian-11
labels:
my_label: value
scratchDisks:
- interface: NVME
metadata:
foo: bar
metadataStartupScript: echo hi > /test.txt
serviceAccount:
email: ${default.email}
scopes:
- cloud-platform
The machineType and zone determine compute capacity and location. The bootDisk.initializeParams.image specifies the OS (here, Debian 11). The networkInterfaces array connects the instance to a VPC; accessConfigs grants a public IP. The serviceAccount block attaches an identity with scopes that control which Google Cloud APIs the instance can access. The metadataStartupScript runs on every boot, useful for initialization tasks.
Enable Confidential Computing for encrypted workloads
Workloads processing sensitive data can use Confidential Computing to encrypt memory contents, protecting data even from privileged access.
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 confidentialInstance = new gcp.compute.Instance("confidential_instance", {
networkInterfaces: [{
accessConfigs: [{}],
network: "default",
}],
name: "my-confidential-instance",
zone: "us-central1-a",
machineType: "n2d-standard-2",
minCpuPlatform: "AMD Milan",
confidentialInstanceConfig: {
enableConfidentialCompute: true,
confidentialInstanceType: "SEV",
},
bootDisk: {
initializeParams: {
image: "ubuntu-os-cloud/ubuntu-2204-lts",
labels: {
my_label: "value",
},
},
},
scratchDisks: [{
"interface": "NVME",
}],
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 = gcp.compute.Instance("confidential_instance",
network_interfaces=[{
"access_configs": [{}],
"network": "default",
}],
name="my-confidential-instance",
zone="us-central1-a",
machine_type="n2d-standard-2",
min_cpu_platform="AMD Milan",
confidential_instance_config={
"enable_confidential_compute": True,
"confidential_instance_type": "SEV",
},
boot_disk={
"initialize_params": {
"image": "ubuntu-os-cloud/ubuntu-2204-lts",
"labels": {
"my_label": "value",
},
},
},
scratch_disks=[{
"interface": "NVME",
}],
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.NewInstance(ctx, "confidential_instance", &compute.InstanceArgs{
NetworkInterfaces: compute.InstanceNetworkInterfaceArray{
&compute.InstanceNetworkInterfaceArgs{
AccessConfigs: compute.InstanceNetworkInterfaceAccessConfigArray{
&compute.InstanceNetworkInterfaceAccessConfigArgs{},
},
Network: pulumi.String("default"),
},
},
Name: pulumi.String("my-confidential-instance"),
Zone: pulumi.String("us-central1-a"),
MachineType: pulumi.String("n2d-standard-2"),
MinCpuPlatform: pulumi.String("AMD Milan"),
ConfidentialInstanceConfig: &compute.InstanceConfidentialInstanceConfigArgs{
EnableConfidentialCompute: pulumi.Bool(true),
ConfidentialInstanceType: pulumi.String("SEV"),
},
BootDisk: &compute.InstanceBootDiskArgs{
InitializeParams: &compute.InstanceBootDiskInitializeParamsArgs{
Image: pulumi.String("ubuntu-os-cloud/ubuntu-2204-lts"),
Labels: pulumi.StringMap{
"my_label": pulumi.String("value"),
},
},
},
ScratchDisks: compute.InstanceScratchDiskArray{
&compute.InstanceScratchDiskArgs{
Interface: pulumi.String("NVME"),
},
},
ServiceAccount: &compute.InstanceServiceAccountArgs{
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 confidentialInstance = new Gcp.Compute.Instance("confidential_instance", new()
{
NetworkInterfaces = new[]
{
new Gcp.Compute.Inputs.InstanceNetworkInterfaceArgs
{
AccessConfigs = new[]
{
null,
},
Network = "default",
},
},
Name = "my-confidential-instance",
Zone = "us-central1-a",
MachineType = "n2d-standard-2",
MinCpuPlatform = "AMD Milan",
ConfidentialInstanceConfig = new Gcp.Compute.Inputs.InstanceConfidentialInstanceConfigArgs
{
EnableConfidentialCompute = true,
ConfidentialInstanceType = "SEV",
},
BootDisk = new Gcp.Compute.Inputs.InstanceBootDiskArgs
{
InitializeParams = new Gcp.Compute.Inputs.InstanceBootDiskInitializeParamsArgs
{
Image = "ubuntu-os-cloud/ubuntu-2204-lts",
Labels =
{
{ "my_label", "value" },
},
},
},
ScratchDisks = new[]
{
new Gcp.Compute.Inputs.InstanceScratchDiskArgs
{
Interface = "NVME",
},
},
ServiceAccount = new Gcp.Compute.Inputs.InstanceServiceAccountArgs
{
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.Instance;
import com.pulumi.gcp.compute.InstanceArgs;
import com.pulumi.gcp.compute.inputs.InstanceNetworkInterfaceArgs;
import com.pulumi.gcp.compute.inputs.InstanceConfidentialInstanceConfigArgs;
import com.pulumi.gcp.compute.inputs.InstanceBootDiskArgs;
import com.pulumi.gcp.compute.inputs.InstanceBootDiskInitializeParamsArgs;
import com.pulumi.gcp.compute.inputs.InstanceScratchDiskArgs;
import com.pulumi.gcp.compute.inputs.InstanceServiceAccountArgs;
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 confidentialInstance = new Instance("confidentialInstance", InstanceArgs.builder()
.networkInterfaces(InstanceNetworkInterfaceArgs.builder()
.accessConfigs(InstanceNetworkInterfaceAccessConfigArgs.builder()
.build())
.network("default")
.build())
.name("my-confidential-instance")
.zone("us-central1-a")
.machineType("n2d-standard-2")
.minCpuPlatform("AMD Milan")
.confidentialInstanceConfig(InstanceConfidentialInstanceConfigArgs.builder()
.enableConfidentialCompute(true)
.confidentialInstanceType("SEV")
.build())
.bootDisk(InstanceBootDiskArgs.builder()
.initializeParams(InstanceBootDiskInitializeParamsArgs.builder()
.image("ubuntu-os-cloud/ubuntu-2204-lts")
.labels(Map.of("my_label", "value"))
.build())
.build())
.scratchDisks(InstanceScratchDiskArgs.builder()
.interface_("NVME")
.build())
.serviceAccount(InstanceServiceAccountArgs.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
confidentialInstance:
type: gcp:compute:Instance
name: confidential_instance
properties:
networkInterfaces:
- accessConfigs:
- {}
network: default
name: my-confidential-instance
zone: us-central1-a
machineType: n2d-standard-2
minCpuPlatform: AMD Milan
confidentialInstanceConfig:
enableConfidentialCompute: true
confidentialInstanceType: SEV
bootDisk:
initializeParams:
image: ubuntu-os-cloud/ubuntu-2204-lts
labels:
my_label: value
scratchDisks:
- interface: NVME
serviceAccount:
email: ${default.email}
scopes:
- cloud-platform
The confidentialInstanceConfig.enableConfidentialCompute activates memory encryption. The confidentialInstanceType specifies the encryption technology (SEV for AMD). The minCpuPlatform ensures the instance runs on AMD Milan processors, which support Confidential Computing. The machineType must be an N2D series (AMD-based) for this feature to work.
Beyond these examples
These snippets focus on specific instance-level features: basic instance configuration with service accounts and Confidential Computing with memory encryption. They’re intentionally minimal rather than full VM deployments.
The examples rely on pre-existing infrastructure such as service accounts with appropriate IAM roles and the default VPC network. They focus on configuring the instance rather than provisioning the surrounding infrastructure.
To keep things focused, common instance patterns are omitted, including:
- VPC and subnet configuration (networkInterfaces details)
- Shielded VM integrity verification (shieldedInstanceConfig)
- GPU accelerators and scratch disks
- Custom machine types and CPU options
These omissions are intentional: the goal is to illustrate how each instance feature is wired, not provide drop-in VM modules. See the Compute Instance resource reference for all available configuration options.
Let's create and Configure Compute Engine Instances
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Instance Updates & Resizing
machineType, minCpuPlatform, shieldedInstanceConfig, enableDisplay, or serviceAccount requires stopping the instance. Set allowStoppingForUpdate to true or set desiredStatus to TERMINATED before making these changes.lifecycle.ignore_changes on machineType to suppress these diffs.Machine Types & Custom Configurations
custom-NUMBER_OF_CPUS-AMOUNT_OF_MEMORY_MB (e.g., custom-6-20480 for 6 vCPU and 20GB RAM). There’s a 6.5 GB per CPU limit unless you add the -ext suffix for extended memory (e.g., custom-2-15360-ext for 2 vCPU and 15 GB).n2d-standard-2, set minCpuPlatform to AMD Milan, and configure confidentialInstanceConfig with enableConfidentialCompute: true and confidentialInstanceType: "SEV".Startup Scripts & Metadata
metadataStartupScript forces instance recreation when changed, while metadata.startup-script doesn’t. On import, metadataStartupScript won’t be set, causing an immediate diff/destroy-recreate if you add it. Choose based on whether you want script changes to trigger recreation.metadata.startup-script in a shell on every boot. Windows instances require different metadata keys depending on script format and timing.metadata property. Add SSH keys to your Pulumi configuration to keep them attached to your instance.Security & Specialized Features
shieldedInstanceConfig can only be used with boot images that support Shielded VM. Additionally, you must set allowStoppingForUpdate to true or desiredStatus to TERMINATED to update this configuration.onHostMaintenance set to TERMINATE. Configure scheduling.onHostMaintenance to TERMINATE when using guestAccelerators.networkPerformanceConfig requires three things: a supported machine type, the image must include GVNIC in guest-os-features, and network_interface.0.nic-type must be GVNIC.Lifecycle & Deletion
deletionProtection is enabled, pulumi destroy will fail and the provider run won’t complete. Disable deletionProtection before removing the resource.bootDisk, networkInterfaces, zone, name, project, hostname, scratchDisks, confidentialInstanceConfig, guestAccelerators, reservationAffinity, networkPerformanceConfig, params, instanceEncryptionKey, and keyRevocationActionType cannot be changed after creation.