The gcp:compute/image:Image resource, part of the Pulumi GCP provider, defines a Compute Engine image: its source disk or snapshot, guest OS features, and storage location. This guide focuses on three capabilities: creating images from persistent disks, configuring guest OS features, and controlling storage locations.
Images are created from existing disks, snapshots, or other images. The examples reference public debian-cloud images as starting points. The examples are intentionally small. Combine them with your own disk customization workflows and organizational policies.
Create an image from a persistent disk
Teams building custom VM images typically create a disk from a public image, customize it, and capture it as a reusable image.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const debian = gcp.compute.getImage({
family: "debian-12",
project: "debian-cloud",
});
const persistent = new gcp.compute.Disk("persistent", {
name: "example-disk",
image: debian.then(debian => debian.selfLink),
size: 10,
type: "pd-ssd",
zone: "us-central1-a",
});
const example = new gcp.compute.Image("example", {
name: "example-image",
sourceDisk: persistent.id,
});
import pulumi
import pulumi_gcp as gcp
debian = gcp.compute.get_image(family="debian-12",
project="debian-cloud")
persistent = gcp.compute.Disk("persistent",
name="example-disk",
image=debian.self_link,
size=10,
type="pd-ssd",
zone="us-central1-a")
example = gcp.compute.Image("example",
name="example-image",
source_disk=persistent.id)
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 {
debian, err := compute.LookupImage(ctx, &compute.LookupImageArgs{
Family: pulumi.StringRef("debian-12"),
Project: pulumi.StringRef("debian-cloud"),
}, nil)
if err != nil {
return err
}
persistent, err := compute.NewDisk(ctx, "persistent", &compute.DiskArgs{
Name: pulumi.String("example-disk"),
Image: pulumi.String(debian.SelfLink),
Size: pulumi.Int(10),
Type: pulumi.String("pd-ssd"),
Zone: pulumi.String("us-central1-a"),
})
if err != nil {
return err
}
_, err = compute.NewImage(ctx, "example", &compute.ImageArgs{
Name: pulumi.String("example-image"),
SourceDisk: persistent.ID(),
})
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 debian = Gcp.Compute.GetImage.Invoke(new()
{
Family = "debian-12",
Project = "debian-cloud",
});
var persistent = new Gcp.Compute.Disk("persistent", new()
{
Name = "example-disk",
Image = debian.Apply(getImageResult => getImageResult.SelfLink),
Size = 10,
Type = "pd-ssd",
Zone = "us-central1-a",
});
var example = new Gcp.Compute.Image("example", new()
{
Name = "example-image",
SourceDisk = persistent.Id,
});
});
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.Disk;
import com.pulumi.gcp.compute.DiskArgs;
import com.pulumi.gcp.compute.Image;
import com.pulumi.gcp.compute.ImageArgs;
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 debian = ComputeFunctions.getImage(GetImageArgs.builder()
.family("debian-12")
.project("debian-cloud")
.build());
var persistent = new Disk("persistent", DiskArgs.builder()
.name("example-disk")
.image(debian.selfLink())
.size(10)
.type("pd-ssd")
.zone("us-central1-a")
.build());
var example = new Image("example", ImageArgs.builder()
.name("example-image")
.sourceDisk(persistent.id())
.build());
}
}
resources:
persistent:
type: gcp:compute:Disk
properties:
name: example-disk
image: ${debian.selfLink}
size: 10
type: pd-ssd
zone: us-central1-a
example:
type: gcp:compute:Image
properties:
name: example-image
sourceDisk: ${persistent.id}
variables:
debian:
fn::invoke:
function: gcp:compute:getImage
arguments:
family: debian-12
project: debian-cloud
The sourceDisk property points to the disk you want to image. The example creates a disk from the latest Debian 12 image using getImage, but in practice you’d customize the disk before imaging it. Once created, the image becomes a global resource that can launch instances in any zone.
Enable guest OS features for specialized workloads
Some workloads require specific OS-level capabilities like UEFI boot, multi-queue SCSI, or confidential computing features.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const debian = gcp.compute.getImage({
family: "debian-12",
project: "debian-cloud",
});
const persistent = new gcp.compute.Disk("persistent", {
name: "example-disk",
image: debian.then(debian => debian.selfLink),
size: 10,
type: "pd-ssd",
zone: "us-central1-a",
});
const example = new gcp.compute.Image("example", {
name: "example-image",
sourceDisk: persistent.id,
guestOsFeatures: [
{
type: "UEFI_COMPATIBLE",
},
{
type: "VIRTIO_SCSI_MULTIQUEUE",
},
{
type: "GVNIC",
},
{
type: "SEV_CAPABLE",
},
{
type: "SEV_LIVE_MIGRATABLE_V2",
},
],
});
import pulumi
import pulumi_gcp as gcp
debian = gcp.compute.get_image(family="debian-12",
project="debian-cloud")
persistent = gcp.compute.Disk("persistent",
name="example-disk",
image=debian.self_link,
size=10,
type="pd-ssd",
zone="us-central1-a")
example = gcp.compute.Image("example",
name="example-image",
source_disk=persistent.id,
guest_os_features=[
{
"type": "UEFI_COMPATIBLE",
},
{
"type": "VIRTIO_SCSI_MULTIQUEUE",
},
{
"type": "GVNIC",
},
{
"type": "SEV_CAPABLE",
},
{
"type": "SEV_LIVE_MIGRATABLE_V2",
},
])
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 {
debian, err := compute.LookupImage(ctx, &compute.LookupImageArgs{
Family: pulumi.StringRef("debian-12"),
Project: pulumi.StringRef("debian-cloud"),
}, nil)
if err != nil {
return err
}
persistent, err := compute.NewDisk(ctx, "persistent", &compute.DiskArgs{
Name: pulumi.String("example-disk"),
Image: pulumi.String(debian.SelfLink),
Size: pulumi.Int(10),
Type: pulumi.String("pd-ssd"),
Zone: pulumi.String("us-central1-a"),
})
if err != nil {
return err
}
_, err = compute.NewImage(ctx, "example", &compute.ImageArgs{
Name: pulumi.String("example-image"),
SourceDisk: persistent.ID(),
GuestOsFeatures: compute.ImageGuestOsFeatureArray{
&compute.ImageGuestOsFeatureArgs{
Type: pulumi.String("UEFI_COMPATIBLE"),
},
&compute.ImageGuestOsFeatureArgs{
Type: pulumi.String("VIRTIO_SCSI_MULTIQUEUE"),
},
&compute.ImageGuestOsFeatureArgs{
Type: pulumi.String("GVNIC"),
},
&compute.ImageGuestOsFeatureArgs{
Type: pulumi.String("SEV_CAPABLE"),
},
&compute.ImageGuestOsFeatureArgs{
Type: pulumi.String("SEV_LIVE_MIGRATABLE_V2"),
},
},
})
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 debian = Gcp.Compute.GetImage.Invoke(new()
{
Family = "debian-12",
Project = "debian-cloud",
});
var persistent = new Gcp.Compute.Disk("persistent", new()
{
Name = "example-disk",
Image = debian.Apply(getImageResult => getImageResult.SelfLink),
Size = 10,
Type = "pd-ssd",
Zone = "us-central1-a",
});
var example = new Gcp.Compute.Image("example", new()
{
Name = "example-image",
SourceDisk = persistent.Id,
GuestOsFeatures = new[]
{
new Gcp.Compute.Inputs.ImageGuestOsFeatureArgs
{
Type = "UEFI_COMPATIBLE",
},
new Gcp.Compute.Inputs.ImageGuestOsFeatureArgs
{
Type = "VIRTIO_SCSI_MULTIQUEUE",
},
new Gcp.Compute.Inputs.ImageGuestOsFeatureArgs
{
Type = "GVNIC",
},
new Gcp.Compute.Inputs.ImageGuestOsFeatureArgs
{
Type = "SEV_CAPABLE",
},
new Gcp.Compute.Inputs.ImageGuestOsFeatureArgs
{
Type = "SEV_LIVE_MIGRATABLE_V2",
},
},
});
});
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.Disk;
import com.pulumi.gcp.compute.DiskArgs;
import com.pulumi.gcp.compute.Image;
import com.pulumi.gcp.compute.ImageArgs;
import com.pulumi.gcp.compute.inputs.ImageGuestOsFeatureArgs;
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 debian = ComputeFunctions.getImage(GetImageArgs.builder()
.family("debian-12")
.project("debian-cloud")
.build());
var persistent = new Disk("persistent", DiskArgs.builder()
.name("example-disk")
.image(debian.selfLink())
.size(10)
.type("pd-ssd")
.zone("us-central1-a")
.build());
var example = new Image("example", ImageArgs.builder()
.name("example-image")
.sourceDisk(persistent.id())
.guestOsFeatures(
ImageGuestOsFeatureArgs.builder()
.type("UEFI_COMPATIBLE")
.build(),
ImageGuestOsFeatureArgs.builder()
.type("VIRTIO_SCSI_MULTIQUEUE")
.build(),
ImageGuestOsFeatureArgs.builder()
.type("GVNIC")
.build(),
ImageGuestOsFeatureArgs.builder()
.type("SEV_CAPABLE")
.build(),
ImageGuestOsFeatureArgs.builder()
.type("SEV_LIVE_MIGRATABLE_V2")
.build())
.build());
}
}
resources:
persistent:
type: gcp:compute:Disk
properties:
name: example-disk
image: ${debian.selfLink}
size: 10
type: pd-ssd
zone: us-central1-a
example:
type: gcp:compute:Image
properties:
name: example-image
sourceDisk: ${persistent.id}
guestOsFeatures:
- type: UEFI_COMPATIBLE
- type: VIRTIO_SCSI_MULTIQUEUE
- type: GVNIC
- type: SEV_CAPABLE
- type: SEV_LIVE_MIGRATABLE_V2
variables:
debian:
fn::invoke:
function: gcp:compute:getImage
arguments:
family: debian-12
project: debian-cloud
The guestOsFeatures array declares capabilities the image supports. UEFI_COMPATIBLE enables UEFI boot mode, VIRTIO_SCSI_MULTIQUEUE improves I/O performance through parallel queues, and SEV_CAPABLE enables AMD confidential computing. Instance types must support the declared features.
Control image storage location for compliance
Organizations with data residency requirements can specify where GCP stores the image data.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const debian = gcp.compute.getImage({
family: "debian-12",
project: "debian-cloud",
});
const persistent = new gcp.compute.Disk("persistent", {
name: "example-disk",
image: debian.then(debian => debian.selfLink),
size: 10,
type: "pd-ssd",
zone: "us-central1-a",
});
const example = new gcp.compute.Image("example", {
name: "example-sl-image",
sourceDisk: persistent.id,
storageLocations: ["us-central1"],
});
import pulumi
import pulumi_gcp as gcp
debian = gcp.compute.get_image(family="debian-12",
project="debian-cloud")
persistent = gcp.compute.Disk("persistent",
name="example-disk",
image=debian.self_link,
size=10,
type="pd-ssd",
zone="us-central1-a")
example = gcp.compute.Image("example",
name="example-sl-image",
source_disk=persistent.id,
storage_locations=["us-central1"])
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 {
debian, err := compute.LookupImage(ctx, &compute.LookupImageArgs{
Family: pulumi.StringRef("debian-12"),
Project: pulumi.StringRef("debian-cloud"),
}, nil)
if err != nil {
return err
}
persistent, err := compute.NewDisk(ctx, "persistent", &compute.DiskArgs{
Name: pulumi.String("example-disk"),
Image: pulumi.String(debian.SelfLink),
Size: pulumi.Int(10),
Type: pulumi.String("pd-ssd"),
Zone: pulumi.String("us-central1-a"),
})
if err != nil {
return err
}
_, err = compute.NewImage(ctx, "example", &compute.ImageArgs{
Name: pulumi.String("example-sl-image"),
SourceDisk: persistent.ID(),
StorageLocations: pulumi.StringArray{
pulumi.String("us-central1"),
},
})
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 debian = Gcp.Compute.GetImage.Invoke(new()
{
Family = "debian-12",
Project = "debian-cloud",
});
var persistent = new Gcp.Compute.Disk("persistent", new()
{
Name = "example-disk",
Image = debian.Apply(getImageResult => getImageResult.SelfLink),
Size = 10,
Type = "pd-ssd",
Zone = "us-central1-a",
});
var example = new Gcp.Compute.Image("example", new()
{
Name = "example-sl-image",
SourceDisk = persistent.Id,
StorageLocations = new[]
{
"us-central1",
},
});
});
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.Disk;
import com.pulumi.gcp.compute.DiskArgs;
import com.pulumi.gcp.compute.Image;
import com.pulumi.gcp.compute.ImageArgs;
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 debian = ComputeFunctions.getImage(GetImageArgs.builder()
.family("debian-12")
.project("debian-cloud")
.build());
var persistent = new Disk("persistent", DiskArgs.builder()
.name("example-disk")
.image(debian.selfLink())
.size(10)
.type("pd-ssd")
.zone("us-central1-a")
.build());
var example = new Image("example", ImageArgs.builder()
.name("example-sl-image")
.sourceDisk(persistent.id())
.storageLocations("us-central1")
.build());
}
}
resources:
persistent:
type: gcp:compute:Disk
properties:
name: example-disk
image: ${debian.selfLink}
size: 10
type: pd-ssd
zone: us-central1-a
example:
type: gcp:compute:Image
properties:
name: example-sl-image
sourceDisk: ${persistent.id}
storageLocations:
- us-central1
variables:
debian:
fn::invoke:
function: gcp:compute:getImage
arguments:
family: debian-12
project: debian-cloud
The storageLocations property pins the image to specific regions or multi-regional locations. This example stores the image in us-central1. Without this property, GCP chooses the storage location automatically.
Beyond these examples
These snippets focus on specific image-level features: disk-based image creation, guest OS feature configuration, and storage location control. They’re intentionally minimal rather than full image build pipelines.
The examples reference pre-existing infrastructure such as public images from debian-cloud project and zones for disk creation. They focus on configuring the image rather than the disk customization workflow that typically precedes image creation.
To keep things focused, common image patterns are omitted, including:
- Image families for versioning (family property)
- Customer-supplied encryption keys (imageEncryptionKey)
- Creating images from snapshots (sourceSnapshot)
- Raw disk imports (rawDisk property)
These omissions are intentional: the goal is to illustrate how each image feature is wired, not provide drop-in image build modules. See the Compute Engine Image resource reference for all available configuration options.
Let's create GCP Compute Images
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Image Sources & Creation
sourceDisk (from an existing disk), sourceImage (from another image), sourceSnapshot (from a snapshot), or rawDisk.source (from a raw disk). These options are mutually exclusive.sourceDiskEncryptionKey, sourceImageEncryptionKey, or sourceSnapshotEncryptionKey) if your source is protected by a customer-supplied encryption key. Use imageEncryptionKey to encrypt the resulting image.sourceDisk to the disk’s ID. The examples show creating a persistent disk from a public image, then using that disk’s ID as the sourceDisk for the new image.Configuration & Constraints
name, diskSizeGb, guestOsFeatures, licenses, project, storageLocations, family, description, all source properties (sourceDisk, sourceImage, sourceSnapshot, rawDisk), and all encryption keys. Changing these will force resource replacement.name must be 1-63 characters long and match the regex a-z?. This means the first character must be a lowercase letter, following characters can be dashes, lowercase letters, or digits, and the last character cannot be a dash.storageLocations to specify regional or multi-regional Cloud Storage bucket locations (e.g., ["us-central1"]). This property is required and immutable.Guest OS Features
UEFI_COMPATIBLE, VIRTIO_SCSI_MULTIQUEUE, GVNIC, SEV_CAPABLE, and SEV_LIVE_MIGRATABLE_V2 using the guestOsFeatures property. This property is required and immutable.Labels & Metadata
labels field is non-authoritative and only manages labels present in your configuration. To see all labels on the resource (including those set by other clients or services), use effectiveLabels.