Deconstructing JSON: Adding a Network Security Group

If you’ve come from the Service Manager days of Azure, you’ll be familiar with the endpoints that came with each Cloud Service, handling the port forwarding needed to RDP to your server or to connect with PowerShell (port 5986). You could add or remove endpoints as need to provide access to whatever service you desired (after making the corresponding firewall change on the server itself.)

This is like parking your car in a garage.  You can choose to lock or unlock the doors (aka the OS firewall) and you can choose to provide access to the garage (aka Azure endpoints).

With ARM, when you deploy a server, you’ll be prompted to confirm the naming of your network security group, this network security group takes the place of the endpoints: providing some default rules, including the RDP access to port 3389.

So how does this differ from an endpoint?  One difference is that there is no port forwarding.  With a cloud service endpoint, the external port wasn’t 3389, because you could have multiple servers behind the single IP address assigned to the cloud service sharing the load balancer.   With ARM, the VM is assigned its own IP address without a load balancer, so the standard port number can be used. If you need to open an additional port, you simply add a new rule to the NSG and open the corresponding rule on the server firewall.

The second difference is that without a NSG (or the cloud service load balancer endpoints) to act as a gateway, your VM is basically just “hanging out there” with only its internal firewall to keep it safe.  So you really need to be sure you’ve checked that firewall configuration and you know what’s open to the public network.  By default, a Windows VM created with an Azure image has RDP and PowerShell/WinRM ports open on the operating system.

If you’ve been following along with my JSON template project, you’ll know that VMs can be created in ARM without the NSG.  And this fine if you are good with having your server parked out on the Internet with a few doors unlocked, so to speak.

But if you want to take advantage of the power of Network Security Groups to limit the sources of connections or to customize the way traffic flows between tiers of servers on your network, you’ll need to include the NSG resource in your template.

First you’ll  need to add a parameter to declare the Network Security Group.

   "networkSecurityGroupName": {
            "defaultValue": "NanoVMnsg",
            "type": "string"
    },

Then you’ll to configure the resource itself to include the security rules.

 {
      "type": "Microsoft.Network/networkSecurityGroups",
      "name": "[parameters('networkSecurityGroupName')]",
      "apiVersion": "2015-06-15",
      "location": "[parameters('location')]",
      "properties": {
            "securityRules": [
                {
                    "name": "winrm",
                    "properties": {
                        "protocol": "Tcp",
                        "sourcePortRange": "*",
                        "destinationPortRange": "5985-5986",
                        "sourceAddressPrefix": "*",
                        "destinationAddressPrefix": "*",
                        "access": "Allow",
                        "priority": 110,
                        "direction": "Inbound"
                    }
                  },
                  {
                    "name": "rdp",
                    "properties": {
                        "protocol": "Tcp",
                        "sourcePortRange": "*",
                        "destinationPortRange": "3389",
                        "sourceAddressPrefix": "*",
                        "destinationAddressPrefix": "*",
                        "access": "Allow",
                        "priority": 120,
                        "direction": "Inbound"
                    }
                 }
                ]
            },
            "dependsOn": []
        },

Finally, you’ll need to adjust your network interface resource to depend on the NSG.

 {
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/networkInterfaces",
      "name": "[variables('nicName')]",
      "location": "[parameters('location')]",
      "dependsOn": [
        "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
        "[concat('Microsoft.Network/virtualNetworks/', parameters('vnetName'))]",
        "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
      ],
      "properties": {
        "ipConfigurations": [
          {
            "name": "ipconfig1",
            "properties": {
             "privateIPAllocationMethod": "Dynamic",
             "publicIPAddress": {
                "id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]"
              },
              "subnet": {
                "id": "[variables('subnetRef')]"
              }
              }
          }
        ],
       "dnsSettings": {
         "dnsServers": []
       },
       "enableIPForwarding": false,
       "networkSecurityGroup": {
               "id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
            }
      }
    },

Go to my GitHub repository to find the full version of this template. By default, it will deploy a Nano Server VM.

Now here’s a little gotcha when it comes to NSGs and remote server management (as of this writing): If you created a VM from the Azure Portal and accept all the defaults (which include an NSG), that NSG doesn’t open the ports for WinRM by default.  It only opens RDP.  The OS firewall is open to accept WinRM and PowerShell, but the NSG blocks it.  You need to edit the NSG to include TCP ports 5985 (http) and/or 5986 (https) for remote management.

You might not notice this issue if you are connecting to full GUI servers with RDP, but if you are trying out Nano Server there is no RDP, so you must be able to connect with PowerShell.