Leveraging the Windows Virtual Desktop Broker to Broker Remote Desktop connections to Azure DevTest Lab Virtual Machines

In this post I want to look at how we can combine two of Azure's Desktop services Azure Virtual Desktop and Azure DevTest Labs.

Leveraging the Windows Virtual Desktop Broker to Broker Remote Desktop connections to Azure DevTest Lab Virtual Machines

In this post I want to look at how we can combine two of Azure's Desktop services:

Let's look at each of these services in a little more detail and look at what the Architecture typically looks like when organizations deploy these services.

Windows Virtual Desktop

Windows Virtual Desktop is Microsoft's Azure based Multi-session Virtual Desktop Infrastructure (VDI) service; I won't go into detail regarding the service as we will only leverage a small part of the solution for our scenario.

From the official documentation we can see some of the possibilities for leveraging the service:

  • Set up a multi-session Windows 10 deployment that delivers a full Windows 10 with scalability
  • Virtualize Microsoft 365 Apps for enterprise and optimize it to run in multi-user virtual scenarios
  • Provide Windows 7 virtual desktops with free Extended Security Updates
  • Bring your existing Remote Desktop Services (RDS) and Windows Server desktops and apps to any computer
  • Virtualize both desktops and apps
  • Manage Windows 10, Windows Server, and Windows 7 desktops and apps with a unified management experience

As this is a managed service Microsoft manages the control plane components, in our solution we are looking to leverage some of these components specifically the Connection Broker and Gateway:

  • Web Access: The Web Access service within Window Virtual Desktop lets users access virtual desktops and remote apps through an HTML5-compatible web browser as they would with a local PC, from anywhere on any device. You can secure Web Access using multifactor authentication in Azure Active Directory.
  • Gateway: The Remote Connection Gateway service connects remote users to Windows Virtual Desktop apps and desktops from any internet-connected device that can run a Windows Virtual Desktop client. The client connects to a gateway, which then orchestrates a connection from a VM back to the same gateway.
  • Connection Broker: The Connection Broker service manages user connections to virtual desktops and remote apps. The Connection Broker provides load balancing and reconnection to existing sessions.
  • Diagnostics: Remote Desktop Diagnostics is an event-based aggregator that marks each user or administrator action on the Windows Virtual Desktop deployment as a success or failure. Administrators can query the event aggregation to identify failing components.
  • Extensibility components: Windows Virtual Desktop includes several extensibility components. You can manage Windows Virtual Desktop using Windows PowerShell or with the provided REST APIs, which also enable support from third-party tools.

The Microsoft Azure team have also published an excellent reference Architecture for running Windows Virtual Desktop in the enterprise, so I would recommend you look at it before planning any WVD based solution.

Azure DevTest Labs

Azure DevTest Labs provides a mini Azure control plan which empowers teams to create and manage environments in a self-service manner. The biggest benefits for me are the controls (around Cost, Security & Compliance) it provides for building a compliant Desktop solution, also the agility which it gives end users by being a self-service solution is currently unmatched.

This of course comes at a cost as organizations still have to engineer their Desktop solution, but the good news is that DevTest Labs & Azure provide all the Lego pieces. Some of the capabilities which are listed in the official documentation are:

  • Create VMs quickly by following fewer than five simple steps.
  • Choose from a curated list of VM bases that are configured, approved, and authorized by the team lead or central IT.
  • Create VMs from pre-created custom images that have all the software and tools already installed.
  • Create VMs from formulas that are essentially custom images combined with the latest builds of the software that's installed when the VMs are created.
  • Install artifacts that are extensions deployed on VMs after they're provisioned.
  • Set auto-shutdown and auto-start schedules on VMs.
  • Claim a pre-created VM without going through the creation process.

The Microsoft Azure team have published a reference Architecture for running Azure DevTest Labs for enterprises, I would recommend you look at it before planning any DevTest Labs based solution.

Combining Windows Virtual Desktop & Azure DevTest Labs

There are many reasons why we would want to combine Windows Virtual Desktop and Azure DevTest Labs, to name just a few:

  • Remote Desktop Host and port 3389 are not accessible from public internet, connectivity happens via WVD Reverse Connect.
  • Support for Azure Active Directory Authentication.
  • Azure DevTest Labs provides us the ability to build single session hosts and give self-service capabilities to end users but still maintain control centrally.

The Architecture would look something like the following, the primary reason for leveraging WVD being the brokering capabilities.

So now we know why we want to combine these solutions, let's look at how we can put them together.

Configure Windows Virtual Desktop

To configure our solution, we need to complete the following steps:

Create Azure Active Directory Security Group

As we will be direct assignment on the Host Pool's Application Group, it makes sense to leverage a Security Group rather than having to assign individual users to the Application Group.

In the Azure Portal open the Azure Active Directory blade, navigate to Groups -> New Group and create a new Security Group.

Create a New Host Pool

I have created a sample ARM Template which will deploy all the required WVD resources. If you would like to create them yourself manually you can do it from the Azure Portal. Click Create a resource, select Host Pool and click create for our scenario make sure that you configure the following options:

  • Enable Personal Pool
  • Enable Direct Assignment

Do not add any Virtual Machines to the Pool these will be added automatically from DevTest Labs.

Select Yes to register desktop app group and click create new workspace.

Finally click Review + create to trigger the creation of your resources once successfully deployed you will see the following in your Resource Group.

Next step we can confirm the Application Group is configured correctly with Application group type set to Desktop.

We need to configure the User/Group Assignment for the Application Group (I leverage AAD Security groups, users assigned to Session Hosts should be member of this group in AAD). Navigate to Assignments -> Add and search for the Security Group which you created earlier in this guide.

You use the Registration Key to register machines for this Host Pool, we can also get the key via the Azure CLI or PowerShell for automation purposes.

If needed, we can also set additional Remote Desktop connection properties for example disabling Network Level Authentication between the Gateway and the RDP host.

Configure Azure DevTest Labs

To configure our solution, we need to complete the following steps:

Create a DevTest Lab

This guide relies on the fact that your DevTest Lab machines will be joined to an Active Directory domain, you could also leverage Azure Active Directory Join but this is out of scope.

You can follow the officially documented steps to create your DevTest Lab resource.

Create DevTest Labs Artifact

We can either create a custom DevTest Lab Artifact or use one of the existing artifacts in the Public Artifact Repository. Typically, we need to perform the following actions manually on new Lab Machines.

  1. Download and install the Windows Virtual Desktop Agent.
  1. Download and install the Windows Virtual Desktop Agent Bootloader.

In my case I built a custom Artifact which can be used to join a DevTest Lab Machine to an existing WVD Host Pool. I also published two chocolatey packages which are a dependency for the Artifact:

Create a DevTest Lab Formula

For our test scenario we can define a simple DevTest Labs Formula for Virtual Machine deployment.

Take note that we only assign a private IP Address to new Machines, the new Machine is also marked as claimable.

We have also specified that three mandatory artifacts should be applied to new Lab Machines, they will perform the domain join and install and register the WVD Agent.

The "Install Windows Virtual Desktop Agent" Artifact will require the Host Pool's Registration Key; the key has a fixed validity period, therefore it may make sense to trigger the Artifact Install based on Event Grid events rather than hard coding it using Formulas.

When we deploy a New DevTest Lab machine, this machine will now be added as a Session Host on our Windows Virtual Desktop Host Pool the next step would be to automatically assign the relevant user.

Trigger Windows Virtual Desktop Session Host User Assignment

There are multiple ways we can implement this, for example we could maintain a pre-provisioned Pool of DevTest Labs Machines which users are able to Claim as needed. We would then setup an event handler which listens for the DevTest Lab Machine claim event and assigns the user to this machine.

I have started working on a simple example for doing just that via the Azure REST API's and an Azure Function, which automates the steps described for Assigning users to a WVD Session Host.

import { AzureFunction, Context } from '@azure/functions';
import * as msRest from "@azure/ms-rest-js";
import * as msRestAzure from "@azure/ms-rest-azure-js";
import * as msRestNodeAuth from "@azure/ms-rest-nodeauth";
import { AppAuthentication } from '../SharedCode/AppAuthentication';
import { DesktopVirtualization } from '../SharedCode/DesktopVirtualization';

const auth = new AppAuthentication();
const clientOptions: msRestAzure.AzureServiceClientOptions = { };
// WVD Details
const subscriptionId = process.env["SUBSCRIPTION_ID"];
const resourceGroupName = process.env["RESOURCE_GROUP_NAME"];
const hostPoolName = process.env["HOST_POOL_NAME"];

// When we get here we have already filtered only the events we are interested in
const handleEvent: AzureFunction = async function (context: Context, eventGridEvent: any): Promise<void> {
    // Log the incoming event payload
    context.log(eventGridEvent);

    // Get standard Lab event properties
    const resourceUri = eventGridEvent.data.resourceUri;
    const resourceUriSegments = resourceUri.split("/");
    const subscription = resourceUriSegments[2];
    const resourceGroup = resourceUriSegments[4];
    const lab = resourceUriSegments[8];

    // Get our login credentials and setup the DTL client
    const credentials = await auth.getAppServiceCredentialsAsync();
    const client = new msRestAzure.AzureServiceClient(credentials, clientOptions);
    const desktopVirtualization = new DesktopVirtualization(client);

    // Claim event for the DTL VM
    if(eventGridEvent.eventType === "Microsoft.Resources.ResourceActionSuccess" && 
        eventGridEvent.data.operationName === "microsoft.devtestlab/labs/virtualmachines/claim/action") {
        // Get non-standard Lab event properties
        const vm = resourceUriSegments[10];
        const claimer = eventGridEvent.data.claims["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"];
        context.log(`VM '${vm}' claimed by '${claimer}' for DTL Lab '${lab}'.`);

        // Example of performing user direct assignment for the WVD session host
        if(await desktopVirtualization.AssignUser(subscriptionId, resourceGroupName, hostPoolName, vm, claimer))
        {

        }
    }

    // Unclaim event for the DTL VM
    if(eventGridEvent.eventType === "Microsoft.Resources.ResourceActionSuccess" && 
        eventGridEvent.data.operationName === "microsoft.devtestlab/labs/virtualmachines/unclaim/action") {
        // Get non-standard Lab event properties
        const vm = resourceUriSegments[10];
        const claimer = eventGridEvent.data.claims["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"];
        context.log(`VM '${vm}' unclaimed by '${claimer}' for DTL Lab '${lab}'.`);
    }
};

export default handleEvent;


Remote Desktop App

Once a user has been assigned to a WVD Session Host, it will appear in their Remote Desktop App and they can connect to their machine as normal.

This post can be combined with my prior post - Engineering Compliant Azure Shared Image Gallery Images and sharing them across multiple Azure Active Directory Tenants for setting up image factory for compliant Images.