Azure Resource Manager and Multiple NAT Rules

Back in the days of cloud services every VM created got a set of default endpoints that let in traffic for RDP and Remoting on a random port, and if you wanted ingress on other ports you just created more endpoints. In the V2 world cloud services don’t exist, and endpoints are now primary configured as inbound NAT rules on a load balancer, with the default being no NAT rules. This is ultimately a much better and more secure way of doing it, however it did make it a pain when you needed to add one or more NAT rule for each VM in your deployment, especially when you had lots of VM’s. Because the NAT rules were part of the Load Balancer object and couldn’t be looped you either needed to know up front how many rules you needed and hard code them into your ARM script, or add them manually after the fact.

In a recent release of the SDK this changed and the NAT Rule were broken out into a separate object, and one that supports the copy command. This now means when you create VM’s in a loop you can also easily assign NAT rules to each VM. In the example below we are create a load balancer and then creating a NAT rule for each VM to allow RDP traffic in on a high port, starting at 5000 for the first VM. By doing this we ensure that every VM has a NAT rule configured without having to change the script each time we increase or decrease the VM count.

For the full deployment script this example comes from see here.

 

{
    "apiVersion": "2015-06-15",
    "name": "[parameters('lbName')]",
    "type": "Microsoft.Network/loadBalancers",
    "location": "[resourceGroup().location]",
    "dependsOn": [
        "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]"
    ],
    "properties": {
        "frontendIPConfigurations": [
            {
                "name": "LoadBalancerFrontend",
                "properties": {
                    "publicIPAddress": {
                        "id": "[variables('publicIPAddressID')]"
                    }
                }
            }
        ],
        "backendAddressPools": [
            {
                "name": "LoadBalancerBackend"
            }
        ]
    }
},
{
    "apiVersion": "2015-06-15",
    "type": "Microsoft.Network/loadBalancers/inboundNatRules",
    "name": "[concat(parameters('lbName'), '/', 'RDP-VM', copyIndex())]",
    "location": "[resourceGroup().location]",
    "copy": {
        "name": "lbNatLoop",
        "count": "[variables('numberOfInstances')]"
    },
    "dependsOn": [
        "[concat('Microsoft.Network/loadBalancers/', parameters('lbName'))]"
    ],
    "properties": {
        "frontendIPConfiguration": {
            "id": "[variables('frontEndIPConfigID')]"
        },
        "protocol": "tcp",
        "frontendPort": "[copyIndex(5000)]",
        "backendPort": 3389,
        "enableFloatingIP": false
    }
},