Wednesday, October 19, 2016

Azure Resource Manager and JSON templates to deploy RDS in Azure IaaS – Part 5

This is part 5 in a series of articles on using Azure Resource Manager (ARM) and JSON templates to create a full Remote Desktop Services environment in Azure IaaS.

Let’s briefly reiterate what was previously covered on this subject. It all started with a first RDS deployment on Azure IaaS covered in the article Full HA RDS 2016 deployment in Azure IaaS in < 30 minutes, Azure Resource Manager FTW! Although this first template already creates a high available RDS environment on Azure, many improvements and features have been added after that. In a second article called RDS on Azure IaaS using ARM & JSON part 2 I covered adding SSL certificate management, the creation of Session Collections, and setting session time out settings. To help visualize what is going on during the automated creation of the environment I created a third article in which I published 2 short videos’ which were also shown at Microsoft Ignite 2016. Up until now the automated RDS deployment was based on the default Windows Server 2016 TP5 image in Azure for the RD Session Host role. Last week after Windows Server 2016 became GA, I updated the deployment to support that version. For demo or PoC environments the default image is ideal, however is most scenarios your, for example, finance department would not be using calculator as their primary published application. In a forth article I covered adding a Custom Template Image for the RD Session Host servers to be able to publish custom applications. This article also briefly touched on RD Licensing configurations, and basic RD Web Access customizations.

So, back to part 5 of the series. We’ll take everything covered in the previous articles as a starting point and are building on top of that. What features are we adding in this version?

- Configurable Active Directory OU locations
- Securing our Custom scripts in Azure blob
- Load Balancing the RD Connection Broker role
- Restricting access to only allow specific groups

Active Directory OU location
In previous versions of the ARM template all servers containing RDS roles created by the JSON template ended up in the default Organizational Unit (OU) in Active Directory. While it’s relatively easy to move these computer objects to a designated OU structure after the domain join process, it’s a good common practice to create computers objects in the designated OU directly during the domain join. Not only does this save time in the process, in some scenario’s where the default OU might have been changed, unwanted Group Policy Objects could be applied to these new servers. Let’s take the following OU structure as an example.

To accommodate the creation of the RD servers inside a custom OU structure, I have added the following parameters to the environment.

As part of this change I decided to also move away from using Desired State Configuration (DSC) for the domain join process. Instead of DSC I moved to using an extension directly in ARM. There is a good example available on this extension as part of Microsoft’s Azure Quick Start Templates: 201-vm-domain-join.

Below you can see the extension in action. The type needs to be configured as “JsonADDomainExtension”. And as part of the settings, the OUPath can be provided.clip_image006

As a result, the server objects are created in the designated OU’s as part of the domain join process.

Securing the Custom scripts
As discussed in previous blog posts, I use the CustomScriptExtension in ARM to be able to launch a PowerShell script to perform the actual deployment of the RDS roles and further configuration. The idea behind the CustomScriptExtension is that you provide a download location where the VM can download the script and execute it. There are various options to store these files. In some cases, you might want to share your scripts with others, in those cases GitHub is ideal. This is also the location where Microsoft provides many examples as part of the Azure Quickstart Templates they provide. In some cases, you might not want to use GitHub to store the scripts publically. You might have developed a custom script that you only want to use inside your organization. In those cases, its’ very convenient to place those files in a container in Azure in the same subscription where you also deploy the RDS environment. As I’ll discuss later on in this chapter, you can also use this method to secure your custom scripts and prevent others from downloading them. Let’s take a look at how to configure this.

First of all, you need a location (storage account) in the Azure Subscription you want to use. Inside this storage account we create a new container.

We can now use this container to upload our custom scripts. Although you can do this using the Azure Portal or using PowerShell, I prefer to use the Microsoft Azure Storage Explorer. This is a small tool you can download and install on macOS, Linux, and Windows. Get the download here:

Using the Azure Storage Explorer, you can easily manage the files inside the blob storage.

Inside our ARM deployment we can now refer to this location in Azure as our custom scripts location to download the script, we provide this URL inside a parameter.

Although the scripts are now stored inside our Azure Subscription, they are still publically accessible. Anyone that would know the location could browse to the scripts and download them. Again, in some scenarios that might not be an issue if the files don’t contain any sensitive data. But in some scenario’s you will want to prevent public access to the scripts.

In order to create a secure location, the access type of the container needs to be changed to Private.

Setting the Access type to Private ensures that there is no anonymous access. Because of this ,the data inside the container cannot be accessed without providing the access key of the storage account.

Now that the container is set to private, how do we access the scripts from within our ARM deployment? Using the same CustomScriptExtension as before we provide the location (URL) of the script as part of the fileUris settings. To be allowed to access the script from within our ARM deployment, we provide both the storageaccountname as well as the storageaccountkey. To make sure these are encrypted we place them inside the ProtectedSettings section of the CustomScriptExtension.

And to make things fully secure we obviously don’t store the storageaccountkey in plain text in our scripts, but rather store the storageaccountkey as a secret inAzure Keyvault.

Using a KeyVault reference we can safely access the storageaccountkey in our ARM deployment

Using this method, we have now created a secure location to store scripts and other files that can be leveraged by our ARM deployment.

Load balancing initial connection RD Connection Broker
In previous versions of the ARM deployment I’ve setup a load balancer to provide high availability and load balancing for the RD Web Access and RD Gateway role. This load balancer, also created by ARM, is equipped with a public IP address to be able to access the environment from the outside. But what about load balancing of the RD Connection Broker role? As you might know, since Windows Server 2012, the RD Connection Broker always handles the initial connection of any user connecting to the environment. Although this process is transparent to the end user, any incoming session will be connecting to the RD Connection Broker first via RDP (3389). The RD Connection Broker then redirects the session, resulting in another connection toward the final destination, one of the RD Session Host servers. This process is explained in more detail here. So in a scenario where we have multiple RD Connection Brokers configured in a High Available scenario, we ideally also make sure we load balance this initial connection. Although this can also be performed by using DNS Round Robin, DNS RR is not aware of RD Connection Broker servers that unreachable and the workload will not be divided equally in most scenario’s. Instead of DNS RR, lets leverage the same type of Azure load balancer we used for the RD Web Access and RD Gateway role, this time load balancing an internal connection.

Since I didn’t cover the load balancer in much detail in previous articles, let’s take a closer look at how this is configured. Inside our ARM deployment we create a new Load Balancer resource as specified below. We place it in the same subnet as the RDS environment and provide a static internal IP Address.

Inside the RD Connection Broker resources in the ARM template, we specify the load Balancer Backend Address Pools property referring back to the pool we also specified above. This is to make sure both RD Connection Broker servers will become members of the pool, and thus become the to be load balanced servers.

Next, we define the load balancing rules. For this scenario we specify will want to load balance TCP port 3389 and set additional parameters for setting like FloatingIP, Idle Time out etc.

Lastly, we configure the probes. By configuring a probe, we tell the load balancer which ports to probe on the destination servers to determine if one of the servers is down.

As an end result we have a new Load Balancer in Azure

And the Load Balancer holds a Back End Pool containing our 2 RD Connection Broker servers.

Now that we have an internal load balancer with our 2 RD Connection Broker servers, we need to take one final step to make sure we can start using it. As you can see the RD Connection Broker Load Balancer was configured with the internal IP Address So we now need to make sure that the RDS deployment points to this IP address to start serving incoming connections. The ARM deployment is already configured to create a HA configuration for the RD Connection Broker role.

As also explained in previous articles, this is configured by a PowerShell script inside an ARM CustomScriptExtension. One of the parameters that is passed to the script is the RD Connection Broker DNS name (RD Connection Broker Client Access Name)

It’s this DNS name that is used by clients connecting to the RDS deployment so we need to make sure this DNS name resolves into the internal IP Address of the load balancer. Since we are using the RD Gateway in this deployment as well, more specifically we need to make sure the RD Gateway role can resolve the DNS name. To accomplish this, we create an A record in DNS matching the Connection Broker Client Access Name and pointing to the IP Address of the load balancer.

And that’s it. Incoming connections through the RD Gateway are now send to one of the two RD Connection Brokers load balanced by the Azure Load Balancer.

Restricting access to only allow specific groups
By default a Session Collection provides access to the Domain Users group in Active Directory. In most cases however you want to restrict access to a specific group of users. To accommodate this I added a new parameter to the ARM deployment.

This parameter is added to the Custom Script extension we’ve mentioned several times before. This scripts adds the AD group provided in the parameter and removes the default Domain Users group. Taking a look at the Server Manager console we can confirm that the RDS deployment is now only accessible for members of the specified AD group. This is a permission on the Session Collection itself, in an upcoming article in this series, I’ll also cover applying specific groups to access the RD Gateway components.

Tuesday, October 18, 2016

Fix available for User Profile Disk unmount issue causing temporary profiles

A fix is now available for the User Profile Disk issue where a UPD does not correctly unmount at logoff causing temporarily profiles!

"...Addressed issue where the user profile disk (UPD) does not get unmounted when a user logs off. Therefore, users get temporary profiles and are not able to work with their own profiles during their next logon. The Event ID 20491 with a description of “Remote Desktop Services could not disconnect a user disk for the user account with a SID of <SID>. The error code is 0xAA.93” will be logged..."


The update is part of the October 2016 Preview of Monthly Quality Rollup for Windows 8.1 and Windows Server 2012 R2


Wednesday, October 12, 2016

Windows Server 2016 GA available in Azure! – used it to deploy RDS on Azure IaaS!

Window Server 2016 DataCenter (14393.321) is now available as an image in Azure IaaS.
I decided to use it to roll out as part of my existing ARM Deployment of RDS on Azure IaaS using JSON. One of the awesome things about the ARM/JSON approach is that it all it took was adding a single parameter option into the azuredeploy.parameters.json file to complete this!

This is where I added the new SKU inside the template parameters file a newly allowed value.

And before launching the ARM deployment, the SKU can be selected from a dropdown box, now with the default being Windows Server 2016 Datacenter.

The entire deployment is now fully performed on Windows Server 2016, RD Web Access now shows the Windows Server 2016, where Technical Preview 5 (TP5) did not.

This version is not available on MSDN yet, so Cloud First, Mobile First!

Tuesday, October 11, 2016

Added new features to RDS on Azure IaaS using ARM and JSON (licensing, Custom RDSH Templates, branding)

If you have been following this blog over the past couple of weeks, you might have read about my previous articles on deploying RDS on Azure IaaS using ARM and JSON templates. To quickly reiterate those previous articles;

[clip_image018%255B4%255D.jpg]In a first article called “Full HA RDS 2016 deployment in Azure IaaS in < 30 minutes, Azure Resource Manager FTW!” I explained the differences between deployment mechanisms in the classic Azure Portal and the new Azure Portal and gave a brief introduction on what JSON is and how to use Visual Studio to build and deploy directly to Azure, based on JSON templates. I showed the end result of this deployment, a high available RDS deployment on Azure IaaS.

[clip_image002%255B5%255D.jpg]In a second article called “
RDS on Azure IaaS using ARM & JSON part 2 – demo at Microsoft Ignite!” I showed some of the enhancements I had built on top the JSON templates. In this article I showed the ability to add publicly trusted SSL certificates, 2 Session Collections with a few sample RemoteApps, configurable Session Timeout settings and the use of 2 storage accounts. This article also contains a link to a session at Microsoft Ignite 2016 where the deployment of this JSON template was shown.

clip_image001[6]Last week I also separately published the 2 short video’s that show the deployment and the end result in action in an article called Video of Ignite session showing RDS on Azure IaaS deployment using ARM/JSON now online!



In this article I want share some of the additions that I have been adding to the ARM deployment.

Configure Licensing method
The previous version of the ARM deployment already added 2 RD Licensing servers to the deployment which are needed to install RDS CAL’s. What’s now also added is the ability to select the desired RDS Licensing method (per user or per device).
When the deployment is started, the desired licensing method can be selected from a dropdown list.

And after completion of the ARM deployment, the selected licensing method is successfully configured in Server Manager.

For setting the RD Licensing method I’m using a PowerShell command by expanding the already existing PowerShell Extension. In the first article I already briefly mentioned the PowerShell Extension option in ARM. It allows you to specify a PowerShell Script to perform actions within a Virtual Machine once it’s deployed. The screenshot below shows how to call a PowerShell Extension. For more information on how to set these up, also see: Windows VM Custom Script extensions with Azure Resource Manager templates

Add Custom RDSH Image
Previous ARM deployments have all been based on a standard Windows Server 2016 image for the RD Session Host Role. In most scenarios however, you would obviously want to deploy your own applications. To accommodate this, I added functionality to select a custom RD Session Host template image to be used for the RD Session Host servers. This allows you to preconfigure a RD Session Host template with custom applications and settings and provide that as a parameter to the ARM deployment.

As part of the previous ARM deployments I also created the necessary storage accounts prior to creating the Virtual Machines. However, if you want to deploy a virtual machine in ARM based on a custom image, that custom image needs to reside in the same storage account as the to be created virtual machine. Because of this (current) limitation we run into a Chicken and Egg problem. How can we make sure that the Custom Template Image is available in the storage account if that storage account is not created yet? There are basically 2 ways to solve this.

The first option is to copy the custom template image as during the ARM deployment. So, after creating the storage account and before creating the RD Session Host servers. The copy action can be performed using Azure PowerShell, so in order to be able to kick of that PowerShell command as part of ARM we need a “dummy” Virtual Machine and create a Custom PowerShell Extension on that. A good example how to perform this is can be found here: Create VM from a custom image in new storage account. Instead of the “dummy” server you can of course also “abuse” another virtual machine that you are also deploying as part of your ARM deployment and configure the Custom PowerShell Extension on that virtual machine. Also be aware that the copy job can add a significant amount of time to the ARM deployment. In my testing a Windows Server 2016 template with Office 2016 installed took about 15 minutes to complete.

The second option is a different and maybe less complex approach. Since we are creating a RD Session Host template, a storage account to host that template is already in place. Why not reuse that storage account to also host the deployed RD Session Host servers? In that case the custom template does not have to be copied and the ARM deployment only needs to be provided with the storage account name and the template name so it can reuse that storage account and deploy the RD Session Host servers in there.

For my ARM template I chose the second option. As part of the parameters the storage account name and the RD Session Host Custom Image can now be provided.

Using the image property of the Virtual Machine we can instruct ARM to use our Custom Template Image.

In this example I used a Custom Template Image based on Windows Server 2016 with Office 2016, Notepad++ and Paint.NET installed as the sample custom applications.

To be able to immediately publish the custom applications I added the following parameters to the ARM deployment. Again, the deployment uses the existing Custom extensions and leverages PowerShell to publish these applications during the ARM deployment

The end result for the end user is that the custom applications are available and accessible in for example RD Web Access as shown below.

Perform basic RD Web Access Branding & customization
To be able to add some very basic RD Web Access branding I added 2 parameters to the ARM deployment that allow you to specify the Workspace name and change the default logo in RD Web Access.

Again using the existing Custom PowerShell extension, these settings are now changed suing PowerShell Cmdlets and the changes are visible to the end user. Obviously these 2 items are not rocket science, but you can image we could extend this to brand the entire Web Access page if we wanted to including colors, fonts etc.

To summarize this article, using ARM and JSON we are now able to;
- Configure the desired Licensing method (User or Device CAL’s)
- Use a Custom Template Image for the RDSH role
- Publish Custom Applications
- Perform basic RD Web Access branding

If you have questions on how certain parts of this deployment have been performed, feel free to reach out to me! Stay tuned for more updates on this RDS deployment in Azure IaaS using ARM!

Monday, October 3, 2016

Video of Ignite session showing RDS on Azure IaaS deployment using ARM/JSON now online!

clip_image002During Microsoft Ignite last week, fellow RDS MVP Benny Tritsch presented a session called “Get an independent insider’s view of desktop virtualization and session remoting.” In this session he presented an independent view on Remote Desktop Services and related technologies. It contains a lot of awesome content coming from various community members! I contributed a part on provisioning an entire RDS High Available deployment on Azure IaaS in ~40 minutes using Azure Resource Manager (ARM) en JSON templates.

The entire session is now available on demand and I can encourage anyone to check out this session:

also uploaded the two short demo videos of my ARM/JSON section so you can view them separately here:

This part demo’s the status of the Azure Portal Prior to kicking of the ARM/JSON deployment, it then kicks of the deployments and shows parts of the result inside Visual Studio.


This part demo’s the end result of the ARM/JSON deployment. It shows the end result inside Visual Studio as well the end results in both RD Web Access and Server Managerclip_image006

For more information on these JSON templates also see these two previous posts:
- Full HA RDS 2016 deployment in Azure IaaS in < 30 minutes, Azure Resource Manager FTW!
- RDS on Azure IaaS using ARM & JSON part 2 – demo at Microsoft Ignite!

I’m planning to extend the JSON template to allow even further customization of the RDS environment and will share my progress here.

Friday, September 30, 2016