Create Azure VM Images

The azure-native:compute:Image resource, part of the Pulumi Azure Native provider, defines a managed VM image that captures OS and data disk configurations for reuse across VM deployments. This guide focuses on four capabilities: capturing images from running VMs, converting VHD blobs and managed disks to images, including data disks in multi-volume images, and applying customer-managed encryption.

Images reference existing VMs, VHD blobs, managed disks, or snapshots. They may require DiskEncryptionSets for customer-managed encryption. The examples are intentionally small. Combine them with your own VM preparation workflows and encryption policies.

Capture an image from a running VM

Teams often need to replicate VM configurations across environments or create golden images from customized VMs. Azure allows you to capture a generalized VM as an image.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const image = new azure_native.compute.Image("image", {
    imageName: "myImage",
    location: "West US",
    resourceGroupName: "myResourceGroup",
    sourceVirtualMachine: {
        id: "/subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM",
    },
});
import pulumi
import pulumi_azure_native as azure_native

image = azure_native.compute.Image("image",
    image_name="myImage",
    location="West US",
    resource_group_name="myResourceGroup",
    source_virtual_machine={
        "id": "/subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM",
    })
package main

import (
	compute "github.com/pulumi/pulumi-azure-native-sdk/compute/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewImage(ctx, "image", &compute.ImageArgs{
			ImageName:         pulumi.String("myImage"),
			Location:          pulumi.String("West US"),
			ResourceGroupName: pulumi.String("myResourceGroup"),
			SourceVirtualMachine: &compute.SubResourceArgs{
				Id: pulumi.String("/subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var image = new AzureNative.Compute.Image("image", new()
    {
        ImageName = "myImage",
        Location = "West US",
        ResourceGroupName = "myResourceGroup",
        SourceVirtualMachine = new AzureNative.Compute.Inputs.SubResourceArgs
        {
            Id = "/subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.compute.Image;
import com.pulumi.azurenative.compute.ImageArgs;
import com.pulumi.azurenative.compute.inputs.SubResourceArgs;
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 image = new Image("image", ImageArgs.builder()
            .imageName("myImage")
            .location("West US")
            .resourceGroupName("myResourceGroup")
            .sourceVirtualMachine(SubResourceArgs.builder()
                .id("/subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM")
                .build())
            .build());

    }
}
resources:
  image:
    type: azure-native:compute:Image
    properties:
      imageName: myImage
      location: West US
      resourceGroupName: myResourceGroup
      sourceVirtualMachine:
        id: /subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM

The sourceVirtualMachine property points to an existing VM by its resource ID. The VM must be in a generalized state before capture: run sysprep on Windows or waagent deprovision on Linux. Once captured, the image can deploy identical VMs across regions or subscriptions.

Create an image from a VHD blob

Organizations migrating from on-premises or other clouds often have VHD files stored in Azure Blob Storage. These VHDs can be converted into managed images for VM deployment.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const image = new azure_native.compute.Image("image", {
    imageName: "myImage",
    location: "West US",
    resourceGroupName: "myResourceGroup",
    storageProfile: {
        osDisk: {
            blobUri: "https://mystorageaccount.blob.core.windows.net/osimages/osimage.vhd",
            osState: azure_native.compute.OperatingSystemStateTypes.Generalized,
            osType: azure_native.compute.OperatingSystemTypes.Linux,
        },
        zoneResilient: true,
    },
});
import pulumi
import pulumi_azure_native as azure_native

image = azure_native.compute.Image("image",
    image_name="myImage",
    location="West US",
    resource_group_name="myResourceGroup",
    storage_profile={
        "os_disk": {
            "blob_uri": "https://mystorageaccount.blob.core.windows.net/osimages/osimage.vhd",
            "os_state": azure_native.compute.OperatingSystemStateTypes.GENERALIZED,
            "os_type": azure_native.compute.OperatingSystemTypes.LINUX,
        },
        "zone_resilient": True,
    })
package main

import (
	compute "github.com/pulumi/pulumi-azure-native-sdk/compute/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewImage(ctx, "image", &compute.ImageArgs{
			ImageName:         pulumi.String("myImage"),
			Location:          pulumi.String("West US"),
			ResourceGroupName: pulumi.String("myResourceGroup"),
			StorageProfile: &compute.ImageStorageProfileArgs{
				OsDisk: &compute.ImageOSDiskArgs{
					BlobUri: pulumi.String("https://mystorageaccount.blob.core.windows.net/osimages/osimage.vhd"),
					OsState: compute.OperatingSystemStateTypesGeneralized,
					OsType:  compute.OperatingSystemTypesLinux,
				},
				ZoneResilient: pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var image = new AzureNative.Compute.Image("image", new()
    {
        ImageName = "myImage",
        Location = "West US",
        ResourceGroupName = "myResourceGroup",
        StorageProfile = new AzureNative.Compute.Inputs.ImageStorageProfileArgs
        {
            OsDisk = new AzureNative.Compute.Inputs.ImageOSDiskArgs
            {
                BlobUri = "https://mystorageaccount.blob.core.windows.net/osimages/osimage.vhd",
                OsState = AzureNative.Compute.OperatingSystemStateTypes.Generalized,
                OsType = AzureNative.Compute.OperatingSystemTypes.Linux,
            },
            ZoneResilient = true,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.compute.Image;
import com.pulumi.azurenative.compute.ImageArgs;
import com.pulumi.azurenative.compute.inputs.ImageStorageProfileArgs;
import com.pulumi.azurenative.compute.inputs.ImageOSDiskArgs;
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 image = new Image("image", ImageArgs.builder()
            .imageName("myImage")
            .location("West US")
            .resourceGroupName("myResourceGroup")
            .storageProfile(ImageStorageProfileArgs.builder()
                .osDisk(ImageOSDiskArgs.builder()
                    .blobUri("https://mystorageaccount.blob.core.windows.net/osimages/osimage.vhd")
                    .osState("Generalized")
                    .osType("Linux")
                    .build())
                .zoneResilient(true)
                .build())
            .build());

    }
}
resources:
  image:
    type: azure-native:compute:Image
    properties:
      imageName: myImage
      location: West US
      resourceGroupName: myResourceGroup
      storageProfile:
        osDisk:
          blobUri: https://mystorageaccount.blob.core.windows.net/osimages/osimage.vhd
          osState: Generalized
          osType: Linux
        zoneResilient: true

The storageProfile defines the OS disk source. The blobUri points to your VHD file in blob storage. The osState must be Generalized, and osType specifies Linux or Windows. The zoneResilient property enables zone-redundant storage for higher availability.

Create an image from a managed disk

When you have a managed disk containing a generalized OS, you can create an image directly from it without needing intermediate blob storage.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const image = new azure_native.compute.Image("image", {
    imageName: "myImage",
    location: "West US",
    resourceGroupName: "myResourceGroup",
    storageProfile: {
        osDisk: {
            managedDisk: {
                id: "subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk",
            },
            osState: azure_native.compute.OperatingSystemStateTypes.Generalized,
            osType: azure_native.compute.OperatingSystemTypes.Linux,
        },
        zoneResilient: true,
    },
});
import pulumi
import pulumi_azure_native as azure_native

image = azure_native.compute.Image("image",
    image_name="myImage",
    location="West US",
    resource_group_name="myResourceGroup",
    storage_profile={
        "os_disk": {
            "managed_disk": {
                "id": "subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk",
            },
            "os_state": azure_native.compute.OperatingSystemStateTypes.GENERALIZED,
            "os_type": azure_native.compute.OperatingSystemTypes.LINUX,
        },
        "zone_resilient": True,
    })
package main

import (
	compute "github.com/pulumi/pulumi-azure-native-sdk/compute/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewImage(ctx, "image", &compute.ImageArgs{
			ImageName:         pulumi.String("myImage"),
			Location:          pulumi.String("West US"),
			ResourceGroupName: pulumi.String("myResourceGroup"),
			StorageProfile: &compute.ImageStorageProfileArgs{
				OsDisk: &compute.ImageOSDiskArgs{
					ManagedDisk: &compute.SubResourceArgs{
						Id: pulumi.String("subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk"),
					},
					OsState: compute.OperatingSystemStateTypesGeneralized,
					OsType:  compute.OperatingSystemTypesLinux,
				},
				ZoneResilient: pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var image = new AzureNative.Compute.Image("image", new()
    {
        ImageName = "myImage",
        Location = "West US",
        ResourceGroupName = "myResourceGroup",
        StorageProfile = new AzureNative.Compute.Inputs.ImageStorageProfileArgs
        {
            OsDisk = new AzureNative.Compute.Inputs.ImageOSDiskArgs
            {
                ManagedDisk = new AzureNative.Compute.Inputs.SubResourceArgs
                {
                    Id = "subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk",
                },
                OsState = AzureNative.Compute.OperatingSystemStateTypes.Generalized,
                OsType = AzureNative.Compute.OperatingSystemTypes.Linux,
            },
            ZoneResilient = true,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.compute.Image;
import com.pulumi.azurenative.compute.ImageArgs;
import com.pulumi.azurenative.compute.inputs.ImageStorageProfileArgs;
import com.pulumi.azurenative.compute.inputs.ImageOSDiskArgs;
import com.pulumi.azurenative.compute.inputs.SubResourceArgs;
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 image = new Image("image", ImageArgs.builder()
            .imageName("myImage")
            .location("West US")
            .resourceGroupName("myResourceGroup")
            .storageProfile(ImageStorageProfileArgs.builder()
                .osDisk(ImageOSDiskArgs.builder()
                    .managedDisk(SubResourceArgs.builder()
                        .id("subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk")
                        .build())
                    .osState("Generalized")
                    .osType("Linux")
                    .build())
                .zoneResilient(true)
                .build())
            .build());

    }
}
resources:
  image:
    type: azure-native:compute:Image
    properties:
      imageName: myImage
      location: West US
      resourceGroupName: myResourceGroup
      storageProfile:
        osDisk:
          managedDisk:
            id: subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk
          osState: Generalized
          osType: Linux
        zoneResilient: true

The managedDisk property references an existing disk by ID. This approach is faster than blob-based images because the disk is already in Azure’s managed storage. The disk must contain a generalized OS; specialized disks retain machine-specific configuration and won’t work for multi-VM deployments.

Include data disks in an image

Applications often require both OS and data disks. Images can include multiple disks, allowing you to deploy VMs with pre-configured data volumes.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const image = new azure_native.compute.Image("image", {
    imageName: "myImage",
    location: "West US",
    resourceGroupName: "myResourceGroup",
    storageProfile: {
        dataDisks: [{
            lun: 1,
            managedDisk: {
                id: "subscriptions/{subscriptionId}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk2",
            },
        }],
        osDisk: {
            managedDisk: {
                id: "subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk",
            },
            osState: azure_native.compute.OperatingSystemStateTypes.Generalized,
            osType: azure_native.compute.OperatingSystemTypes.Linux,
        },
        zoneResilient: false,
    },
});
import pulumi
import pulumi_azure_native as azure_native

image = azure_native.compute.Image("image",
    image_name="myImage",
    location="West US",
    resource_group_name="myResourceGroup",
    storage_profile={
        "data_disks": [{
            "lun": 1,
            "managed_disk": {
                "id": "subscriptions/{subscriptionId}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk2",
            },
        }],
        "os_disk": {
            "managed_disk": {
                "id": "subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk",
            },
            "os_state": azure_native.compute.OperatingSystemStateTypes.GENERALIZED,
            "os_type": azure_native.compute.OperatingSystemTypes.LINUX,
        },
        "zone_resilient": False,
    })
package main

import (
	compute "github.com/pulumi/pulumi-azure-native-sdk/compute/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewImage(ctx, "image", &compute.ImageArgs{
			ImageName:         pulumi.String("myImage"),
			Location:          pulumi.String("West US"),
			ResourceGroupName: pulumi.String("myResourceGroup"),
			StorageProfile: &compute.ImageStorageProfileArgs{
				DataDisks: compute.ImageDataDiskArray{
					&compute.ImageDataDiskArgs{
						Lun: pulumi.Int(1),
						ManagedDisk: &compute.SubResourceArgs{
							Id: pulumi.String("subscriptions/{subscriptionId}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk2"),
						},
					},
				},
				OsDisk: &compute.ImageOSDiskArgs{
					ManagedDisk: &compute.SubResourceArgs{
						Id: pulumi.String("subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk"),
					},
					OsState: compute.OperatingSystemStateTypesGeneralized,
					OsType:  compute.OperatingSystemTypesLinux,
				},
				ZoneResilient: pulumi.Bool(false),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var image = new AzureNative.Compute.Image("image", new()
    {
        ImageName = "myImage",
        Location = "West US",
        ResourceGroupName = "myResourceGroup",
        StorageProfile = new AzureNative.Compute.Inputs.ImageStorageProfileArgs
        {
            DataDisks = new[]
            {
                new AzureNative.Compute.Inputs.ImageDataDiskArgs
                {
                    Lun = 1,
                    ManagedDisk = new AzureNative.Compute.Inputs.SubResourceArgs
                    {
                        Id = "subscriptions/{subscriptionId}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk2",
                    },
                },
            },
            OsDisk = new AzureNative.Compute.Inputs.ImageOSDiskArgs
            {
                ManagedDisk = new AzureNative.Compute.Inputs.SubResourceArgs
                {
                    Id = "subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk",
                },
                OsState = AzureNative.Compute.OperatingSystemStateTypes.Generalized,
                OsType = AzureNative.Compute.OperatingSystemTypes.Linux,
            },
            ZoneResilient = false,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.compute.Image;
import com.pulumi.azurenative.compute.ImageArgs;
import com.pulumi.azurenative.compute.inputs.ImageStorageProfileArgs;
import com.pulumi.azurenative.compute.inputs.ImageOSDiskArgs;
import com.pulumi.azurenative.compute.inputs.SubResourceArgs;
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 image = new Image("image", ImageArgs.builder()
            .imageName("myImage")
            .location("West US")
            .resourceGroupName("myResourceGroup")
            .storageProfile(ImageStorageProfileArgs.builder()
                .dataDisks(ImageDataDiskArgs.builder()
                    .lun(1)
                    .managedDisk(SubResourceArgs.builder()
                        .id("subscriptions/{subscriptionId}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk2")
                        .build())
                    .build())
                .osDisk(ImageOSDiskArgs.builder()
                    .managedDisk(SubResourceArgs.builder()
                        .id("subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk")
                        .build())
                    .osState("Generalized")
                    .osType("Linux")
                    .build())
                .zoneResilient(false)
                .build())
            .build());

    }
}
resources:
  image:
    type: azure-native:compute:Image
    properties:
      imageName: myImage
      location: West US
      resourceGroupName: myResourceGroup
      storageProfile:
        dataDisks:
          - lun: 1
            managedDisk:
              id: subscriptions/{subscriptionId}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk2
        osDisk:
          managedDisk:
            id: subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myManagedDisk
          osState: Generalized
          osType: Linux
        zoneResilient: false

The dataDisks array adds additional volumes to the image. Each data disk requires a lun (Logical Unit Number) that identifies its attachment point. When you deploy a VM from this image, both OS and data disks are created automatically, preserving your application’s storage layout.

Encrypt an image with DiskEncryptionSet

Compliance requirements often mandate encryption at rest using customer-managed keys. DiskEncryptionSet allows you to encrypt images with keys from Azure Key Vault.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const image = new azure_native.compute.Image("image", {
    imageName: "myImage",
    location: "West US",
    resourceGroupName: "myResourceGroup",
    storageProfile: {
        osDisk: {
            blobUri: "https://mystorageaccount.blob.core.windows.net/osimages/osimage.vhd",
            diskEncryptionSet: {
                id: "/subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/diskEncryptionSets/{existing-diskEncryptionSet-name}",
            },
            osState: azure_native.compute.OperatingSystemStateTypes.Generalized,
            osType: azure_native.compute.OperatingSystemTypes.Linux,
        },
    },
});
import pulumi
import pulumi_azure_native as azure_native

image = azure_native.compute.Image("image",
    image_name="myImage",
    location="West US",
    resource_group_name="myResourceGroup",
    storage_profile={
        "os_disk": {
            "blob_uri": "https://mystorageaccount.blob.core.windows.net/osimages/osimage.vhd",
            "disk_encryption_set": {
                "id": "/subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/diskEncryptionSets/{existing-diskEncryptionSet-name}",
            },
            "os_state": azure_native.compute.OperatingSystemStateTypes.GENERALIZED,
            "os_type": azure_native.compute.OperatingSystemTypes.LINUX,
        },
    })
package main

import (
	compute "github.com/pulumi/pulumi-azure-native-sdk/compute/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewImage(ctx, "image", &compute.ImageArgs{
			ImageName:         pulumi.String("myImage"),
			Location:          pulumi.String("West US"),
			ResourceGroupName: pulumi.String("myResourceGroup"),
			StorageProfile: &compute.ImageStorageProfileArgs{
				OsDisk: &compute.ImageOSDiskArgs{
					BlobUri: pulumi.String("https://mystorageaccount.blob.core.windows.net/osimages/osimage.vhd"),
					DiskEncryptionSet: &compute.DiskEncryptionSetParametersArgs{
						Id: pulumi.String("/subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/diskEncryptionSets/{existing-diskEncryptionSet-name}"),
					},
					OsState: compute.OperatingSystemStateTypesGeneralized,
					OsType:  compute.OperatingSystemTypesLinux,
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var image = new AzureNative.Compute.Image("image", new()
    {
        ImageName = "myImage",
        Location = "West US",
        ResourceGroupName = "myResourceGroup",
        StorageProfile = new AzureNative.Compute.Inputs.ImageStorageProfileArgs
        {
            OsDisk = new AzureNative.Compute.Inputs.ImageOSDiskArgs
            {
                BlobUri = "https://mystorageaccount.blob.core.windows.net/osimages/osimage.vhd",
                DiskEncryptionSet = new AzureNative.Compute.Inputs.DiskEncryptionSetParametersArgs
                {
                    Id = "/subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/diskEncryptionSets/{existing-diskEncryptionSet-name}",
                },
                OsState = AzureNative.Compute.OperatingSystemStateTypes.Generalized,
                OsType = AzureNative.Compute.OperatingSystemTypes.Linux,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.compute.Image;
import com.pulumi.azurenative.compute.ImageArgs;
import com.pulumi.azurenative.compute.inputs.ImageStorageProfileArgs;
import com.pulumi.azurenative.compute.inputs.ImageOSDiskArgs;
import com.pulumi.azurenative.compute.inputs.DiskEncryptionSetParametersArgs;
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 image = new Image("image", ImageArgs.builder()
            .imageName("myImage")
            .location("West US")
            .resourceGroupName("myResourceGroup")
            .storageProfile(ImageStorageProfileArgs.builder()
                .osDisk(ImageOSDiskArgs.builder()
                    .blobUri("https://mystorageaccount.blob.core.windows.net/osimages/osimage.vhd")
                    .diskEncryptionSet(DiskEncryptionSetParametersArgs.builder()
                        .id("/subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/diskEncryptionSets/{existing-diskEncryptionSet-name}")
                        .build())
                    .osState("Generalized")
                    .osType("Linux")
                    .build())
                .build())
            .build());

    }
}
resources:
  image:
    type: azure-native:compute:Image
    properties:
      imageName: myImage
      location: West US
      resourceGroupName: myResourceGroup
      storageProfile:
        osDisk:
          blobUri: https://mystorageaccount.blob.core.windows.net/osimages/osimage.vhd
          diskEncryptionSet:
            id: /subscriptions/{subscription-id}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/diskEncryptionSets/{existing-diskEncryptionSet-name}
          osState: Generalized
          osType: Linux

The diskEncryptionSet property references a DiskEncryptionSet resource that wraps your Key Vault key. When VMs are deployed from this image, their disks inherit the encryption configuration. The DiskEncryptionSet must exist before image creation and requires permissions to access your Key Vault.

Beyond these examples

These snippets focus on specific image-level features: image sources (VMs, blobs, managed disks, snapshots), multi-disk images with data volumes, and customer-managed encryption with DiskEncryptionSet. They’re intentionally minimal rather than full VM deployment workflows.

The examples may reference pre-existing infrastructure such as VMs in generalized state, VHD blobs in storage accounts, managed disks and snapshots, and DiskEncryptionSets with Key Vault access. They focus on configuring the image rather than provisioning the source resources.

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

  • HyperV generation specification (hyperVGeneration)
  • Extended location for edge deployments
  • Resource tagging for organization
  • Zone-resilient storage configuration details

These omissions are intentional: the goal is to illustrate how each image source is wired, not provide drop-in VM templates. See the Image resource reference for all available configuration options.

Let's create Azure VM Images

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Image Sources & Creation
What sources can I use to create an Azure VM image?
You can create images from four sources: VHD blobs in storage accounts (using blobUri), managed disks (using managedDisk), snapshots (using snapshot), or existing virtual machines (using sourceVirtualMachine).
How do I create an image from an existing virtual machine?
Use the sourceVirtualMachine property with the VM’s resource ID instead of configuring storageProfile. This captures the entire VM configuration as an image.
How do I include data disks in my image?
Configure storageProfile.dataDisks with an array of disks, specifying the source (blobUri, managedDisk, or snapshot) and the lun (logical unit number) for each disk.
Why am I getting an error that the destination VHD already exists?
When using a source image, the destination virtual hard drive must not already exist. Ensure the destination location is empty before creating the image.
Configuration & Requirements
When do I need to specify hyperVGeneration?
You must specify hyperVGeneration when creating an image from a blob source. For managed resources like disks or snapshots, you may need to specify it if Azure cannot deduce the value from the source.
What osState should I use for my image?
Use Generalized for images that have been prepared with sysprep (Windows) or waagent (Linux) to remove machine-specific information. All provided examples use Generalized.
How do I encrypt my VM image?
Specify diskEncryptionSet in the osDisk configuration with the resource ID of an existing DiskEncryptionSet. This applies to images created from blobs, managed disks, or snapshots.
Limitations & Properties
What properties can't be changed after creating an image?
The location, imageName, and resourceGroupName properties are immutable and cannot be changed after the image is created.
What does zoneResilient mean and when should I use it?
Setting zoneResilient to true makes the image available across availability zones in the region. Use this for high-availability scenarios where VMs may be deployed across multiple zones.
How do I use a different Azure API version for this resource?
Generate a local SDK package using the CLI command pulumi package add azure-native compute [ApiVersion]. Available versions include 2022-08-01, 2022-11-01, 2023-03-01, 2023-07-01, 2023-09-01, 2024-03-01, 2024-07-01, and 2025-04-01.

Using a different cloud?

Explore compute guides for other cloud providers: