Quickly deploy RDS 2016 in Azure


In this post I’ll show how to deploy a minimal Windows Server 2016 Remote Desktop Services farm in Azure in 20 minutes using Azure Resource Manager template. Such farm will be enough to provide Desktop-as-a-Service (DaaS) for 5 to 100 end-users. It was designed as a single-tenant solution, then can be quickly deployed in Azure with minimal effort for every customer. My ARM template includes:

  1. One VM with Active Directory Domain Controller and DNS roles
  2. One VM with RD Connection Broker and RD Licensing Server (per-user licensing through SPLA)
  3. One VM with RD Gateway and RD Web
  4. Specified number of RD Session Hosts.

My ARM template is based on RDS 2016 Base template from GitHub. I recommend you to watch this video from Ignite to understand the details. The resulting farm isn’t protected from DC, CB or GW failures, but it is much cheaper then Full-HA deployment with a pair of Connection Brokers with Azure SQL Database, a pair of Gateways with a Load Balancer and a highly available AD environment. If you need an RDS farm for 100+ end-users, I recommend you to modify my template and add high availability for the core components, add Azure Backup capabilities and Azure SMT for centralized management. But I assume that such large RDS farms are rare among CSP partners, and those capabilities can be added manually instead of creating a super-complex ARM template.

Login to the Azure Portal and click “More Services” in the left pane. Choose “Template” menu in the list and then click “Add” to create a new Azure Resource Manager Template.

capture_09012017_180344

Specify the template name and description:

capture_09012017_181419Then copy the following text and paste it to the template:

capture_09012017_181657

{
   "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
   "contentVersion": "1.0.0.0",
   "parameters": {
     "gwdnsLabelPrefix": {
       "type": "string",
       "metadata": {
         "description": "Unique gateway public DNS prefix. The fqdn will look something like 'prefix.region.cloudapp.azure.com'. Up to 62 chars, digits or dashes, lowercase, should start with a letter."
       }
     },
     "adDomainName": {
       "type": "string",
       "metadata": {
         "description": "The name of the AD domain."
       },
       "defaultValue": "contoso.com"
     },
     "adminUsername": {
       "type": "string",
       "metadata": {
         "description": "The name of the domain admin. Can't be 'administrator' or 'user'."
       },
       "defaultValue": "rdsadmin"
     },
     "adminPassword": {
       "type": "securestring",
       "metadata": {
         "description": "The password for the administrator account of the new VM and the domain"
       }
     },
     "numberOfRdshInstances": {
       "type": "int",
       "defaultValue": 1,
       "metadata": {
         "description": "Initial number of RD Session Hosts"
       }
     },
     "rdshVmSize": {
       "type": "string",
       "allowedValues": [
         "Standard_A2m_V2",
         "Standard_A4m_V2",
         "Standard_A8m_V2",
         "Standard_D11_V2",
         "Standard_D12_V2",
         "Standard_D13_V2",
         "Standard_D14_V2",
         "Standard_D15_V2"
       ],
       "metadata": {
         "description": "The size of the RD Session Host VMs"
       },
       "defaultValue": "Standard_D11_V2"
     }
   },
   "variables": {
     "adAssetLocation": "https://raw.githubusercontent.com/Azure/AzureStack-QuickStart-Templates/master/ad-non-ha",
     "gwpublicIPAddressName": "gwpip",
     "imageSKU": "2016-Datacenter",
     "adVMSize": "Basic_A1",
     "adVnetName": "[concat('ADVNET',resourceGroup().name)]",
     "adSubnetName": "[concat('ADStaticSubnet',resourceGroup().name)]",
     "vnetID": "[resourceId('Microsoft.Network/virtualNetworks', variables('adVnetName'))]",
     "staticSubnetID": "[concat(variables('vnetID'),'/subnets/', variables('adSubnetName'))]",
     "adTemplateURL": "[concat(variables('adAssetLocation'),'/adVmTemplate.json')]",
     "adStorageName": "[tolower(concat('adsa',uniqueString(resourceGroup().id)))]",
     "adVmDeployment": "CreateAdVms",
     "adVmDeploymentId": "[concat('Microsoft.Resources/deployments/', variables('adVmDeployment'))]",
     "deployPrimaryAdTemplateURL": "[concat(variables('adAssetLocation'),'/deployPrimaryAD.json')]",
     "deployPrimaryAd": "DeployPrimaryAd",
     "deployPrimaryAdID": "[concat('Microsoft.Resources/deployments/', variables('deployPrimaryAd'))]",
     "adPDCVMName": "DC1",
     "vnetwithDNSTemplateURL": "[concat(variables('adAssetLocation'),'/vnet-with-dns-server.json')]",
     "updateVNetDNS1": "updateVNetDNS",
     "updateVNetDNS1ID": "[concat('Microsoft.Resources/deployments/', variables('updateVNetDNS1'))]",
     "nicTemplateURL": "[concat(variables('adAssetLocation'),'/nic.json')]",
     "publicLBName": "[concat('ADPLB',resourceGroup().name)]",
     "publicIPAddressID": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]",
     "lbFE": "ADLBFE",
     "rdpNAT": "ADRDPNAT",
     "publiclbID": "[resourceId('Microsoft.Network/loadBalancers',variables('publicLBName'))]",
     "publiclbFEConfigID": "[concat(variables('publiclbID'),'/frontendIPConfigurations/',variables('lbFE'))]",
     "rdpPort": 3389,
     "adRDPNATRuleID": "[concat(variables('publiclbID'),'/inboundNatRules/',variables('rdpNAT'))]",
     "adNICName": "[concat('ADNic',resourceGroup().name)]",
     "lbBE": "ADLBBE",
     "publicBEAddressPoolID": "[concat(variables('publiclbID'),'/backendAddressPools/',variables('lbBE'))]",
     "gwLBName": "[concat('GWPLB',resourceGroup().name)]",
     "publicIPAddressName": "[tolower(concat('adpip',uniqueString(resourceGroup().Id)))]",
     "gwIPAddressID": "[resourceId('Microsoft.Network/publicIPAddresses',variables('gwpublicIPAddressName'))]",
     "gwlbFE": "GWLBFE",
     "gwlbID": "[resourceId('Microsoft.Network/loadBalancers',variables('gwLBName'))]",
     "gwlbFEConfigID": "[concat(variables('gwlbID'),'/frontendIPConfigurations/',variables('gwlbFE'))]",
     "gwlbBE": "GWLBBE",
     "gwBEAddressPoolID": "[concat(variables('gwlbID'),'/backendAddressPools/',variables('gwlbBE'))]",
     "dnsLabelPrefix": "[tolower(concat('adns', resourceGroup().name))]",
     "storageAccountName": "[tolower(concat('rdsa',uniqueString(resourceGroup().id)))]",
     "storageAccountType": "Standard_LRS",
     "uniqueStorageAccountContainerName": "[tolower(concat('sc', uniqueString(resourceGroup().id)))]",
     "imagePublisher": "MicrosoftWindowsServer",
     "imageOffer": "WindowsServer",
     "vnetAddressRange": "10.0.0.0/16",
     "subnetAddressRange": "10.0.0.0/24",
     "dnsServerPrivateIp": "10.0.0.4",
     "subnet-id": "[concat(resourceId('Microsoft.Network/virtualNetworks', variables('adVnetName')),'/subnets/', variables('adSubnetName'))]",
     "assetLocation": "https://raw.githubusercontent.com/Azure/azure-QuickStart-Templates/master/rds-deployment/",
     "nsgName": "RDSNsg",
     "nsgID": "[resourceId('Microsoft.Network/networkSecurityGroups',variables('nsgName'))]",
     "subnets": [
       {
         "name": "[variables('adSubnetName')]",
         "properties": {
           "addressPrefix": "[variables('subnetAddressRange')]",
           "networkSecurityGroup": {
             "id": "[variables('nsgID')]"
           }
         }
       }
     ]

  },
   "resources": [
     {
       "name": "[variables('nsgName')]",
       "type": "Microsoft.Network/networkSecurityGroups",
       "location": "[resourceGroup().location]",
       "apiVersion": "2015-06-15",
       "properties": {
         "securityRules": [
           {
             "name": "rule1",
             "properties": {
               "protocol": "*",
               "sourcePortRange": "*",
               "destinationPortRange": "*",
               "sourceAddressPrefix": "*",
               "destinationAddressPrefix": "*",
               "access": "Allow",
               "priority": 101,
               "direction": "Inbound"
             }
           }
         ]
       }
     },
     {
       "name": "[variables('adVnetName')]",
       "type": "Microsoft.Network/virtualNetworks",
       "location": "[resourceGroup().location]",
       "apiVersion": "2015-06-15",
       "dependsOn": [
         "[variables('nsgID')]"
       ],
       "properties": {
         "addressSpace": {
           "addressPrefixes": [
             "[variables('vnetAddressRange')]"
           ]
         },
         "subnets": [
           {
             "name": "[variables('adSubnetName')]",
             "properties": {
               "addressPrefix": "[variables('subnetAddressRange')]",
               "networkSecurityGroup": {
                 "id": "[variables('nsgID')]"
               }
             }
           }
         ]
       }
     },
     {
       "name": "[variables('publicIPAddressName')]",
       "type": "Microsoft.Network/publicIPAddresses",
       "location": "[resourceGroup().location]",
       "apiVersion": "2015-06-15",
       "dependsOn": [
         "[variables('vnetID')]"
       ],
       "properties": {
         "publicIPAllocationMethod": "Dynamic",
         "dnsSettings": {
           "domainNameLabel": "[variables('dnsLabelPrefix')]"
         }
       }
     },
     {
       "name": "[variables('gwpublicIPAddressName')]",
       "type": "Microsoft.Network/publicIPAddresses",
       "location": "[resourceGroup().location]",
       "apiVersion": "2015-06-15",
       "dependsOn": [
         "[variables('deployPrimaryAdID')]"
       ],
       "properties": {
         "publicIPAllocationMethod": "Static",
         "dnsSettings": {
           "domainNameLabel": "[parameters('gwdnsLabelPrefix')]"
         }
       }
     },
     {
       "apiVersion": "2015-06-15",
       "type": "Microsoft.Compute/availabilitySets",
       "name": "gw-availabilityset",
       "location": "[resourceGroup().location]"
     },
     {
       "apiVersion": "2015-06-15",
       "type": "Microsoft.Compute/availabilitySets",
       "name": "cb-availabilityset",
       "location": "[resourceGroup().location]"
     },
     {
       "apiVersion": "2015-06-15",
       "type": "Microsoft.Compute/availabilitySets",
       "name": "rdsh-availabilityset",
       "location": "[resourceGroup().location]"
     },
     {
       "name": "[variables('publiclbName')]",
       "type": "Microsoft.Network/loadBalancers",
       "apiVersion": "2015-06-15",
       "location": "[resourceGroup().location]",
       "dependsOn": [
         "[variables('publicIPAddressID')]"
       ],
       "properties": {
         "frontendIPConfigurations": [
           {
             "name": "[variables('lbFE')]",
             "properties": {
               "publicIPAddress": {
                 "id": "[variables('publicIPAddressID')]"
               }
             }
           }
         ],
         "backendAddressPools": [
           {
             "name": "[variables('lbBE')]"
           }
         ],
         "inboundNatRules": [
           {
             "name": "[variables('rdpNAT')]",
             "properties": {
               "frontendIPConfiguration": {
                 "id": "[variables('publiclbFEConfigID')]"
               },
               "protocol": "tcp",
               "frontendPort": "[variables('rdpPort')]",
               "backendPort": 3389,
               "enableFloatingIP": false
             }
           }
         ]
       }
     },
     {
       "name": "[variables('gwlbName')]",
       "type": "Microsoft.Network/loadBalancers",
       "apiVersion": "2015-06-15",
       "location": "[resourceGroup().location]",
       "dependsOn": [
         "[variables('gwIPAddressID')]"
       ],
       "properties": {
         "frontendIPConfigurations": [
           {
             "name": "[variables('gwlbFE')]",
             "properties": {
               "publicIPAddress": {
                 "id": "[variables('gwIPAddressID')]"
               }
             }
           }
         ],
         "backendAddressPools": [
           {
             "name": "[variables('gwlbBE')]"
           }
         ],
         "loadBalancingRules": [
           {
             "name": "LBRule01",
             "properties": {
               "frontendIPConfiguration": {
                 "id": "[variables('gwlbFEConfigID')]"
               },
               "backendAddressPool": {
                 "id": "[variables('gwBEAddressPoolID')]"
               },
               "protocol": "Tcp",
               "frontendPort": 443,
               "backendPort": 443,
               "enableFloatingIP": false,
               "idleTimeoutInMinutes": 5,
               "loadDistribution": "SourceIPProtocol",
               "probe": {
                 "id": "[concat(variables('gwlbID'),'/probes/tcpProbe')]"
               }
             }
           },
           {
             "name": "LBRule02",
             "properties": {
               "frontendIPConfiguration": {
                 "id": "[variables('gwlbFEConfigID')]"
               },
               "backendAddressPool": {
                 "id": "[variables('gwBEAddressPoolID')]"
               },
               "protocol": "Udp",
               "frontendPort": 3391,
               "backendPort": 3391,
               "enableFloatingIP": false,
               "idleTimeoutInMinutes": 5,
               "loadDistribution": "SourceIPProtocol",
               "probe": {
                 "id": "[concat(variables('gwlbID'),'/probes/tcpProbe')]"
               }
             }
           }
         ],
         "probes": [
           {
             "name": "tcpProbe",
             "properties": {
               "protocol": "Tcp",
               "port": 443,
               "intervalInSeconds": 5,
               "numberOfProbes": 2
             }
           },
           {
             "name": "tcpProbe01",
             "properties": {
               "protocol": "Tcp",
               "port": 3391,
               "intervalInSeconds": 5,
               "numberOfProbes": 2
             }
           }
         ],
         "inboundNatRules": [
           {
             "name": "rdp",
             "properties": {
               "frontendIPConfiguration": {
                 "id": "[variables('gwlbFEConfigID')]"
               },
               "protocol": "tcp",
               "frontendPort": 3389,
               "backendPort": 3389,
               "enableFloatingIP": false
             }
           }

        ]
       }
     },
     {
       "name": "[variables('adVmDeployment')]",
       "type": "Microsoft.Resources/deployments",
       "apiVersion": "2016-02-01",
       "dependsOn": [
         "[variables('publiclbID')]"
       ],
       "properties": {
         "mode": "Incremental",
         "templateLink": {
           "uri": "[variables('adTemplateURL')]",
           "contentVersion": "1.0.0.0"
         },
         "parameters": {
           "adminUsername": {
             "value": "[parameters('adminUsername')]"
           },
           "adminPassword": {
             "value": "[parameters('adminPassword')]"
           },
           "adRDPNATRuleID": {
             "value": "[variables('adRDPNATRuleID')]"
           },
           "storageAccount": {
             "value": "[variables('adStorageName')]"
           },
           "subnetResourceId": {
             "value": "[variables('staticSubnetID')]"
           },
           "primaryAdIpAddress": {
             "value": "[variables('dnsServerPrivateIp')]"
           },
           "storageAccountType": {
             "value": "[variables('storageAccountType')]"
           },
           "vmName": {
             "value": "[variables('adPDCVMName')]"
           },
           "vmSize": {
             "value": "[variables('adVMSize')]"
           },
           "adDNicName": {
             "value": "[variables('adNICName')]"
           }
         }
       }
     },
     {
       "name": "[variables('deployPrimaryAd')]",
       "type": "Microsoft.Resources/deployments",
       "apiVersion": "2016-02-01",
       "dependsOn": [
         "[variables('adVmDeploymentID')]"
       ],
       "properties": {
         "mode": "Incremental",
         "templateLink": {
           "uri": "[variables('deployPrimaryAdTemplateURL')]",
           "contentVersion": "1.0.0.0"
         },
         "parameters": {
           "primaryADName": {
             "value": "[variables('adPDCVMName')]"
           },
           "domainName": {
             "value": "[parameters('adDomainName')]"
           },
           "adminUsername": {
             "value": "[parameters('adminUsername')]"
           },
           "adminPassword": {
             "value": "[parameters('adminPassword')]"
           },
           "assetLocation": {
             "value": "[variables('adAssetLocation')]"
           }
         }
       }
     },
     {
       "name": "[variables('updateVNetDNS1')]",
       "type": "Microsoft.Resources/deployments",
       "apiVersion": "2016-02-01",
       "dependsOn": [
         "[variables('deployPrimaryAdID')]"
       ],
       "properties": {
         "mode": "Incremental",
         "templateLink": {
           "uri": "[variables('vnetwithDNSTemplateURL')]",
           "contentVersion": "1.0.0.0"
         },
         "parameters": {
           "virtualNetworkName": {
             "value": "[variables('adVnetName')]"
           },
           "virtualNetworkAddressRange": {
             "value": "[variables('vnetAddressRange')]"
           },
           "subnets": {
             "value": "[variables('subnets')]"
           },
           "dnsServerAddress": {
             "value": [
               "[variables('dnsServerPrivateIp')]"
             ]
           }
         }
       }
     },
     {
       "apiVersion": "2015-06-15",
       "type": "Microsoft.Storage/storageAccounts",
       "name": "[variables('storageAccountName')]",
       "location": "[resourceGroup().location]",
       "properties": {
         "accountType": "[variables('storageAccountType')]"
       }
     },
     {
       "apiVersion": "2015-06-15",
       "type": "Microsoft.Network/networkInterfaces",
       "name": "gw-nic",
       "location": "[resourceGroup().location]",
       "dependsOn": [
         "[variables('gwlbID')]",
         "[variables('adVmDeploymentID')]"
       ],
       "properties": {
         "ipConfigurations": [
           {
             "name": "ipconfig",
             "properties": {
               "privateIPAllocationMethod": "Dynamic",
               "subnet": {
                 "id": "[variables('subnet-id')]"
               },
               "loadBalancerBackendAddressPools": [
                 {
                   "id": "[variables('gwBEAddressPoolID')]"
                 }
               ],
               "loadBalancerInboundNatRules": [
                 {
                   "id": "[concat(variables('gwlbID'),'/inboundNatRules/rdp')]"
                 }
               ]
             }
           }
         ],
         "dnsSettings": {
           "dnsServers": [
             "[variables('dnsServerPrivateIp')]"
           ]
         }
       }
     },
     {
       "apiVersion": "2015-06-15",
       "type": "Microsoft.Network/networkInterfaces",
       "name": "cb-nic",
       "location": "[resourceGroup().location]",
       "dependsOn": [
         "[variables('publiclbID')]",
         "[variables('adVmDeploymentID')]"
       ],
       "properties": {
         "ipConfigurations": [
           {
             "name": "ipconfig",
             "properties": {
               "privateIPAllocationMethod": "Dynamic",
               "subnet": {
                 "id": "[variables('subnet-id')]"
               }
             }
           }
         ],
         "dnsSettings": {
           "dnsServers": [
             "[variables('dnsServerPrivateIp')]"
           ]
         }
       }
     },
     {
       "apiVersion": "2015-06-15",
       "type": "Microsoft.Network/networkInterfaces",
       "name": "[concat('rdsh-', copyindex(), '-nic')]",
       "location": "[resourceGroup().location]",
       "copy": {
         "name": "rdsh-nic-loop",
         "count": "[parameters('numberOfRdshInstances')]"
       },
       "dependsOn": [
         "[variables('publiclbID')]",
         "[variables('adVmDeploymentID')]"
       ],
       "properties": {
         "ipConfigurations": [
           {
             "name": "ipconfig",
             "properties": {
               "privateIPAllocationMethod": "Dynamic",
               "subnet": {
                 "id": "[variables('subnet-id')]"
               }
             }
           }
         ],
         "dnsSettings": {
           "dnsServers": [
             "[variables('dnsServerPrivateIp')]"
           ]
         }
       }
     },
     {
       "apiVersion": "2015-06-15",
       "type": "Microsoft.Compute/virtualMachines",
       "name": "GW1",
       "location": "[resourceGroup().location]",
       "dependsOn": [
         "[variables('deployPrimaryAdID')]",
         "[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]",
         "Microsoft.Network/networkInterfaces/gw-nic"
       ],
       "properties": {
         "hardwareProfile": {
           "vmSize": "Standard_A2"
         },
         "availabilitySet": {
           "id": "[resourceId('Microsoft.Compute/availabilitySets', 'gw-availabilityset')]"
         },
         "osProfile": {
           "computerName": "GW1",
           "adminUsername": "[parameters('adminUsername')]",
           "adminPassword": "[parameters('adminPassword')]"
         },
         "storageProfile": {
           "imageReference": {
             "publisher": "[variables('imagePublisher')]",
             "offer": "[variables('imageOffer')]",
             "sku": "[variables('imageSKU')]",
             "version": "latest"
           },
           "osDisk": {
             "name": "osdisk",
             "vhd": {
               "uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName')),providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).primaryEndpoints.blob,variables('uniqueStorageAccountContainerName'),'/','gw-vm-os-disk.vhd')]"
             },
             "caching": "ReadWrite",
             "createOption": "FromImage"
           }
         },
         "networkProfile": {
           "networkInterfaces": [
             {
               "id": "[resourceId('Microsoft.Network/networkInterfaces','gw-nic')]"
             }
           ]
         }
       },
       "resources": [
         {
           "apiVersion": "2015-06-15",
           "type": "Microsoft.Compute/virtualMachines/extensions",
           "name": "GW1/gateway",
           "location": "[resourceGroup().location]",
           "dependsOn": [
             "[resourceId('Microsoft.Compute/virtualMachines', 'GW1')]"
           ],
           "properties": {
             "publisher": "Microsoft.Powershell",
             "type": "DSC",
             "typeHandlerVersion": "2.11",
             "autoUpgradeMinorVersion": true,
             "settings": {
               "modulesUrl": "[concat(variables('assetLocation'),'/Configuration.zip')]",
               "configurationFunction": "Configuration.ps1\\Gateway",
               "Properties": {
                 "DomainName": "[parameters('adDomainName')]",
                 "AdminCreds": {
                   "UserName": "[parameters('adminUsername')]",
                   "Password": "PrivateSettingsRef:AdminPassword"
                 }
               }
             },
             "protectedSettings": {
               "Items": {
                 "AdminPassword": "[parameters('adminPassword')]"
               }
             }
           }
         }
       ]
     },
     {
       "apiVersion": "2015-06-15",
       "type": "Microsoft.Compute/virtualMachines",
       "name": "[concat('RDSH', copyindex())]",
       "location": "[resourceGroup().location]",
       "copy": {
         "name": "rdsh-vm-loop",
         "count": "[parameters('numberOfRdshInstances')]"
       },
       "dependsOn": [
         "[variables('deployPrimaryAdID')]",
         "[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]",
         "[concat('Microsoft.Network/networkInterfaces/', 'rdsh-', copyindex(), '-nic')]"
       ],
       "properties": {
         "hardwareProfile": {
           "vmSize": "[parameters('rdshVmSize')]"
         },
         "availabilitySet": {
           "id": "[resourceId('Microsoft.Compute/availabilitySets', 'rdsh-availabilityset')]"
         },
         "osProfile": {
           "computerName": "[concat('RDSH', copyIndex())]",
           "adminUsername": "[parameters('adminUsername')]",
           "adminPassword": "[parameters('adminPassword')]"
         },
         "storageProfile": {
           "imageReference": {
             "publisher": "[variables('imagePublisher')]",
             "offer": "[variables('imageOffer')]",
             "sku": "[variables('imageSKU')]",
             "version": "latest"
           },
           "osDisk": {
             "name": "osdisk",
             "vhd": {
               "uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName')),providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).primaryEndpoints.blob,variables('uniqueStorageAccountContainerName'),'/','rdsh-',copyindex(),'-os-disk.vhd')]"

            },
             "caching": "ReadWrite",
             "createOption": "FromImage"
           }
         },
         "networkProfile": {
           "networkInterfaces": [
             {
               "id": "[resourceId('Microsoft.Network/networkInterfaces',concat('rdsh-', copyindex(), '-nic'))]"
             }
           ]
         }
       },
       "resources": [
         {
           "apiVersion": "2015-06-15",
           "type": "Microsoft.Compute/virtualMachines/extensions",
           "name": "[concat('RDSH', copyindex(),'/sessionhost')]",
           "location": "[resourceGroup().location]",
           "dependsOn": [
             "[resourceId('Microsoft.Compute/virtualMachines', concat('RDSH', copyindex()))]"
           ],
           "properties": {
             "publisher": "Microsoft.Powershell",
             "type": "DSC",
             "typeHandlerVersion": "2.11",
             "autoUpgradeMinorVersion": true,
             "settings": {
               "ModulesUrl": "[concat(variables('assetLocation'),'/Configuration.zip')]",
               "ConfigurationFunction": "Configuration.ps1\\SessionHost",
               "Properties": {
                 "DomainName": "[parameters('adDomainName')]",
                 "AdminCreds": {
                   "UserName": "[parameters('adminUsername')]",
                   "Password": "PrivateSettingsRef:AdminPassword"
                 }
               }
             },
             "protectedSettings": {
               "Items": {
                 "AdminPassword": "[parameters('adminPassword')]"
               }
             }
           }
         }
       ]
     },
     {
       "apiVersion": "2015-06-15",
       "type": "Microsoft.Compute/virtualMachines",
       "name": "CB1",
       "location": "[resourceGroup().location]",
       "dependsOn": [
         "[variables('deployPrimaryAdID')]",
         "[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]",
         "Microsoft.Network/networkInterfaces/cb-nic",
         "rdsh-vm-loop"
       ],
       "properties": {
         "hardwareProfile": {
           "vmSize": "Standard_A2"
         },
         "availabilitySet": {
           "id": "[resourceId('Microsoft.Compute/availabilitySets', 'cb-availabilityset')]"
         },
         "osProfile": {
           "computerName": "CB1",
           "adminUsername": "[parameters('adminUsername')]",
           "adminPassword": "[parameters('adminPassword')]"
         },
         "storageProfile": {
           "imageReference": {
             "publisher": "[variables('imagePublisher')]",
             "offer": "[variables('imageOffer')]",
             "sku": "[variables('imageSKU')]",
             "version": "latest"
           },
           "osDisk": {
             "name": "osdisk",
             "vhd": {
               "uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName')),providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).primaryEndpoints.blob,variables('uniqueStorageAccountContainerName'),'/','cb-vm-os-disk.vhd')]"
             },
             "caching": "ReadWrite",
             "createOption": "FromImage"
           }
         },
         "networkProfile": {
           "networkInterfaces": [
             {
               "id": "[resourceId('Microsoft.Network/networkInterfaces','cb-nic')]"
             }
           ]
         }
       }
     },
     {
       "type": "Microsoft.Compute/virtualMachines/extensions",
       "name": "CB1/rdsdeployment",
       "apiVersion": "2015-06-15",
       "location": "[resourceGroup().location]",
       "dependsOn": [
         "[resourceId('Microsoft.Compute/virtualMachines', 'CB1')]",
         "Microsoft.Compute/virtualMachines/GW1/extensions/gateway",
         "rdsh-vm-loop"
       ],
       "properties": {
         "autoUpgradeMinorVersion": true,
         "protectedSettings": {
           "Items": {
             "adminPassword": "[parameters('adminPassword')]"
           }
         },
         "publisher": "Microsoft.Powershell",
         "settings": {
           "modulesUrl": "[concat(variables('assetLocation'),'/Configuration.zip')]",
           "configurationFunction": "Configuration.ps1\\RDSDeployment",
           "Properties": {
             "adminCreds": {
               "UserName": "[parameters('adminUsername')]",
               "Password": "PrivateSettingsRef:adminPassword"
             },
             "connectionBroker": "[concat('CB1.',parameters('adDomainName'))]",
             "domainName": "[parameters('adDomainName')]",
             "externalfqdn": "[reference(variables('gwpublicIPAddressName')).dnsSettings.fqdn]",
             "numberOfRdshInstances": "[parameters('numberOfRdshInstances')]",
             "sessionHostNamingPrefix": "RDSH",
             "webAccessServer": "[concat('GW1.',parameters('adDomainName'))]"
           }
         },
         "type": "DSC",
         "typeHandlerVersion": "2.11"
       }
     }
   ]
 }

Then save the template, open it and click “Deploy”:

capture_09012017_233030

Create a new Resource Group, specify the Region and provide some parameters:

  1. Unique RD Gateway public DNS prefix
  2. Active Directory Domain Name
  3. Domain Admin username and password
  4. Number of RD Session Hosts and their size. Agree with Terms and Conditions and click “Purchase”.

capture_09012017_233152

In 20-30 minutes you’ll have a ready-to-use RDS 2016 farm, accessible via specified Gwdns Label Prefix + Region URL. In my case it is https://kotlyarenkodaas.northeurope.cloudapp.azure.com/RDWeb.

capture_09012017_235615Manual steps after the deployment:

  1. Configure SSL certificates
  2. Create Session Host collections
  3. Install required applications for end-users
  4. Configure User Profile Disks
  5. Optional: Implement auto-scaling of RD Session Hosts
  6. Optional: Configure backup using Azure Backup.

That’s all for today. Have fun with RDS 2016 and ARM!

Comments (5)

  1. steve says:

    Could the two VMs be consolidated into one, so runs both these workloads.
    1. One VM with Active Directory Domain Controller and DNS roles
    2. One VM with RD Connection Broker and RD Licensing Server (per-user licensing through SPLA)

    1. While Connection Broker installation on DC is supported since WS 2012 SP1, it is not recommended:
      1) Security reasons – hack of CB or RD Licensing will expose the whole domain.
      2) You won’t be able to add high-availability to CB in the future.
      My template deploys DC on Basic_A1 VM, which is relatively cheap and don’t increase the bill significantly.

  2. Shah says:

    How can I edit this to deploy in my existing AD Domain in azure. So to deploy the rest of the VMs, but join my existing domain.

    1. Just remove lines that are responsible for DC creation.

  3. Any clue as to the approximate month-to-month cost of something like this on Azure?

Skip to main content