This topic is part of a series about how to deploy a Windows Server 2016 RDS farm in Microsoft Azure. Previously, we have created the network resources, the storage account for diagnostics and the Windows image. In this topic, we will create all the Azure VM required for the solution. The deployment will be processed from a JSON template. This series talks about the following subjects:
- Deploy a Windows Server 2016 RDS Farm in Microsoft Azure
- Create Microsoft Azure networks, storage and Windows image
- Deploy the Microsoft Azure Virtual Machines
- Configure Domain Controllers
- Deploy the RDS farm
- Configure File Servers for User Profile Disk (UPD)
- RDS final configuration
Github
The template for this series are located in my Github. I have created a folder called RDSFarm that containers JSON template. For this topic, I have used RDS-VMs.json.
JSON template explanation
In this template, I create an availability set for each kind of service. So, I have 5 availability sets (Domain Controllers, File Servers, RD Host, RD Broker and RD Gateway). So, I have the following block code for each availability set:
{ "type": "Microsoft.Compute/availabilitySets", "sku": { "name": "Aligned" }, "name": "[parameters('ASDomainControllersName')]", "apiVersion": "[variables('computeResouresApiVersion')]", "location": "[variables('ResourcesLocation')]", "tags": { "displayName": "AS_DomainControllers" }, "properties": { "platformUpdateDomainCount": 5, "platformFaultDomainCount": 2 } }
Then I create the virtual network adapters. Each VM has one network adapters excepted the File Servers which have two (cluster and management). Each vNIC is connected to the right subnet. You can see also that I have created a loop (copy section): because each kind of service has at least two VMs, the loop avoids me to duplicate several times same block code.
{ "type": "Microsoft.Network/networkInterfaces", "name": "[concat(parameters('PrefixNameDC'), copyindex())]", "apiVersion": "[variables('NetworkResouresApiVersion')]", "location": "[variables('ResourcesLocation')]", "tags": { "displayName": "vNIC_DomainControllers" }, "copy": { "name": "DCnicLoop", "count": "[parameters('numberOfDC')]" }, "properties": { "ipConfigurations": [ { "name": "ipconfig1", "properties": { "privateIPAllocationMethod": "Dynamic", "subnet": { "id": "[Variables('vNetSubIntRef')]" } } } ], "dnsSettings": { "dnsServers": [] }, "enableIPForwarding": false } }
Next I create data disks. File Servers have four data disks each (for Storage Spaces Direct). Each Domain Controller has one data disk to host the AD database and RD Hosts have a data disk for application. All these disks are managed disks. I have also made a loop for each kind of data disk:
{ "type": "Microsoft.Compute/disks", "name": "[concat(parameters('PrefixNameDC'), copyindex(),'-Data01')]", "apiVersion": "[variables('computeResouresApiVersion')]", "location": "[variables('ResourcesLocation')]", "tags": { "displayName": "Disks_DomainControllers" }, "copy": { "name": "DCDskLoop", "count": "[parameters('numberOfDC')]" }, "properties": { "creationData": { "createOption": "Empty" }, "accountType": "Standard_LRS", "diskSizeGB": 10 } }
I have also created a public IP for the RD Access load balancer:
{ "type": "Microsoft.Network/publicIPAddresses", "name": "[parameters('PublicIPName')]", "apiVersion": "[variables('NetworkResouresApiVersion')]", "location": "[variables('ResourcesLocation')]", "tags": { "displayName": "Public IP Address" }, "properties": { "publicIPAllocationMethod": "Static", "idleTimeoutInMinutes": 4 }, "dependsOn": [] }
To finish, the following JSON block code creates VMs. I have a block code for each kind of VM. Then I use a loop to deploy several times the same VM with a different name. I use the Windows image to deploy the VM. Credentials are provided from parameters. Boot diagnostics are enabled and logs are stored in the storage account. Each vNIC is also bound to the right VM. VMs are added to availability set and connected to the right data disks.
{ "name": "[concat(parameters('PrefixNameDC'), copyindex())]", "type": "Microsoft.Compute/virtualMachines", "apiVersion": "[variables('computeResouresApiVersion')]", "location": "[variables('ResourcesLocation')]", "tags": { "displayName": "VM_DomainControllers" }, "copy": { "name": "DCVMLoop", "count": "[parameters('NumberOfDC')]" }, "dependsOn": [ "[resourceId('Microsoft.Compute/availabilitySets', parameters('ASDomainControllersName'))]", "[resourceId('Microsoft.Network/networkInterfaces', concat(parameters('PrefixNameDC'), copyindex()))]" ], "properties": { "osProfile": { "computerName": "[concat(parameters('PrefixNameDC'), copyindex())]", "adminUsername": "[parameters('adminUser')]", "adminPassword": "[parameters('adminPassword')]", "windowsConfiguration": { "provisionVmAgent": "true" } }, "hardwareProfile": { "vmSize": "Standard_DS1_v2" }, "storageProfile": { "imageReference": { "id": "[parameters('OSDiskMasterPath')]" }, "osDisk": { "name": "[concat(parameters('PrefixNameDC'), copyindex(),'-OS')]", "createOption": "FromImage", "managedDisk": { "storageAccountType": "Standard_LRS" } }, "dataDisks": [ { "lun": 2, "name": "[concat(parameters('PrefixNameDC'), copyindex(),'-Data01')]", "createOption": "Attach", "managedDisk": { "id": "[resourceId('Microsoft.Compute/disks', concat(parameters('PrefixNameDC'), copyindex(),'-Data01'))]" } } ] }, "networkProfile": { "networkInterfaces": [ { "id": "[resourceId('Microsoft.Network/networkInterfaces', concat(parameters('PrefixNameDC'), copyindex()))]" } ] }, "diagnosticsProfile": { "bootDiagnostics": { "enabled": true, "storageUri": "[reference(resourceId('rdsfarm', 'Microsoft.Storage/storageAccounts', parameters('Sto_LogsAccount')), '2015-06-15').primaryEndpoints['blob']]" } }, "availabilitySet": { "id": "[resourceId('Microsoft.Compute/availabilitySets', parameters('ASDomainControllersName'))]" } } }
Template deployment
To run the deployment with the JSON template, go to the marketplace and search for Template Deployment.
Then, copy past the template. You should have something like this:
Next change parameters as you wish and click on purchase.
After the deployment, I have stopped all VMs to not spend money immediately for VMs not used.
Result
Once the deployment is finished, you should have several Azure VM depending on the loop settings. On my side, I have 10 Azure VMs.
If I select a VM such as a file server, you can see that managed disks are well bound to Azure VM.
Network interfaces are also connected to the server and well associated with the right subnet.
Azure VM are also inside Availability Sets.
To finish, boot diagnostics are enabled and stored in the storage account.
Next topic
In the next topic, I will configure the domain controller. I’ll set the AD site and I’ll promote the Azure domain controllers.
Very nice! Can I ask, have you looked at adding a “deploy to Azure” button to the project? Seems like it would allow you to skip the copy/paste part.
https://azure.microsoft.com/en-us/blog/deploy-to-azure-button-for-azure-websites-2/
Hi,
I will do it in the final template json. I’d like to merge both templates and add the VPN connection, the DNS configuration, the Azure SQL Server and all thing I do manually. Then I’d like to run PowerShell script when VM are powered up to automate all thing I can.
Thank you very much.
Romain
Hi Romain,
Is there any chance you could provide this in a situation where there is already a domain controller (or two in HA) so we only deal with the RDS configuration?
Hi,
In this case, download the template and remove all section related to Domain Controllers.
Thank you Romain – appreciate the reply. Any chance you could be more specific than that?
I wouldn’t know what to remove and from where. Are there any dependencies?
Cheers,
Question. Why would you not use a VMSS for this? I feel that VMSS would be better for scaling in this setup.
Sure but when I have written the article, VM Scale Set was not available :p