Building a Windows 10 Enterprise Multi Session Master Image with the Azure Image Builder DevOps Task


G'day WVD Community,


Organization are evaluating how to build Master Images for WVD. One of the options available is through the AIB DevOps Task which provides a high level of automation, repeatability and integration with Azure, which is where the WVD Service lives! 


This post is to provide an introduction on how to get started with Building a Windows 10 Enterprise Multi Session Master Image with the Azure Image Builder DevOps Task.


Note: As this guidance was in a Word document I have left the numbering intact.


What will we do in this post?

  1. Introduction
  2. Pre-requisites
  3. Setup Azure Image Builder with PowerShell

  4. Create and Configure the Azure VM Image Builder DevOps Task Release Pipeline

  5. Use the newly build Image in WVD Spring Release!

  6. Troubleshooting AIB DevOps Release Pipeline


Call out!

A big thank you to all the people helping me with this document, Marvin, Stefan, Jason, Jim and last but not least Scott for helping me getting started!


1 - Introduction

Windows 10 Enterprise multi-session (Win10ms), formerly known as Windows 10 Enterprise for Virtual Desktops (EVD), is a new Remote Desktop Session Host that allows multiple concurrent interactive sessions. Win10ms is available in the Azure Marketplace, used to build pooled Sessions Hosts in a Hostpool for Windows Virtual Desktops (WVD).

These WVD pooled Session hosts are Virtual Machines in Azure and need to be created from a Win10ms Image. Within the Marketplace standard Win10ms images are available but most organizations require customized images including configuration settings and software such as FSLogix for profile management and Microsoft Teams optimized for WVD.

Keeping these Session Hosts up to date can be a challenge as feature updates are not supported for Win10ms in Azure. Also considering multiple users potentially are connected to the same VM, installing updates could be disruptive as well.

Depending on the capacity required organizations could potentially use a large amount of Session Hosts. Ensuring all these VM’s have an identical configuration and have a consistent end-user experience, updating these VM’s can be labor intensive and often is error prone activity.  Automating the update of the Session Hosts in a controlled and repeatable process is a trivial requirement for a successful WVD deployment. This also prevents “configuration drift” between VM’s which can occur over time.

Although System Center Endpoint Manager and Microsoft Deployment Toolkit (MDT) today are used in many organizations, these technologies are not fully optimized and integrated with Azure. In the case of SCCM, Win10ms Market Place Images would need to be copied locally and back to Azure resulting in additional and often manual activities. Additionally, the Win10ms Image for most organizations should require relatively less configuration and optimization as the Marketplace Win10ms image is already optimized for WVD from the Marketplace. From an application perspective, with the capability to support MSIX, App-Attach soon, these images should also not require having many applications installed.

1.1  Azure Image Builder

To overcome these challenges Azure Image Builder (AIB) can be used. AIB is developed for Azure to assist with the automated creation of Managed Images in Azure. As WVD is a PaaS in Azure these services have full integration.

Azure Image Builder can be used in different ways to build a Win10ms Master Image.

  1. AIB Portal (future release)
  2. AIB PowerShell (or Cli)
  3. AIB DevOps Task

This post will provide guidance on using the AIB DevOps Task.

1.2  AIB DevOps Task

For large organizations and enterprises, key criteria for an Image Build process includes version control, reporting, requirements management, project management, automated builds, lab management, testing and release management capabilities. Azure DevOps can be used to address these requirements by creating a WVD Win10ms Build Release Pipeline in Azure DevOps.

This document provides guidance on how to setup and use WVD Win10ms Build Release Pipeline to achieve a repeatable and controlled process (DevOps) for automating Win10ms builds (AIB) and versioning and replicating those Images to the locations/Azure Regions where they are needed (Shared Image Gallery).


Note: this post is using the guidance as provided by Daniel Sol on his GitHub location: but uses it for a WVD scenario to create a Windows 10 ms Image.




2 - Pre-requisites

These instructions assume the following pre-requisites are in place:

  • You have Owner access to an Azure Subscription so you can create Resource Groups, VMs, Key Vaults, Images etc. In a real production environment these rights can be restricted to smaller scopes.
  • AAD has been setup for the subscription above where you have Global Admin access, so you can create Service Principals and Managed Identities. In a real production environment these rights can be restricted by RBAC.


This post will provide guidance on how to setup a DevOps Pipeline for a Win10ms build. Note that this document is not intended to explain all the aspects of Azure, PowerShell, and DevOps. Some basic knowledge on these topics is assumed to be successful.


3 - Setup Azure Image Builder with PowerShell

These are some of the basic Azure components that need to be in place before we can start building.

The details of these tasks can be found in the links below.

  1. Create a Resource Group (RG) – this will be used to store AIB and SIG components.
  2. Enable and register Azure Image Builder
    1. Via PowerShell – commands here
    2. Via Azure CLI – commands here

Note: It takes a couple of minutes to register the service.


  1. Create an Azure user-assigned Managed Identity
    1. Via PowerShell – commands here
    2. Via Azure CLI – commands here

Note: Use the resource group created earlier.

  1. Grant permissions to the Azure user-assigned Managed Identity to the resource group
    1. Via PowerShell – commands here
    2. Via Azure CLI – commands here

Note 1: For the PS commands, update the variable $aibRoleImageCreationPath if you need to download in a different path

Note 2: For the PS commands, make sure you create a variable $subscriptionID that is your Azure Subscription ID.



  1. Create an Azure Shared Image Gallery (SIG) and an Image definition in the resource group created earlier. Azure CLI commands here.
  1. Create a storage account in the resource group created earlier that will be used to transfer the artifacts from ADO tasks to the image. Azure CLI commands here.

4 - Create and Configure the Azure VM Image Builder DevOps Task Release Pipeline

4.1           Create a DevOps Organization

Go to and follow the steps as described in Create an organization


4.2           Create a DevOps Project

Go to<your organization name> and create a new project as described in Create a project in Azure DevOps




Name your project in the “Project name” field (1) and click “Create” (2).



4.3           Create a DevOps Git Repository

The WVD Image requires to be customized through a customization script and potentially additional software is required. A repository needs to be created to store the customization script and potentially other content. Additionally, the customization script requires to be maintained with version control etc.

In Azure DevOps, Repos are used to manage your code and, in our example, the AIB Customization Powershell Script. You can connect your favorite development environment to Azure Repos to access your repos and manage the code. The code can be shared using:

Consideration 1: Type of Repository

The Repository can be in any Azure DevOps supported repository. In our case we will use a standard Azure DevOps repository to host our PowerShell customization script used in the AID DevOps task but alternatively you could also use your GitHub repository.  If a GitHub repository will be used the steps in this paragraph can be skipped.

Consideration 1: Size of Repository needed.

A Git Repo is not intended to store for example application Packages or other packages with large size (Artifacts in Azure DevOps are free up till 2Gb). An alternative solution to store large packages is in Azure File Share storage which should be made accessible during the build. AIB supports the usage of existing VNETs that can be used.


Select “Repos” (1) and select “Initialize” (2).



Create a Folder in the Repo by selecting the 3 dots in the right top corner (1). Select “+ New” (2) and click on “Folder” (3).



Provide the name of the folder in the “New folder name” and use “WVD” (or any other name you prefer).
Provide the name of the “New file name” field (make sure it has the .ps1 extension) and click on “Create”. In our example we use “Customization.ps1” for the New file name.



Note:  Sample script for WVD Windows 10 Enterprise multi session customization.

Sample customization scripts can be found in my GitHub repository:

These customization script include the by Microsoft recommended customizations as documented here:

and here


Add the customization content (PowerShell script) to customize the Windows 10 build.

Once finished with the customization content (1) click on “Commit” (2).



In the next screen leave the default values and click on “Commit” (1) again.



4.4           Create a DevOps Release Pipeline

In Azure DevOps-specific terms, a pipeline is a sequence of steps on your code, in our example we can look at the code as the Custimization Powershell. A build is a pipeline that has no side-effects, it only takes in code and puts out compiled "artifacts". A release is a pipeline that has side-effects, it takes in artifacts and publishes or deploys them to environments.

The latter is more aligned with an Image build workflow, where we are building a Managed Image to be released as a Managed Image, potentially in a Shared Image Gallery, to be deployed in Azure and used in our different environments (dev, test, prod).


In your project, navigate to the “Pipelines” page. Then choose the action to create a new Release Pipeline by Selecting Releases (1) and click on “New Pipeline” (2).



Under “Select a template” click on “Empty Job”.



Provide a Name for the Stage name field, in our case we use “Win10ms Image Build”. There is no need to click a button to confirm.



Click on the Add an artefact. The Artifact



In the Add an artefact window, select Azure Repos Git (1). Select your Project name (2). Select the source (repository) (3) and select master as Default branch (4). Click “Add”.



Once the Artifact has been created we have linked the repository with our customization script to our release Pipeline, which can now be referred to and used during our Image build process.

To ensure the Artifact is updated with the most recent Repo Commit (version) we need to configure a Pull request trigger. Click the Trigger icon on the Artifact (1) and enable the “Pull request trigger” (2). Each time a new Release is created, the Artifact will pull the latest committed Repo content to be included in the Release Pipeline.



Click the task in the Win10ms Image Build stage.



We will now see the Agent Job which has not yet assigned any tasks.

Click “+” to add a task and search for “Image Builder” (2).



If the “Azure VM Image Builder DevOps Task (Preview)” is not already installed, click the Install (1) button. In our example below the Task is already installed.



If the “Azure VM Image Builder DevOps Task (Preview)” task is already installed, click “Add” (1).



4.5           Configure Azure VM Image Builder DevOps task in Pipeline

The Image Builder Task needs to be configured to your requirements. The Pipeline with the AIB task provides a way to create a repeatable process to create updated images in an automated way from a managed and versioned repository.



The below example values will use the following:

Source: Market place image Windows 10 Enterprise Multi Session 20H1 with Office 365 installed

Customizer: A PowerShell script stored in the Repository (created earlier for the project) used by the Pipeline including all the customization we want to include in the Image.

Distribute: In our example we will distribute the Image


Select the “Azure VM Image Builder DevOps Task” checkbox.



4.5.1       Azure Subscription

Select (1) the Azure subscription to use (2).



After selecting the Azure Subscription click the dropdown button next to Authorize (1) and select “Advanced Options”.



In the “Add an Azure Resource Manager sercive connection” select the Resource group you created earlier where AIB and the SIG is located. This will authorize the Pipeline to use the selected Resource Group.


Note: Alternatively you can decide not to select a resource group where you allow this service connection to have access to all Resource Groups within the subscription. This might be useful when you like to re-use this connection for other purposes.


4.5.2         Resource Group and Location

Select the “Resource Group” (1) that Azure Image Builder is going to use to store the Image artifacts and select the location for resources to be created in the “Location” field (2).



4.5.3       Managed Identity

Provide the “Identity Resource Id” of the Managed Identity created earlier.

The “Identity Resource Id” can be found under the property blade of the Managed Identity.


1.1.1       Image Type

For the Image Type field select “Marketplace”.

The “Base Image” field can be ignored as we will provide the details in the “If base image is not in the list (Optional)” field.

To get the values required for the “If base image is not in the list (Optional)” field, use the Get-AzVMImage command. See reference table below:

Publisher Name







Win10 Ent MS 2004



Win10 Ent 2004 – Gen1



Win10 Ent MS 1909



Win10 Ent 1909 – Gen1



Win10 Ent MS 1903



Win10 Ent MS 2004 with O365



Win10 Ent MS 1909 with O365



Win10 Ent MS 1903 with O365




Win Server 2019 datacenter


In our example we will choose the Marketplace image with Office included.

The format “publisher:offer:sku” can be extracted from the table above which is MicrosoftWindowsDesktop:office-365:20h1-evd-o365pp

In the “If base image is not in the list (Optional)” field type: MicrosoftWindowsDesktop:office-365:20h1-evd-o365pp

We will leave the field “Base Image Version (optional)” set to “latest” as we want the latest version.


4.5.5       Provisioner

Under “Customize” we will select “PowerShell” for the “Provisioner” field as we are creating a Windows Image.

4.5.6       Run Windows Update as last customization

If preferred, you can select “Run Windows Update as last customization” to ensure the Image includes all the latest updates.


Note: This is the Windows Update configuration that is executed:




"type": "WindowsUpdate",
    "searchCriteria": "IsInstalled=0",
    "filters": [
        "exclude:$_.Title -like '*Preview*'",




It will install important and recommended Windows Updates, that are not preview.


4.5.7       Build path

Click on the three dots next to “Build path” (1). A window will popup with our Repository from where we select the path. Click on the folder “WVD” (2) which we have created earlier and click “OK” (3). Make sure to click on the folder and not the customization.ps1 script.



4.5.8       Inline customization script

For the “Inline customization script” field enter a PowerShell inline commands separated by commas, and if you want to run a script in your build directory, you can use:




& 'c:\buildArtifacts\WVD\Customization.ps1'





Note: If your base image restricts PowerShell scripts from executing unless signed an alternative inline script command should be used instead.




'& set-executionpolicy remotesigned -scope Process -Force; c:\BuildArtifacts\WVD\Customization.ps1'





4.5.8       Storage Account

Select the storage account you created in the prereqs, if you do not see it in the list, Image Builder does not have permissions to it.


Note: When the build starts, Image Builder will create a container called 'imagebuilder-vststask', this is where the build artifacts from the repo are stored. You need to manually delete the storage account or container after each build!!!


4.5.10       Distribute

There are 3 distribute types supported:  Managed Image

The ResourceID of the Managed Image needs to be provided in this format:


  • Locations  Azure Shared Image Gallery

The ResourceID of the Image Definition needs to be provided in the following format:


Note: The Image Definition for the Shared Image Gallery MUST already exist!


Regions: list of regions, comma separated, e.g. westus2, eastus, centralus  VHD

You cannot pass any values to this, Image Builder will emit the VHD to the temporary Image Builder resource group, ‘'IT__', in the 'vhds' container. When you start the release build, image builder will emit logs, and when it has finished, it will emit the VHD URL.


In our example below we distribute to a Shared Image Gallery in a single location (westus2) .

31.png  VM Size

You can override the VM size, from the default of Standard_D1_v2. You may do this to reduce total customization time, or because you want to create the images that depend on certain VM sizes, such as GPU / HPC etc.


4.5.11       Run the Pipeline

Click on “Save” (1) and Click “OK” (2) in the Save window.



Now you can click “Create Release



In the “Create a new release” windows click on “Create”.


You can view the Release by clicking on “Release-1”.



4.5.12       Navigate through Release Pipelines

Each time when you create a new Release Pipeline they are preserved under Releases” (1). By clicking on the Release Number or button under Stages (2) you can look at the details of each previously run Release.



5       Use the newly build Image in WVD Spring Release!

After a successful Release Pipeline build you can use the Image when creating a new Hostpool.

Click on “Browse all images and disks” (1), select “My Items” (2), select “Shared Images” (3) and select the Image we just created (4).



6   Troubleshooting AIB DevOps Release Pipeline

 Packer Log files

Select the Agent job 1.



Notice the “Template name”



A new Resource Group is created corresponding with the highlighted number in the template.



Open the Resource Group and open the Storage Account in the Resource Group to get the Blob Storage for the Packer Log files.


Note: This Resource Group including the storage account will be deleted after the Image has been created. If you want to read the logs you will need to do it during the build and distribution of the Image.


Sample output of Packer Log.



Known Issues

The (free) Agent job for the Release Pipeline has a limitation to only run for 60 minutes. After 60 minutes it will timeout and report it hasn’t heard from the agent. This is as expected, and the build will likely finish successful.



As The Agent hasn’t finish it will not clean up the Resource Group created for the Release Pipeline which also includes the storage account with the Packer logs (see previous paragraph on Packer Log files).  Please make sure to clean up this Resource Group manually!




The End!


I hope this was useful to get you going. If anything is missing please feel free to reach out.





7 Replies

@Roel SchellensThis is great but my org requires tags be created on new objects in Azure. How can I do this using your method? It's failing during the packer section creating the template since it's missing the required Azure tags.  

@Roel Schellens 


Since yesterday (11/08/2020) and today, the windows update part of the Azure VM Image Builder DevOps Task no longer works. Getting this error: PACKER ERR error initializing provisioner 'windows-update': Incompatible API version with PACKER ERR plugin. Plugin version: 4, Ours: 5

That is correct. We are currently looking into this. Will report back when we have more info.

Cool stuff Roel!

Not sure if this is me, but my release pipeline never seems to finish (using own agents).

The RG is left with the temp storage account and the intermediate image remaining.

Last line of the log file:

[687ab117-35ce-4ddd-8a17-f76d0b8f2157] PACKER OUT Done exporting Packer logs to Azure Storage.

I can see and use the new image version in the shared image gallery.


The agent insists the process is still running:

created archive C:\azp\agent\_work\_temp\
Source for image: { type: 'PlatformImage',
publisher: 'MicrosoftWindowsDesktop',
offer: 'office-365',
sku: '20h1-evd-o365pp',
version: '19041.508.2009070256' }
template name: t_51_1602768302997
starting put template...
put template: Succeeded
starting run template...
It's been like this for over an hour now. Is it waiting for something? Should I be more patient?
Can I check anywhere else for logging or status?

Lol, never mind.


Of course, as soon as I posted this comment the pipeline gracefully finishes, everything cleaned up as promised.


Thanks again!