Tags: Microsoft

Azure CDN – Speeding up WordPress on Azure App Service and proving the results

It’s probably no secret that half of the IT blogs out there are running on WordPress or a similar Platform. WordPress is easy to use, simple, and requires little maintenance to run – what’s not to love?!

As with any Website – it doesn’t matter how good the back-end code or server hosting the site is, if your user is half way around the globe from where your site is hosted, the experience may not be that great… and not great experiences do not make for happy visitors. In the case of business/shopping websites this can mean international customers getting a bad experience for example, which is less than ideal if you are looking to grow and provide the same great service to users around the globe.

To combat this issue, a Content Delivery Network (CDN) is a great solution. A CDN essentially spreads your data across geographically separate servers across the world, and ensures that user requests are dealt with by the server that is closest to the end user. It is worth noting this means closest in networking distance, which is not always the same as physical distance. Azure has a great CDN offering with Points of Presence spread all over the world: https://docs.microsoft.com/en-us/azure/cdn/cdn-pop-locations – you would need to be in a VERY remote location to not have an almost local POP.

I’m going to test out WordPress running on Azure App Service natively, and then setup Azure CDN and compare the two – making use of Performance tests within Azure App Service to highlight the difference in metrics for both arrangements (Without CDN, and with CDN).

To start, I have deployed WordPress on Azure App Service with MySQL In App using the below template:

Head over to https://github.com/Azure/azure-quickstart-templates/tree/master/wordpress-app-service-mysql-inapp for the template.

Once this was deployed I completed the usual WordPress setup, and I now have a functioning site – but without any content. To create some content for testing, I used a plugin designed to generate posts and pages (with images) to give the site some content (including images) we can use when testing response times:

Once installed and run, this plugin gave me lots of posts and pages, to simulate the content of a real site, and all of these posts included an image:

Now I can start seeing how the site performs – without a CDN in place. Initially, I’m using a Performance Test to measure site performance – which can be accessed from the App Service pane in the Portal:

Creating a test is simple, I am just going to simulate 1000 users accessing in a 1 minute window:

As you can see, the metrics are coming back from the West EU test as follows with no CDN:

Average response time is probably the most key metric here – so 2.89 secs average from the West EU test region. To give an idea of the variation, I ran the test again, but this time from the East Asia region:

As you can see, there is a noticeable speed difference, albeit one that is to be expected. Based on the metrics (2.89s average West EU vs 5.81 average East Asia), we can see that the average response time for clients in the East Asia Region is around 200% that of those in the West EU region. So… about twice the waiting time for the page to load.

Configuring the Azure CDN

Configuring a CDN Endpoint for Azure Web Apps is extremely simple – it can be done from the Web App section of the Azure Portal:

For this test I have configured the CDN Endpoint as below. I’m using the Standard Akamai Offering for my test:

Once we have filled in the details the Endpoint is created:

Once the endpoint is created, we are presented with a new URL to access the CDN version of the site:

I then configured WordPress to integrate with the CDN using the CDN Enabler plugin:

To check the function – if we now have a look at the properties of an image on the page, we can see it is being sourced from the Azure CDN, and thus from a location geographically close to our users:

Because the plugin includes any content in wp-content, any image we upload to the Website will be provided to users via the CDN. Next up, I re-ran the performance tests to measure the performance differences now that we have switched some content to the CDN:

West EU:

East Asia:

Based on the above test results, when implementing the CDN endpoint, we saw the following differences in test results in terms of average speed increases:

without CDN with CDN % speed increase
West EU 2.89 1.52 47
East Asia 5.81 2.11 64

There are a few key results from this test:

  • Utilizing the CDN improved performance in both the local and remote regions – in both cases significantly
  • Remote regions saw the greatest performance boost at a 67% average load time speed increase
  • The performance in the remote (East Asia) region was better than the original test of the West EU region after we had added the CDN endpoint
  • Once configured both in Azure and in the Application (WordPress) there is no further configuration required
  • We can further improve the speed by taking more elements of the Web Application and bringing them into the CDN – for example theme files, static code, CSS etc. In my test I have only included the wp-content directory but there is more that could be added.

Hope this has been useful… until next time!





Testing out the Azure Firewall Preview

Azure Firewall was released for preview this week, so I thought I would give it a quick try and look at some of the features available. The firewall provides the following features at the current time:

  • Built-in high availability – built into the Azure Platform, so no requirement to create load balanced configurations
  • Unrestricted cloud scalability – the firewall can scale to meet your requirements and meet changing traffic demands
  • FQDN filtering – outbound HTTP/S traffic can be filtered on a specific set of domain names without requiring SSL termination
  • Network traffic filtering rules – centrally create allow or deny network filtering rules, based on IP, port, and protocol. Azure Firewall is fully stateful, and rules can be enforced and logged across multiple subscriptions and VNETs.
  • Outbound SNAT support – outbound virtual network traffic IP addresses are translated to the Azure Firewall Public IP so you can identify and allow VNET traffic to remote Internet Destinations
  • Azure Monitor logging –  All Firewall events are integrated with Azure Monitor. This allows archiving of logs to a storage account, streaming to Event Hub, or sending them to Log Analytics.

You can read more about the features here: https://docs.microsoft.com/en-us/azure/firewall/overview

Getting access to the Azure Firewall is easy – it’s built directly into the VNET Configuration window:

However, before we can use this, we need to enable the Public Preview for our Subscription with a few PowerShell commands:

Register-AzureRmProviderFeature -FeatureName AllowRegionalGatewayManagerForSecureGateway -ProviderNamespace Microsoft.Network
Register-AzureRmProviderFeature -FeatureName AllowAzureFirewall -ProviderNamespace Microsoft.Network

You’ll need to wait upto 30 minutes at this point for the request to be enabled – see https://docs.microsoft.com/en-us/azure/firewall/public-preview for further information. You can run the following commands to check the status:

Get-AzureRmProviderFeature -FeatureName AllowRegionalGatewayManagerForSecureGateway -ProviderNamespace Microsoft.Network
Get-AzureRmProviderFeature -FeatureName AllowAzureFirewall -ProviderNamespace Microsoft.Network

If all is well – it should look like this:

Finally, run the following command to complete the setup:

Register-AzureRmResourceProvider -ProviderNamespace Microsoft.Network

Before we can add a Firewall to a VNET, we need to create a subnet called “AzureFirewallSubnet” – this is to allow the firewall to communicate with addresses on the VNET. Once this is completed, we can setup the Firewall. This is just a case of filling in some basic details:

Once we have completed the basic details, we can review and complete the deployment:

Now that the Firewall is created, we are ready to start testing. In order to test the Firewall out, we need a subnet that is routed out via this Firewall. To do this, I used a route table that directs traffic to the Firewall IP:

We now have a Subnet within our VNET that is routed via the Azure Firewall – so now we can test out some rules. My lab environment is now setup as below (Note the jump VM in a separate Subnet that is NOT routed to the Firewall. This is to allow me to RDP to the test box as I have no VPN in place to test from etc.):

From the Test VM, internet access is now blocked – because there is no firewall rule in place to allow it. I am going to add an “Application Rule collection” which I will use to allow HTTPS access to jakewalsh.co.uk, but not HTTP access. This is configured from the Firewall management interface via the Azure Portal:

Then you will be presented with the following window:

Once I have clicked on “Add” the rule will be added to the Azure Firewall. From my test VM, access to https://jakewalsh.co.uk works, but note that HTTP does not:



The same also works in reverse, so we can selectively block HTTP or HTTPS sites as we require.

As well as the Application Rules we can deploy, we can also create more traditional firewall rules (replace

Overall, the Azure Firewall complements and extends the functionality of Network security groups and gives additional control over networks residing within Azure. The rules are simple to adjust and easy to work with. It will be promising to see how this feature develops over the coming months…

Azure VM Scale Sets and Remote Desktop Services?

When using any environment that provides virtual desktops at scale, it makes sense to have only the required number of resources running at the right time – rather than all of the resources all of the time. The usual approach to this is to use power management – so unused virtual machines are shut down when not in use.

With Azure we have another potential option designed for large workloads – to use Virtual Machine Scale Sets. This allows us to automatically scale up and down the number of Virtual Machines based on various factors and choices. This effectively allows us to ensure the most economical use of resources – as we never pay for more than we need to use, because the machines are de-allocated when not required. Scale Sets also provide a number of features around image management and VM sizing that could be useful for VDI environments.

In this post I am going to explore the validity and feasibility of VM Scale Sets for a Remote Desktop Services Environment. To start this post – I have the following environment configured, minus the scale set:

Note: if you need an RDS environment – this Azure template is awesome: https://azure.microsoft.com/en-gb/resources/templates/rds-deployment/ – I would advise using multiple infrastructure VMs for each role if this is a production service though.

Next – I configured a single server with the RDS Session Host role and all of the applications I require, as this will become our VM image. I then ran sysprep /generalize as per the Microsoft instructions for Image Capture in Azure. (See here). Once this is done we need to stop and de-allocate the VM, and then we need to turn this into an image we can use with a scale set:

$vmName = "rdsimage01"
$rgName = "eus-rg01"
$location = "EastUS"
$imageName = "rdsworker"
Stop-AzureRmVM -ResourceGroupName $rgName -Name $vmName -Force
Set-AzureRmVm -ResourceGroupName $rgName -Name $vmName -Generalized
$vm = Get-AzureRmVM -Name $vmName -ResourceGroupName $rgName
$image = New-AzureRmImageConfig -Location $location -SourceVirtualMachineId $vm.ID
New-AzureRmImage -Image $image -ImageName $imageName -ResourceGroupName $rgName

Once this is done – we have a VM image saved:

So once we have an image – we can create Virtual Machines from this image, and create a Scale Set that will function as the means to scale up and down the environment. However – we need to do some more work first, as if we just scale up and down with a sysprepped VM, we end up with a VM off domain that won’t be of any use to us…. !

Usually – I just spin up Lab VMs using a JSON Template that creates the VM and joins it to an existing lab domain, using the JoinDomain extension. This saves me lots of time and gives me VMs deployed with minimal input (just a VM name is all I have to enter):

      "apiVersion": "2015-06-15",
      "type": "Microsoft.Compute/virtualMachines/extensions",
      "name": "[concat(parameters('dnsLabelPrefix'),'/joindomain')]",
      "location": "[resourceGroup().location]",
      "dependsOn": [
        "[concat('Microsoft.Compute/virtualMachines/', parameters('dnsLabelPrefix'))]"
      "properties": {
        "publisher": "Microsoft.Compute",
        "type": "JsonADDomainExtension",
        "typeHandlerVersion": "1.3",
        "autoUpgradeMinorVersion": true,
        "settings": {
          "Name": "[parameters('domainToJoin')]",
          "OUPath": "[parameters('ouPath')]",
          "User": "[concat(parameters('domainToJoin'), '\\', parameters('domainUsername'))]",
          "Restart": "true",
          "Options": "[parameters('domainJoinOptions')]"
        "protectedSettings": {
          "Password": "[parameters('domainPassword')]"

See https://github.com/Azure/azure-quickstart-templates/tree/master/201-vm-domain-join for more details and to use this template.

Now that we have a template – we are ready to go. I’m using Visual Studio to create the JSON for my deployment – and fortunately there is a built in scale set template we can use and modify for this purpose:

With the template up and running, we just need to add some parameters – and we can run a basic test deployment to confirm everything is working. My parameters for the basic template are shown below:

A quick test deployment confirms we are up and running:

However, there are a few issues with the template we need to correct – namely:

  • The machines are not joined to the Domain – and we need to place them into the correct OU for GPO settings too
  • A new VNET is created – we need to either use peering (prior to creation – or domain join operations will fail), or better an existing VNET already setup
  • The load balancer created is not required – we’ll be using the RDS Broker anyway

For this test – all I am concerned about is the domain join and VNET. The load balancer won’t be used so I can just discard this – however, the VNET and Domain Join issues will need to be resolved!

Issue 1 – using an existing VNET

To fix this, I am not going to reinvent the wheel – we just need some minor adjustment to the JSON file, based on this Azure docs article – https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-mvss-existing-vnet. In short, this will achieve the following:

  1. Add a subnet ID parameter, and include this in the variables section as well as the parameters.json
  2. Remove the Virtual Network resource (because our existing VNET is already in place)
  3. Remove the dependsOn from the Scale Set (because the VNET is already created)
  4. Change the Network Interfaces of the VMs in the scale set to use the defined subnet in the existing VNET

Issue 2 – joining the Scale Set VMs to an AD Domain

To get the VMs in the scale set joined to an AD Domain we need to make use of JsonADDomainExtension.

"extensionProfile": {
    "extensions": [
            "name": "joindomain",
            "properties": {
                "publisher": "Microsoft.Compute",
                "type": "JsonADDomainExtension",
                "typeHandlerVersion": "1.3",
                "settings": {
                    "Name": "[parameters('domainName')]",
                    "OUPath": "[variables('ouPath')]",
                    "User": "[variables('domainAndUsername')]",
                    "Restart": "true",
                    "Options": "[variables('domainJoinOptions')]"
                "protectedsettings": {
                    "Password": "[parameters('domainJoinPassword')]"

With this added to the JSON template for our deployment, we just need to add the variables and parameters (shown below) and then we are good to go:

Note: the first time I used this I had an issue with the Domain Join – it was caused by specifying only the domain admin username. When specified in the form above (domain\\adminusername) it then worked fine.

Now when we run the template, we get the usual Visual Studio output confirming success – but also a scale set, and, machines joined to the domain:

Because I have previously configured the image used in the Scale Set with the RDS Role, and the Software required – we just need the servers to use an RDS Broker that will manage inbound connections into the RDS Farm. This is where I encounter the first sticking point – these need to be added manually when the Session Collection is created 🙁

This wasn’t a massive issue for this test – so I went ahead and created a Session Collection and added in my VMs:

Next I tested the solution by launching a Desktop via Remote Desktop Web Access:

Bingo – I was then logged into an RDS Session. Note the RDS Connection Name (showing the Broker) and the Computer Name (showing the Session host). This confirms we are running as expected:

I’ve now demonstrated the RDS Farm up and running, utilizing machines created by a Scale Set, and also accessed via a connection broker. But – we aren’t quite done yet, as we have not looked how a scale set could enhance this solution. Below are a few ways we can improve the environment using Scale Sets, and a few limitations when used with RDS:

  • We have the option to Manually increase VM instances if we need more Session Hosts:

Note: this will require adding to the RDS Session collection manually (or via PowerShell)

  • We can scale the environment automatically using Auto Scale:

Below you can see a default scale rule (5 VMs in the Scale set) and then a rule that runs between 0600 and 1800 daily, and increases the VM Count up to 10 VMs if average CPU usage goes above 80%.

The rule for this Scale operation is shown below:

Note: this will still require machines adding to the Session Collection manually.

  • We can increase the size of the VMs

Once a new size has been selected – the existing VMs show as not up to date:

We would then need to upgrade the VMs in the scale set (requiring a reboot), but, does not require the VMs to be re-added to the Session Collection. With this option a drain, upgrade, drain, upgrade option would be available. This allows for a sizing upscale – without lots of reconfiguration or management required.

Overall, it would seem that although scale sets aren’t able to fully integrate with Remote Desktop Services collections, they are still very capable and powerful when it comes to managing RDS Workloads. Scale Sets can be used to size and provision machines, as well as to provide simple options to increase environment capacity and power. Purely using a scale set for the ability to spin up new VMs, or to manage sizing across multiple VMs is a logical step. We also have the option to reimage a VM – taking it back to a clean configuration.

Key Observations from my investigation:

  • We can scale an RDS environment very quickly, but RDS Servers can’t be automatically added to a session collection – the GPO settings for this don’t appear to support RDS post 2008R2 (whereby Session Collections and the new configuration method was introduced). This means servers have to be manually added when the Scale Set is scaled up
  • Scale sets can be used to increase VM size quickly – without reimaging servers (a reboot is all that is required)
  • Scaling can only look at performance metrics – we can’t scale on user count for example
  • Reimaging means we can take servers back to a clean build quickly – if a server has an issue we would just prevent logons and then reimage.
  • Scaling down can’t take logged on users into consideration – so we’d need a way of draining servers down first
  • Scale Sets will also allow us to scale up to very large environments with minimal effort – just increase VM count or size, and add the servers into the RDS Collection. A growing business for example – or one that provides a hosted desktop could scale from 10 servers to a few hundred with minimal effort.

Hope this helps, and congratulations if you have made it to the end of this article! Until next time!