The gcp:compute/instance:Instance resource, part of the Pulumi GCP provider, provisions a GCE VM instance: its machine type, boot disk, network placement, and security features. This guide focuses on two capabilities: basic VM configuration with service accounts and confidential computing for memory encryption.
A GCE instance doesn’t exist in isolation. It requires a boot disk image, network configuration, and service account for GCP service access. The examples assume the default VPC exists and reference service accounts created separately. They’re intentionally small demonstrations of instance-level features rather than complete infrastructure stacks.
Launch an instance with service account and startup script
Most deployments start by defining a VM with a boot disk, machine type, and network configuration. Service accounts grant permissions to interact with GCP services, while startup scripts automate initial setup tasks.
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
At launch, GCE provisions the VM in the specified zone with the chosen machine type. The bootDisk defines the OS image, here pulling from debian-cloud’s public image family. The networkInterfaces property attaches the instance to the default VPC and assigns an ephemeral external IP through accessConfigs. The serviceAccount property grants the VM permissions based on the specified scopes (cloud-platform provides full API access). The metadataStartupScript runs on first boot, executing the provided shell commands.
Enable confidential computing with SEV encryption
Workloads handling sensitive data can use Confidential VM to encrypt memory contents through AMD SEV (Secure Encrypted Virtualization). This protects data while it’s being processed, not just at rest or in transit.
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
Confidential computing requires specific hardware. The machineType must be an N2D series instance (AMD EPYC processors), and minCpuPlatform must specify “AMD Milan” or newer. The confidentialInstanceConfig enables memory encryption by setting enableConfidentialCompute to true and specifying the encryption type (SEV). This configuration ensures data remains encrypted in memory even from the hypervisor.
Beyond These Examples
These snippets focus on specific instance-level features: boot disk configuration and image selection, service account attachment and IAM scopes, startup scripts for bootstrapping, 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 permissions and the default VPC network (or custom VPC if needed). They focus on configuring the instance rather than provisioning the surrounding networking and IAM.
To keep things focused, common instance patterns are omitted, including:
- Shielded VM configuration (shieldedInstanceConfig)
- GPU accelerators (guestAccelerators)
- Custom VPC and subnet configuration
- Additional disk attachment (attachedDisks)
- Network tags and firewall integration
- Instance resizing controls (allowStoppingForUpdate)
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.
Frequently Asked Questions
Instance Updates & Lifecycle
allowStoppingForUpdate to true or manually set desiredStatus to TERMINATED before updating machineType, minCpuPlatform, serviceAccount, shieldedInstanceConfig, or enableDisplay.deletionProtection enabled cannot be deleted. Disable deletionProtection before running pulumi destroy.name, zone, bootDisk, networkInterfaces, confidentialInstanceConfig, guestAccelerators, project, reservationAffinity, scratchDisks, hostname, instanceEncryptionKey, keyRevocationActionType, networkPerformanceConfig, or params after creation. Changing these forces a new instance.Machine Types & Compute Resources
machineType, but you must set allowStoppingForUpdate to true for the change to succeed.lifecycle.ignore_changes on machineType to suppress these diffs.-ext suffix for extended memory, such as custom-2-15360-ext for 2 vCPUs and 15GB of memory.GPUs & Accelerators
scheduling.onHostMaintenance set to TERMINATE. They won’t function with other maintenance options.Security & Confidential Computing
n2d-standard-2), set minCpuPlatform to AMD Milan, and configure confidentialInstanceConfig with enableConfidentialCompute: true and confidentialInstanceType: 'SEV'.shieldedInstanceConfig only works with boot images that have shielded VM support. Verify your image is on the compatible list.Metadata & Startup Scripts
metadata.startup-script can be updated without recreating the instance, while metadataStartupScript forces recreation on any change. The two mechanisms cannot be used simultaneously.metadataStartupScript isn’t set in state, causing an immediate diff. You’ll need to modify your state file if you want to specify this property after importing.Networking & Performance
networkPerformanceConfig requires three conditions: a supported machineType, an image with GVNIC in guest-os-features, and network_interface.0.nic-type set to GVNIC. Verify all three are configured correctly.Ready to get started?
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Create free account