RDS 2016 Farm: Deploy the Microsoft Azure VM

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:

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.

About Romain Serre

Romain Serre works in Lyon as a Senior Consultant. He is focused on Microsoft Technology, especially on Hyper-V, System Center, Storage, networking and Cloud OS technology as Microsoft Azure or Azure Stack. He is a MVP and he is certified Microsoft Certified Solution Expert (MCSE Server Infrastructure & Private Cloud), on Hyper-V and on Microsoft Azure (Implementing a Microsoft Azure Solution).

7 comments

  1. Oliver Radcliffe

    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

  2. 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?

  3. Question. Why would you not use a VMSS for this? I feel that VMSS would be better for scaling in this setup.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

x

Check Also

Getting started with Azure Update Management to handle Windows updates

For most of the companies, the patch management is a challenge. All customers don’t have ...

Create a Hub-and-Spoke topology with Azure Virtual Network Peering

Currently I’m working on AZ-102 certification and I wanted to share with you a small ...

Getting started with Azure File Sync

Azure File Sync is a Microsoft feature released in July 2018. It enables to synchronize ...