Table of Contents
Introduction
Azure Virtual Desktop (AVD) is Microsoft’s comprehensive Desktop-as-a-Service platform, enabling secure remote access to Windows desktops and apps from virtually anywhere. While powerful, deploying AVD in a robust, enterprise-ready manner can be complex: it requires careful orchestration of network, identity, security, storage, and user access. Manual configuration is time-consuming, error-prone, and difficult to scale or audit.
Supportability it is also worth calling out that currently only Hybrid identities are supported for FSLogix so this is setup where Entra Connect is in place and my identities are syncing from on-premises to the cloud.
To solve these challenges, I created a modular, fully automated deployment suite for AVD, leveraging Bicep/ARM templates and PowerShell scripts. This toolkit enables organisations to deploy a secure, best-practice AVD environment rapidly and consistently, with built-in automation for identity, storage, and security policies.
The Problem: Why Automate AVD Deployment?
Traditional manual deployment of AVD involves:
- Creating and securing storage for user profiles (with FSLogix and Azure AD Kerberos)
- Building host pools, VMs, and networking with correct access controls
- Registering session hosts, configuring DNS, and tweaking group memberships
- Managing Conditional Access, admin consent, and NTFS permissions
This process is not only tedious, but risks misconfiguration—especially in environments with hybrid identity or strict compliance needs. Automation solves this by:
- Reducing human error
- Enforcing best practices (e.g., least privilege, private endpoints)
- Providing an auditable, repeatable, and scalable deployment path
- Saving time for both IT admins and end users
The Solution: Modular AVD Automation Suite
This project consists of five modular scripts/templates, each responsible for a critical phase in the AVD deployment journey. Below, I detail each step, what it does, and how you should run it.
I have create a GitHub Repo for this that you can access here: andrew-kemp/AzureVirtualDesktop: AVD Scripts and resources
Assumptions
I have made the following assumptions for this script:
- You have an existing vNet configured in a resource group
- You have 2 existing Groups in Entra one for user and one for admin access. This can be used for both the FSLogix access control and/or the AVD Login permissions. This can be a synced group if required.
- You have a Management device to add the DNS entry for the storage from, as you all know you should never just log onto a DC or DNS server to perform these kind of tasks 😉.
- You have a Windows 11 Domain hybrid joined device to set the permissions for FSLogix from.
01-Deploy-CoreAVDInfra.bicep / 01-Deploy-CoreAVDInfra.json
What it does:
- Sets up the core AVD infrastructure: storage with Azure AD Kerberos and private endpoint, networking, FSLogix shares, host pool, app group, and workspace.
- Applies least-privilege RBAC assignments to storage.
- Outputs resource IDs for downstream automation.
How to run:
Deploy via Azure CLI, PowerShell, or the Azure Portal. You’ll provide parameters for storage naming, AD/Kerberos config, vNet/subnet, and RBAC group Object IDs.
If deploying via a custom script in the Azure Portal fill in the following details:

- Resource Group – This can be a new resource group of an existing resource group
- Region – This corresponds to the region of the resource group
- Storage Account Name – This needs to be unique, this can only be lower-case and numeric.
- Kerberos Domain Name and GUID are needed for the Entra Kerberos settings. This can be obtained by running the following commands from a domain joined devce:
$domainInformation = Get-ADDomain
$domainGuid = $domainInformation.ObjectGUID.ToString()
$domainName = $domainInformation.DnsRoot
$domainGuid
4dcd55ce-5e53-4a52-871e-2f68fcd90c4f
$domainName
ad.kemponline.co.uk

- SMB Share Contributor Group Object ID – This is the Object ID of an Entra Group (Can be synced) for the Users who will need contributor rights to the Shares
- SMB Share Elevated Contributor Group Object ID – This is the Object ID of an Entra Group (can be synced) for the users who will have full control over the shares.
The final section is to set the networking specifics. This assumes you already have a vNet in Azure to use so please specify the name of the:
- Resource group for the vNEt
- The name of the vNet
- The Subnet you want to use
- The Prefix of the Private Endpoint
This then will go a head and create to core infrastructure for AVD:
- Resource Group of set to
- Host pool
- Workspae
- Application Group
- Storage Account with no public access
- Storage account private endpoint
- 2 shares on the storage
- RBAC permissions for 2 groups to access the shares.
02-Deploy-SessionHosts.Bicep / 02-Deploy-SessionHosts.json
What it does:
- Provisions as many Windows 11 AVD session hosts as you need.
- Joins each VM to the vNet, configures DNS, and registers to the host pool.
- Attaches extensions for Entra ID join, guest attestation, and a custom script for session prep.
How to run:
Deploy via Azure CLI, PowerShell, or Portal. You’ll need the host pool registration key from step 1, plus session host count, admin credentials, storage info, and DNS settings.
If running via the Custom Deployment option in the Azure Portal you will see something like this: Remember to either select the resource group you created previously or create a new one if you want.


Then you will need to set the following:
- Region – leave this to the default of the resource group
- Session Host Prefix – This is the prefix of your AVD Hosts so AVD-0, AVD-1 and so on.
- The number of session hosts you’d like default is 2
- the local admin password
- the local admin username
- The Host pool registration key – this is obtained from the Host pool that you created in the previous step.
- Enter the Virtual network details, again this is assuming that you already have a vNet in place:
- Resource Group of the vNet
- vNet Name
- Subnet name
- the storage account name, now this is used in 02a-SessionHostPrep.ps1 as it sets the registry values for FSLogix
- DNS servers – These are optional incase your vNet has a different DNS setting. you can leave blank if required.
- The URL to where 02a-SessionHostPrep.ps1 is hosted, mine is in GitHub and uses the Raw URL.
Validate the settings and once set run the deployment and you will be left with your AVD Session hosts, Entra Joined, Enrolled in Intune, and configured with the FSLogix settings.
02a-SessionHostPrep.ps1
What it does:
- Runs automatically as a VM extension for each session host.
- Configures FSLogix registry keys, enables Cloud Kerberos, and removes unwanted UWP/Store apps.
How to run:
No manual steps—this runs as part of the custom script extension attached by the Bicep/ARM template above.
This script is only used to configure the FSLogix settings and remove the unwanted built in Windows Applications.
03-AVD-Ent-Config.ps1
This script finalises the AVD install.
How to run:
Run interactively in Azure Cloud Shell or PowerShell with the required modules (Az, Microsoft.Graph). Follow the prompts for group names, app group, and confirmation actions.
What it does:
- Interactive script to create (or use existing) AAD groups for AVD users, admins, and devices.
- Assigns appropriate RBAC roles for AVD and VM access.
- Enables VM auto-shutdown for cost control.
- Guides you through excluding the storage app from Conditional Access.
- Crucially: Prompts and reminds you to grant admin consent for the [Storage Account] app in the Entra Portal, which is required for FSLogix shares to work.
This will save you from having to go to your Conditional Access Policies and excluding the Storage enterprise application from each Conditional Access policies that is targeted at the “All Cloud Apps”
After completion, manually grant admin consent in Entra (Azure AD) for the [Storage Account] mystorage.file.core.windows.net application. Without this, FSLogix will not function.
04-Add-StorageDNSRecord.ps1.ps1
In order for FSLogix to work you will need to update your internal DNS so it will know where mystorage.file.core.windows.net is located. This can be run from your management device that has the Remote Server Admin Tools (RSAT) installed on, as you should never (or not need to)log on to your Domain Controller or DNS server to update things like DNS records.
What it does:
- Adds a DNS A record for your storage account’s private endpoint in your internal DNS zone.
- Ensures the DNS zone exists, creates it if needed, and adds the record for the storage account.
How to run:
On your DNS administration server (with the DNSServer PowerShell module), run the script and follow prompts for DNS server, zone, storage account, and private endpoint IP.
05-MountAndSetSharePermissions.ps1
Finally you will need to run this script logged on to a Windows Computer as a user assigned the admin rights for FSLogix. This will set the ACL’s on the shares for FSLogix preventing un authorised access.
What it does:
- Maps the FSLogix profiles and redirections shares from Azure Files using UNC paths.
- Sets secure NTFS permissions for your AVD admin and user groups.
- Disconnects mapped drives after applying permissions.
How to run:
On a hybrid-joined Windows device (with permissions on the storage account), run the script and follow prompts for drive letter, storage account name, and group names.
Summary & Conclusion
Deploying Azure Virtual Desktop at scale shouldn’t be a manual, error-prone process.
This modular toolkit makes enterprise-grade AVD deployments rapid, repeatable, and secure.
By combining infrastructure-as-code (Bicep/ARM) with interactive PowerShell for identity and permissions, you get:
- Automated, best-practice infrastructure
- Secure storage and networking
- Streamlined identity and access configuration
- Clear manual steps for admin consent and DNS
Don’t forget: After running the config script, always grant admin consent for your [Storage Account] app in Entra ID—this is essential for FSLogix to work!
As always i’d love to know how you use this, if there are any additions you’d like to see in it or if there is anything not working or working how you’d think it should.