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 are the different ways to create a VM image?

You can create images from four sources:

  1. Blob VHD - Use storageProfile.osDisk.blobUri
  2. Managed disk - Use storageProfile.osDisk.managedDisk
  3. Snapshot - Use storageProfile.osDisk.snapshot
  4. Existing VM - Use sourceVirtualMachine.id
Why am I getting an error about the destination VHD already existing?
When creating an image from a source, the destination virtual hard drive must not already exist. Ensure you’re using a unique destination or delete the existing VHD first.
What OS state is required when creating an image?
The OS disk must have osState set to Generalized, indicating the source VM has been generalized before image creation.
Encryption & Security
How do I encrypt a VM image?
Configure storageProfile.osDisk.diskEncryptionSet.id to reference an existing DiskEncryptionSet resource in your subscription.
Configuration & Requirements
When do I need to specify hyperVGeneration?
If your image source is a blob VHD, you may need to specify hyperVGeneration if it cannot be automatically deduced from the source.
What properties can't be changed after creating an image?
The location, imageName, and resourceGroupName properties are immutable and cannot be modified after creation.
Advanced Features
What does zoneResilient do?
Setting storageProfile.zoneResilient to true enables zone redundancy, making the image available across availability zones in the region.
How do I include data disks in my image?
Add entries to storageProfile.dataDisks with a lun number and disk source (blobUri, managedDisk, or snapshot).

Using a different cloud?

Explore compute guides for other cloud providers: