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!