top of page
  • Writer's pictureNathan

Azure VM Image Builder - Part 1

Updated: Feb 10

Azure VM Image Builder is a managed service from Microsoft that lets you easily build, customize, and distribute your own custom VM images. All you need to do is create a configuration file which describes the image that you want to build and then you must submit that configuration to the Azure VM Image Builder service. The service will take care of the rest. Azure VM Image Builder is built on HashiCorp’s Packer software.

Now that the intro is out of the way, I’m just going to jump right into the technical details, as this blog post is going to be lengthy enough already.


Image Templates

The “configuration” file that I referenced earlier is officially called an Image Template. Originally, the only option available was to create your Image Templates using Infrastructure as Code (IaC) such as Bicep, ARM Templates, or Terraform. Here is a link to Microsoft’s IaC reference page for Image Templates which lists all of the possible options. However, recently Microsoft has added the option to build your Image Template directly in the Azure Portal.

To oversimplify things, an Image Template answers three major questions:

  1. What’s the source of your new VM image?

  2. What customizations do you want to perform to your new VM image?

  3. Where do you want to store / distribute your new VM image?

I’ll go over all three of these things in more detail below.


Image Template: Source

There are three options that you can use as the source of your custom image:

1. PlatformImage

  • This is an image from the Azure Marketplace

  • You must specify a valid Publisher, Offer, Sku, and Version.

  • Version can be specified as ‘latest’ if you don’t want to give a specific version number

  • You can use the Az CLI command az vm image list --output table --all to find out info about marketplace images

  • You can add some filters to your command to list only certain images, for example az vm image list --publisher Canonical --sku gen2 --output table --all

  • Optionally, if needed by your Marketplace image, you can specify a PlanInfo block, which defines PlanName, PlanProduct, and PlanPublisher

2. ManagedImage

  • This uses an existing Managed Image that you have in Azure

  • Managed Images are an entirely different subject which I won’t go into detail on in this blog post. If you want more information on Managed Images then check this link for some documentation from Microsoft

  • The specified managed image must be located in the same Subscription and Region as your Image Template

  • You can use the Az CLI command az image list to see available managed images

3. SharedImageVersion

  • This uses an existing Image Version that you have stored in an Azure Compute Gallery (formerly known as Shared Image Gallery)

  • The specified Image Version must be located in the same Region as your Image Template

  • You can use the Az CLI command az sig image-version list to see available Image Versions.


Image Template: Customizers

Customizers are functions that can be used to, you guessed it, customize your image. You can do things like run a script, reboot, run windows update and more.

You can run multiple customizers. They execute in the order they are listed. If one customizer fails, then the entire customization step fails. In other words, all customizers must succeed for the image build to succeed.

There are five different types of customizers:

1. Shell

  • This support Linux images and lets you run shell scripts

  • The script could be stored in a separate file, in which case you must specify the URI to that file. Or the contents of the script can be stored inline in the customizer itself

  • Do not put sensitive values in your inline shell customizers, as the inline commands can be read by anyone who has access to the Image Template

  • If your command needs elevated rights, then just use sudo in the command.

2. PowerShell

  • This supports Windows images and lets you run PowerShell scripts

  • Same as above, the script could be stored in a separate file, or commands can be inline with the customizer

  • Same warning as above, do not put sensitive values in your inline commands!

  • If your script needs elevated rights, then make sure to set runElevated: true on your customizer

3. File

  • This supports both Linux and Windows images and lets you download a file, either from a GitHub repo or from Azure Storage

  • You can only download one file. If you need to download more than one file, or download an entire directory, then use a compressed file. After you download the compressed file, you could use a Shell/PowerShell customizer to decompress the file.

  • This customizer is only good for smaller files that are less than 20 MB. For larger files you should use a Shell customizer and run wget or curl, or a PowerShell customizer and run Invoke-WebRequest

  • You must specify a destination for the file:

    • The destination folder must exist already. You could use a Shell/PowerShell customizer to create the path beforehand, if needed

    • Linux images are only allowed to use a destination somewhere under /tmp

    • Windows images can use any destination, as long as it already exists

4. WindowsRestart

  • This support Windows images and it restarts a Windows VM

  • This waits for the VM to come back online before continuing

  • This allows you to install software which requires a reboot

5. WindowsUpdate

  • This supports Windows images and allows you to perform Windows Updates to your image

  • This customizer is built on an open-source project maintained by the Packer community. It’s not officially supported by Microsoft, however they will still provide best effort support

  • If there are any outstanding Windows restarts waiting, or any application installs still ongoing, then this customizer will fail. You could use a PowerShell customizer that runs Start-Sleep to give application installs more time, or you could do a WindowsRestart customizer to clear out any pending restarts, before running the WindowsUpdate customizer


Generalize / Deprovision / SysPrep

When all your customizers are complete, Azure VM Image Builder will generalize your image. Generalizing is the process where your image is set up so it can be reused over and over to create multiple VMs. For Windows images, the sysprep command is used. For Linux, the waagent -deprovision command is used.

If the default generalization process does not work for your image, then you can override it and use your own. Just create a script with a specific name and location, and VM Image Builder will use that script instead.

  • For Windows Images, create a file named c:\DeprovisioningScript.ps1

  • For Linux Images, create a file named /tmp/

An example of when you might need to use your own custom Sysprep command is when you need to utilize nested virtualization. The default Sysprep command doesn't include /mode:vm, and this property may be required when your image needs the HyperV role installed. If you need to add this command argument, you must override the default Sysprep command.


Image Template - Distributing your image

Azure VM Image Builder supports three distribution targets:

1. ManagedImage

  • Your customized image is stored in a managed image

  • The destination RG of the managed image must already exist

  • If you pick a destination Region that’s different than your Image Template, then this will increase the deployment time as the image must be distributed to that other Region

2. SharedImage:

  • Your customized image is stored in an Azure Compute Gallery

  • You can choose to let Azure perform automatic versioning of your image, or you can specify your own explicit image versions

  • You can specify multiple Regions where the image will be replicated to. At a minimum, you must choose the region where the Image Gallery is located. Note: more regions means more build time because it takes time to replicate to other regions

3. VHD

  • Your customized image is stored as a VHD file

  • The VHD is put into a Storage Account inside the staging Resource Group that's automatically created by Azure VM Image Builder (more on this in Part 2). What’s important to know is that you should copy the VHD to a different location as soon as possible. Because if the Image Template is deleted, then this staging Resource Group is also deleted, which includes the storage account containing the VHD

If desired, you can specify multiple distribution targets at the same time. Each target must have a unique value for runOutputName. Azure stores the current state of each distribution target in that runOutputName field.


Managed Identity

One last thing that I want to mention is that you must create one or more User-Assigned Managed Identity for the Azure VM Image Builder service to use.

An Identity is required to be used at the Image Template scope. This Identity is used for things like reading source Images, creating your Distribution targets, and accessing Azure Storage Accounts. You can only assign one identity for this purpose.

Here are the permissions needed in order for the Managed Identity to create Managed Images and/or Image Versions:

  1. Microsoft.Compute/galleries/read

  2. Microsoft.Compute/galleries/images/read

  3. Microsoft.Compute/galleries/images/versions/read

  4. Microsoft.Compute/galleries/images/versions/write

  5. Microsoft.Compute/images/read

  6. Microsoft.Compute/images/write

  7. Microsoft.Compute/images/delete

Here is a link to my GitHub repo where you can find an RBAC role definition that you could use to build a custom role in your tenant.

Optionally, you can also assign one or more Identities that will be attached to the temporary VM that is used to build your image. This is useful if your Customizer scripts need to access KeyVault or other Azure resources. If desired, you can use the same Managed Identity mentioned above for this purpose as well.



There are a few more settings that you can configure in your Image Template which I do not cover here. Some will also be covered in Part 2 of this series. I recommend reading the sources that are referenced below for details on every available option.

So with your IaC code now complete, you have to actually create the Image Template resource. If you wrote your code in Bicep, and you need a little help with deploying it, then have a look at my Deploying Bicep Series.


Part 2

So now you know how to create an Image Template (for the most part) How do you build your image? And what happens under the hood during your build? That will all be covered in Part 2!





bottom of page