Create a Windows VM with Azure Image Builder

Applies to: ✔️ Windows VMs

This article is to show you how you can create a customized Windows image using the Azure VM Image Builder. The example in this article uses customizers for customizing the image:

  • PowerShell (ScriptUri) - download and run a PowerShell script.
  • Windows Restart - restarts the VM.
  • PowerShell (inline) - run a specific command. In this example, it creates a directory on the VM using mkdir c:\\buildActions.
  • File - copy a file from GitHub onto the VM. This example copies to c:\buildArtifacts\index.html on the VM.
  • buildTimeoutInMinutes - Increase a build time to allow for longer running builds, the default is 240 minutes, and you can increase a build time to allow for longer running builds.
  • vmProfile - specifying a vmSize and Network properties
  • osDiskSizeGB - you can increase the size of image
  • identity - providing an identity for Azure Image Builder to use during the build

You can also specify a buildTimeoutInMinutes. The default is 240 minutes, and you can increase a build time to allow for longer running builds. The minimum allowed value is 6 minutes; shorter values will cause errors.

We will be using a sample .json template to configure the image. The .json file we are using is here: helloImageTemplateWin.json.


For Windows users, the Azure CLI examples below can be run on Azure Cloud Shell using Bash.

Register the features

To use Azure Image Builder, you need to register the feature.

Check your registration.

az provider show -n Microsoft.VirtualMachineImages | grep registrationState
az provider show -n Microsoft.KeyVault | grep registrationState
az provider show -n Microsoft.Compute | grep registrationState
az provider show -n Microsoft.Storage | grep registrationState
az provider show -n Microsoft.Network | grep registrationState

If they do not say registered, run the following:

az provider register -n Microsoft.VirtualMachineImages
az provider register -n Microsoft.Compute
az provider register -n Microsoft.KeyVault
az provider register -n Microsoft.Storage
az provider register -n Microsoft.Network

Set variables

We will be using some pieces of information repeatedly, so we will create some variables to store that information.

# Resource group name - we are using myImageBuilderRG in this example
# Region location 
# Run output name
# name of the image to be created

Create a variable for your subscription ID.

subscriptionID=$(az account show --query id --output tsv)

Create a resource group

This resource group is used to store the image configuration template artifact and the image.

az group create -n $imageResourceGroup -l $location

Create a user-assigned identity and set permissions on the resource group

Image Builder will use the user-identity provided to inject the image into the resource group. In this example, you will create an Azure role definition that has the granular actions to perform distributing the image. The role definition will then be assigned to the user-identity.

Create user-assigned managed identity and grant permissions

# create user assigned identity for image builder to access the storage account where the script is located
identityName=aibBuiUserId$(date +'%s')
az identity create -g $imageResourceGroup -n $identityName

# get identity id
imgBuilderCliId=$(az identity show -g $imageResourceGroup -n $identityName --query clientId -o tsv)

# get the user identity URI, needed for the template

# download preconfigured role definition example
curl -o aibRoleImageCreation.json

imageRoleDefName="Azure Image Builder Image Def"$(date +'%s')

# update the definition
sed -i -e "s/<subscriptionID>/$subscriptionID/g" aibRoleImageCreation.json
sed -i -e "s/<rgName>/$imageResourceGroup/g" aibRoleImageCreation.json
sed -i -e "s/Azure Image Builder Service Image Creation Role/$imageRoleDefName/g" aibRoleImageCreation.json

# create role definitions
az role definition create --role-definition ./aibRoleImageCreation.json

# grant role definition to the user assigned identity
az role assignment create \
    --assignee $imgBuilderCliId \
    --role "$imageRoleDefName" \
    --scope /subscriptions/$subscriptionID/resourceGroups/$imageResourceGroup

Download the image configuration template example

A parameterized image configuration template has been created for you to try. Download the example .json file and configure it with the variables you set previously.

curl -o helloImageTemplateWin.json

sed -i -e "s/<subscriptionID>/$subscriptionID/g" helloImageTemplateWin.json
sed -i -e "s/<rgName>/$imageResourceGroup/g" helloImageTemplateWin.json
sed -i -e "s/<region>/$location/g" helloImageTemplateWin.json
sed -i -e "s/<imageName>/$imageName/g" helloImageTemplateWin.json
sed -i -e "s/<runOutputName>/$runOutputName/g" helloImageTemplateWin.json
sed -i -e "s%<imgBuilderId>%$imgBuilderId%g" helloImageTemplateWin.json

You can modify this example, in the terminal using a text editor like vi.

vi helloImageTemplateWin.json


For the source image, you must always specify a version, you cannot use latest. If you add or change the resource group where the image is distributed to, you must make sure the permissions are set on the resource group.

Create the image

Submit the image configuration to the VM Image Builder service

az resource create \
    --resource-group $imageResourceGroup \
    --properties @helloImageTemplateWin.json \
    --is-full-object \
    --resource-type Microsoft.VirtualMachineImages/imageTemplates \
    -n helloImageTemplateWin01

When complete, this will return a success message back to the console, and create an Image Builder Configuration Template in the $imageResourceGroup. You can see this resource in the resource group in the Azure portal, if you enable 'Show hidden types'.

In the background, Image Builder will also create a staging resource group in your subscription. This resource group is used for the image build. It will be in this format: IT_<DestinationResourceGroup>_<TemplateName>


You must not delete the staging resource group directly. First delete the image template artifact, this will cause the staging resource group to be deleted.

If the service reports a failure during the image configuration template submission:

  • Review these troubleshooting steps.
  • You will need to delete the template, using the following snippet, before you retry submission.
az resource delete \
    --resource-group $imageResourceGroup \
    --resource-type Microsoft.VirtualMachineImages/imageTemplates \
    -n helloImageTemplateWin01

Start the image build

Start the image building process using az resource invoke-action.

az resource invoke-action \
     --resource-group $imageResourceGroup \
     --resource-type  Microsoft.VirtualMachineImages/imageTemplates \
     -n helloImageTemplateWin01 \
     --action Run 

Wait until the build is complete. This can take about 15 minutes.

If you encounter any errors, please review these troubleshooting steps.

Create the VM

Create the VM using the image you built. Replace <password> with your own password for the aibuser on the VM.

az vm create \
  --resource-group $imageResourceGroup \
  --name aibImgWinVm00 \
  --admin-username aibuser \
  --admin-password <password> \
  --image $imageName \
  --location $location

Verify the customization

Create a Remote Desktop connection to the VM using the username and password you set when you created the VM. Inside the VM, open a cmd prompt and type:

dir c:\

You should see these two directories created during image customization:

  • buildActions
  • buildArtifacts

Clean up

When you are done, delete the resources.

Delete the image builder template

az resource delete \
    --resource-group $imageResourceGroup \
    --resource-type Microsoft.VirtualMachineImages/imageTemplates \
    -n helloImageTemplateWin01

Delete the role assignment, role definition and user-identity.

az role assignment delete \
    --assignee $imgBuilderCliId \
    --role "$imageRoleDefName" \
    --scope /subscriptions/$subscriptionID/resourceGroups/$imageResourceGroup

az role definition delete --name "$imageRoleDefName"

az identity delete --ids $imgBuilderId

Delete the image resource group

az group delete -n $imageResourceGroup

Next steps

To learn more about the components of the .json file used in this article, see Image builder template reference.