Azure NSG Template Exploration

2019/08/04

I should be digging deeper into Azure Active Directory authorization concepts today for my upcoming AZ-301 exam. To be honest, I just wasn’t feeling it. I felt the urge to sink my teeth into something with more immediate practical applications at my job. We use Azure Resource Manager (ARM) templates for consistent resource deployment. We’re expanding our library of templates to include for customer deployment of supporting resources to connect with our network. Customers with less experience in Azure or specific resources appreciate receiving a template they can deploy quickly after customizing a small parameters file.

I pulled up the ARM template reference and started running a few deployment scenarios using Network Security Groups (NSG) - one of the simpler resources to template. I soon found some curious rabbit trails in the template parameters that drew my mind into pursuit.

Round 1 - Default Rules

The first series of tests explored the defaultSecurityRules section of the template. Azure NSGs contain three default rules for each flow direction: Inbound and Outbound. Documentation states that these three rules cannot be modified or deleted, but you can effectively over-ride them by creating rules of higher priority. Default rule priorities of 65000, 65001, and 65500 lie in a special range well below the lowest user-defined rule priority of 4096. Any user-defined rule that provides the same traffic-matching characteristics of a default rule will over-ride the default.

I was surprised to see a section in the template for defaultSecurityRules. Can we actually modify or add to the list of “default” rules? Check out the Microsoft.Network networkSecurityGroups template reference to see the section of interest. I attempted a few deployments to observe the impacts on the default rules.

The Setup

I created a new Resource Group, rg-nsgTest,  for all test deployments. Each test consisted of a new deployment in that Resource Group so I could track the templates and output. I saved the JSON representation of the NSG after each deployment to compare for changes. The table below lists the results of each run. Starting from a simple template defining a single user-defined rule and “default” rule, I ran through a few trials to see if Azure accepted any modifications to the default rules.

Results

Test Results
Create “default” rule with priority 65000 Input ignored; Default rule ‘AllowVnetInBound’ created as normal
Create new “default” rule with priority 65002 Input ignored; Rule not created
Create a “default” rule with priority 4000 Input ignored; Rule not created
Create a “default” rule with priority 65501 Input ignored; Rule not created
Create new “default” rule with name ‘AllowVnetInBound’, priority 65000 Input ignored; Rule not created
Duplicate rule ‘AllowVnetInBound’ but change “access” parameter to deny Input ignored; Rule not created

Across the board the results agree with documentation: default NSG rules cannot be modified. Why include this section in the ARM template? I’m not certain, to be honest. It may be a placeholder for a future capability to modify the default ruleset. Or it could allow for use of output of a show nsg command as input to another deployment without removal of the default rules. The latter doesn’t seem feasible at least for output from Az CLI, as other output is also not valid as input for a deployment. Whatever the reason inclusion of the defaultSecurityRules section in the template remains an annoying curiosity.

Technically the documentation states, “You cannot remove the default rules, but you can override them by creating rules with higher priorities.” (emphasis mine) Work in templates and in the Portal consistently indicates that the default rules cannot be modified at all.

Round 2 - etag

Readers who have used the Azure CLI to dump a resource may remember the etag parameter. The output below shows partial output of an NSG using the Azure CLI show command. You can see multiple etags, the first being for the NSG itself and the second for one of the user-defined security rules.

"etag": "W/\"ec94eea2-c7da-4814-ab8b-76eb18b842d0\"", "id": "/subscriptions/[removed]/resourceGroups/rg-nsgTest/providers/Microsoft.Network/networkSecurityGroups/nsgGuid", "location": "eastus2", "name": "nsgGuid", "networkInterfaces": null, "provisioningState": "Succeeded", "resourceGroup": "rg-nsgTest", "resourceGuid": "960431fd-9ccf-4134-8eb4-01936e301eab", "securityRules": [ { "access": "Allow", "description": "regular rule 1", "destinationAddressPrefix": "20.20.20.20", "destinationAddressPrefixes": [], "destinationApplicationSecurityGroups": null, "destinationPortRange": "443", "destinationPortRanges": [], "direction": "Inbound", "etag": "W/\"ec94eea2-c7da-4814-ab8b-76eb18b842d0\"",

Note the etag is the same for the NSG and the rule. I found this to be the case throughout testing, that the etag matched for the NSG and all rules.

What’s an etag? Try this link first. Think of it this way: When you view an Azure resource, such as with Az CLI, you’re viewing it’s state in an instance of time. You wake up one morning and show the NSG. The output on your terminal shows the state of the NSG at the instant you sent the request. If you return two hours later and deploy a template to update the NSG, has it changed since you issued the show command? The etag provides a way to determine this. If you issue the show command again and the etag is the same as the first, the resource has not changed.

Can we use the etag in the template to enforce a synchronization check between deployments? In other words, can we submit the etag with the template to ensure we’re only updating the resource if its state matches our last-known state?

Results

Simply: No. The etag is not part of the template specification, so a deployment trying to specify it as a parameter is rejected. I don’t think there is a simple way to handle this via template alone. I believe state must be tracked by another means - querying the etag, storing it outside of the template, then peforming another query of etag and comparing to the last-known value immediately before template deployment.

Round 3 - The GUID

My final experiment concerned another property found in the NSG template: resourceGuid. As a “Globally Unique IDentifier”, the GUID uniquely identifies a resource. It is Microsoft’s implementation of the Universally Unique IDentifier (UUID), and not isolated to Azure resources.

A unique identifier allows tracking of a resource regardless of modification to any of its parameters. The GUID serves as the glue that uniquely and consistently identifies the object. How can we use this parameter in NSG deployments? An NSG is largely mutable as its security policies can change and even be identical to another NSG. The NSG name typically remains consistent. Those of you thinking about the NSG ID will remember that property is simply the “fully-qualified name” of the NSG:

"/subscriptions/[removed]/resourceGroups/rg-nsgTest/providers/Microsoft.Network/networkSecurityGroups/validGuid2"

The IDs of two NSGs in the same Resource Group would be separated only by their names. Could having the GUID as a parameter in the template allow us to change the name of an NSG? The Portal does not offer a mechanism for name change, but the Portal exhibits limitations in parameters it can modify. Perhaps this is one.

The Setup

Starting from the end of Round 1, I queried the GUID of the last NSG created. This GUID was used in templates in an attempt to effect name change on the NSG by using the same template, specifying the resourceGuid in the template, and changing the NSG name in the template.

Results

Test Result
Change name w/o specifying GUID New NSG created with new name
Change name w/specifying GUID New NSG created with new name
Create new NSG; Specify GUID New NSG created; GUID different from one specified in template

I can’t determine the purpose of the resourceGuid parameter for the NSG resource. I did not test other providers/resources. Several other Azure resources contain a resourceGuid parameter. Maybe it is honored for other resources. Maybe it is included in the NSG resource for completeness, or future implementation of the name change feature referenced in the link above.

Conclusion

What started as an inquisitive refresher in templates developed into a useful examination of ARM templates for management of resource configurations. It reminded me that templates cannot themselves track resource state. Template deployments through the Azure CLI won’t compare the etag property to confirm deployment against a known state. The effort also uncovered questions that I am pursuing through other channels - what is the purpose of defaultSecurityRules and resourceGuid in the template? If functional, their use isn’t clearly documented. Even a response of “these are not currently used” would be great feedback for awareness in similar future situations.

I’ll update this article with any feedback I receive on the resourceGuid and defaultSecurityRules parameters. If anyone is familiar with these parameters feel free to comment or leave an answer on my MSDN question.