AVD Deployment and Update – Part 1 Building Automation


With the move to Azure Virtual Desktop came a number of questions. The main question was how to control the Deployment and Update process.

Manually, deploying and updating Azure Virtual Desktop images can take a long time, so I investigated to see how I could automate these processes. I chose Azure DevOps to be my solution.

I set to work looking at how Azure DevOps could be used to automate these processes and make my life easier. I couldn’t find much online so went about figuring it out myself. Thankfully I like a good challenge 😊

I ended up creating several Azure DevOps pipelines to handle the Deployment and Update tasks. The result was an automation system that can be used for:

  • New AVD Environment Builds
  • Host Pool Updates
  • Scaling out an existing Host Pool.

This blog will be separated into multiple sections. In this first section we will concentrate on how the Automation has been built. This will include the structure of the Azure DevOps project and scripts.

In subsequent blogs I will demonstrate how it is used to build new AVD environments, update an existing environment, and add additional Session Hosts to an existing Host Pool.

AVD Deployment and Update – Part 2 Deploy AVD Environment

AVD Deployment and Update – Part 3 Automating Updates


For a look at the actual scripts and code behind this Automation please check my GitHub page below:

AVD-DEPLOY/readme.md at main · jamesatighe/AVD-DEPLOY (github.com)

Azure DevOps

As with any Azure DevOps build. The first thing I did was create an Azure DevOps project.

Variable Groups

Then I looked at variables that are used to control the various deployments. For this I use several variable groups.

Under Pipelines > Library the required Variable groups can be found.

I use 2 separate Variable Groups. AVD – Selectable Variables and AVD – Static Variables.

AVD – Static Variables contains all the main variables required by the ARM Deployment, such as the Subscription ID, Machine Size, Resource Group names, and the main settings that remain the same throughout deployments.

AVD – Selectable Variables contain variables which can be changed per build. This includes simple variables such as DesktopName, vmNumberofInstances, and vmNamePrefix. Which can be tailored as required.

It also contains some special variables that control the way the deployment is processed.


This variable controls whether the deployment is for an update or not.

Setting this to true will result in the newest AVD image version being used for deployment, a clean-up script to be run to remove the old Session Hosts from the Host Pool, power them off, and tag the with Remove: true

This would normally be used after running the AVD – Update pipeline to perform Windows updates. This pipeline would create a new version of the AVD image ready for deployment.

If update is set to false then the AVD image version specified in the SharedImageGalleryVersionName will be used, and no clean-up script will be used.

This enables us to deploy the AVD Session Hosts as any specific version we need, ideal to rollback a deployment to an earlier AVD image.


This variable is used to specify whether this is a new build or a deployment against an existing Host Pool.

Setting this to true will create all resources again including the AVD Workspace, Host Pool, Application Group, and all Session Hosts. Due to deployments being done as incremental this will not overwrite existing resources if they already exist. This switch MUST be used for a newBuild.

Also, when set to true the Session Host numbering will start a 0 (e.g. <VMPrefix>-0)

If newBuild is set to false then the Workspace, Host Pool and Application group will not be created. Only new Session Hosts will be created. This is to be used for scaling out an existing Host Pool.

When this variable is set to false the deployment will use logic to count the current number of Session Hosts and continue the numbering.
So, if there were already 10 Session Hosts. The new host numbering would start at 10 (as <VMPrefix>-0 to <VMPrefix>-9 would already exist)


This variable is used with the update variable to manually specify the AVD image version to deploy. If update is set to false, the value in this variable will be used to deploy the Session Hosts.


I created several Pipelines/releases to handle to deployment and update workload.

AVD Image Update – This pipeline automatically applies Windows Update to the existing Shared Image Gallery Definition and create a new version that is imported into the Gallery.

This allow the AVD image to be updated without manually intervention.

Destroy Old Session Host – This pipeline is used to remove any old Session Hosts that are marked for removal. It removes the Session Host from the Host Pool and then removes the Session Host VMs and any dependencies (NICs Disks etc)

Pipeline Release

There is also a Pipeline Release that has been created that handles the main deployment.

This main Pipeline Release is responsible for the main deployment of the actual AVD platform. This included the creation of the Host Pool, Application Group, Session Hosts etc.

This release has been created to allow multiple different functions. It can be used to do the following:

  • Create brand new Greenfield AVD environment. New Host Pool, Application Group, Session Hosts.
  • Update an existing Host Pool to use new Session Hosts from an updated image.
  • Scale out an existing Host Pool to add additional Session Hosts.

Naming of VMs

The naming of the Session Hosts is done based on the the PreBuild.ps1 script in the main deployment task.

The Session Host VMs are named as follows:

<VMPrefix>-MinorVersion-PatchVersion-<VM number>

The MinorVersion for an image will be set to the month the image was patched. So for August this would be 08.

The PatchVersion would be incremented if the image was patched multiple times within a month.

This numbering allows us to easily identify the patch level of the Session Hosts.

e.g. PROD-08-0-0

This would be the production image from patch month 08 (August) and no additional patches (hence the 0)

AVD Image Update Pipeline

This pipeline is used to automatically update the existing Shared Image Gallery Definition.

It contains several tasks.

First the PreBuild.ps1 script is called from an Azure PowerShell Task.

This PowerShell is responsible for getting the latest Shared Image Gallery Definition Version which is used to build the update VM.

It also generates the new Version number based on the month as explained previously, and also downloads the required Windows Update Packer Plugin used for automatic updates.

Variables from this task are outputted to be used in subsequent tasks.

Next, a Use Packer task is run. Followed by a Packer build Task.

This Packer Build Task simply runs a Packer build that will :

  • Deploy a VM based on the latest existing Shared Image Gallery Definition Version,
  • Run Windows update using the Windows Update Packer Plugin by RJG
  • Sysprep VM
  • Capture the VM into a new Shared Image Gallery Definition Version

Finally, a Inline PowerShell Task runs to remove the managed image created to upload into the Shared Image Gallery.

Destroy Old Session Hosts Pipeline

This pipeline is very simple. It contains only a single Azure PowerShell Task.

This tasks runs the DestroySessionHosts.ps1 script. Which removes any old Session Hosts marked with the Remove tag equal to True.

This pipeline is triggered manually after full testing has been performed on an updated Host Pool.

Deploy Pipeline Release

The main part of the deployment is the Pipeline Release used for the actual AVD deployment. In my case this is called TigheTec AVD – Prod

This release consists of several tasks.

The first is an Azure PowerShell Task.

This PowerShell is used to determine what Shared Image Gallery Version is used for the Session Host VMs. This is based on the AVD – Selectable Variables that we saw earlier.

Depending on the selection it will either pick the latest version (for an update) or a manually selected value (for Scale Out or New Builds)

This PowerShell also creates a JSON object for the Version tag that is applied to the VM. This is to ensure the Session Host VM is stamped with relevant Shared Image Gallery Version.

Following this there is a ARM Template Deployment Task

This task uses outputs from the previous PowerShell Task. Specifically for the Shared Image Gallery Version, VMPrefix and Tags.

This script is a rather complicated ARM template that has been created for my specific deployment needs. I have discussed this previously in Deploy Azure Virtual Desktop with Project BICEP – TigheTec Cloud Consulting

This is the main ARM template that will deploy the entire AVD environment. If you have your deployment scripts you would add it here with the relevant template and parameter files.

Finally, there is another Azure PowerShell Task.

This is used to perform clean-up operations on existing Session Hosts. This is only used if the AVD – Selectable Variable settings have update set to true.

If so this script will run and find any Session Hosts VMs where their Version tag does not match the latest deployed version. It will then deal with user messaging, removal before deallocating the machine and adding the Remove tag to equal True.

If the update variable is set to false, this clean-up script will be skipped.


So we have now gone through the structure and components of the Automation. In the next part of this Blog Series I will go through some examples of process is action.

Hopefully, this can be useful to some people.

If anyone has any questions on the exact setup of the task please feel free to let me know!

3 thoughts on “AVD Deployment and Update – Part 1 Building Automation

  1. I am trying to make a similiar pipeline as the “TigheTec AVD – Prod” pipeline for our environment, but are the bicep files from the AVD-BICEP repository on your github? As the “MainParameters.json” file is already filled with variables.


    1. Sorry just saw I never replied to this.

      The bicep files are on my GitHub.

      The “MainParameters.json” was the file used with the converted MainBuild.json file for the build.

      This is more in there as an example.

      You will need to create your own parameter file to include any of the parameters you will be using in you MainBuild.bicep file.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s