The gcp:compute/image:Image resource, part of the Pulumi GCP provider, defines a Compute Engine image: the source disk or snapshot to capture, OS features to enable, and where to store the image data. This guide focuses on three capabilities: creating images from persistent disks, configuring guest OS features, and controlling storage location.
Images are created from existing persistent disks, snapshots, or other images. The examples reference disks created from public base images like debian-12. The examples are intentionally small. Combine them with your own disk preparation workflows and image management strategies.
Create an image from a persistent disk
Most custom image workflows start by creating a disk from a base OS image, customizing it, and capturing that disk 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
At runtime, GCP captures the disk’s contents and creates an image you can use to launch new instances. The sourceDisk property points to the disk to capture. The name property sets the image identifier. The example uses getImage to find the latest Debian 12 image, creates a disk from it, and then captures that disk as a custom image.
Enable guest OS features for specialized workloads
Workloads requiring UEFI boot, multi-queue SCSI, or confidential computing need specific OS-level capabilities declared at image creation time.
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 OS supports. UEFI_COMPATIBLE enables UEFI boot mode. VIRTIO_SCSI_MULTIQUEUE improves disk I/O performance. GVNIC enables Google Virtual NIC for better networking. SEV_CAPABLE and SEV_LIVE_MIGRATABLE_V2 enable AMD Secure Encrypted Virtualization for confidential computing. These features must match what the OS actually supports; declaring unsupported features causes boot failures.
Control image storage location for regional access
Organizations with regional compliance or performance requirements can specify where GCP stores 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-regions. Regional storage (like “us-central1”) keeps data in one region for compliance. Multi-regional storage (like “us”) replicates across regions for availability. Without this property, GCP chooses storage 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 management workflows.
The examples rely on pre-existing infrastructure such as persistent disks to capture as images, and base OS images from public projects like debian-cloud or ubuntu-os-cloud. They focus on configuring the image rather than the disk preparation that precedes it.
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)
- Creating images from other images (sourceImage)
- Raw disk imports from Cloud Storage (rawDisk)
These omissions are intentional: the goal is to illustrate how each image feature is wired, not provide drop-in image management 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 Creation & Sources
sourceDisk (existing disk), sourceImage (another image URL), sourceSnapshot (snapshot URL), or rawDisk.source (raw disk). These options are mutually exclusive.sourceDisk property to the disk’s ID. The disk must exist in the same project.sourceSnapshot property with the snapshot’s URL. This is one of the four mutually exclusive source options.Naming & Validation
a-z?.Immutability & Updates
name, sourceDisk, sourceImage, sourceSnapshot, diskSizeGb, guestOsFeatures, storageLocations, family, and all encryption key properties. Changing these forces resource replacement.labels field is non-authoritative and only manages labels in your configuration. To see all labels on the resource (including those set by other clients), use effectiveLabels.Encryption & Security
sourceDiskEncryptionKey, sourceImageEncryptionKey, or sourceSnapshotEncryptionKey. If you encrypt the image itself with imageEncryptionKey, you must provide the same key when using the image later.Configuration & Features
UEFI_COMPATIBLE, VIRTIO_SCSI_MULTIQUEUE, GVNIC, SEV_CAPABLE, and SEV_LIVE_MIGRATABLE_V2. Configure these in the guestOsFeatures array.storageLocations to specify regional (e.g., us-central1) or multi-regional storage locations. This property is required and immutable.family property) groups related images and always returns the latest non-deprecated image. This lets you create disks using a family name instead of a specific image name.