Skip to content

Azure NSG

Import Azure Network Security Group configurations exported with the Azure CLI or the ARM REST API. netbobr turns each custom security rule into flows, extracting source/destination addresses, protocols, and port ranges for compliance analysis. Azure NSG is the highest-accuracy native cloud format, and the only one that supports Tier 2 rules (both specific source and destination CIDRs).

Export Commands

# Export a single NSG
az network nsg show --resource-group myRG --name myNSG > nsg.json

# Export all NSGs in a resource group
az network nsg list --resource-group myRG > nsgs.json

# Export all NSGs in a subscription
az network nsg list > all-nsgs.json

# Export only the custom rules of one NSG
az network nsg rule list --resource-group myRG --nsg-name myNSG > rules.json

# Export a single rule
az network nsg rule show --resource-group myRG --nsg-name myNSG --name allow-https > rule.json

Supported JSON Shapes

netbobr auto-detects all of these shapes:

SourceShape
az network nsg showOne NSG object with a top-level securityRules array (the Azure CLI flattens properties)
ARM template / REST GET of one NSGOne NSG object with properties.securityRules
az network nsg listA top-level JSON array of NSG objects
ARM REST list (GET .../networkSecurityGroups)An object with a value array of NSG objects
az network nsg rule listA top-level array of rules. Rules are grouped by the NSG named in each rule id
az network nsg rule showA single rule object, flat or with a properties wrapper

Only custom security rules are imported. Azure's default security rules (AllowVnetInBound, AllowAzureLoadBalancerInBound, DenyAllInBound, AllowVnetOutBound, AllowInternetOutBound, DenyAllOutBound) are skipped in every shape. That includes the defaultSecurityRules arrays of nsg show / nsg list, and the defaults that az network nsg rule list --include-default mixes into its flat rule list. A rule counts as a default when its id contains /defaultSecurityRules/, or when it has one of those names and a priority of 65000 or more. Each file gets one summary warning:

6 Azure default security rules were not evaluated (defaults are not modelled): AllowVnetInBound, AllowAzureLoadBalancerInBound, DenyAllInBound, AllowVnetOutBound, AllowInternetOutBound, DenyAllOutBound

Example

{
  "name": "web-nsg",
  "properties": {
    "securityRules": [
      {
        "name": "allow-https-inbound",
        "properties": {
          "access": "Allow",
          "protocol": "Tcp",
          "direction": "Inbound",
          "sourceAddressPrefix": "10.0.0.0/8",
          "destinationAddressPrefix": "172.16.1.0/24",
          "destinationPortRange": "443",
          "priority": 100,
          "description": "HTTPS from internal to web tier"
        }
      },
      {
        "name": "allow-sql-outbound",
        "properties": {
          "access": "Allow",
          "protocol": "Tcp",
          "direction": "Outbound",
          "sourceAddressPrefix": "172.16.1.0/24",
          "destinationAddressPrefix": "172.16.2.0/24",
          "destinationPortRange": "1433",
          "priority": 200,
          "description": "SQL from web tier to DB tier"
        }
      }
    ]
  }
}

This produces two flows: 10.0.0.0/8 -> 172.16.1.0/24 TCP/443 (inbound) and 172.16.1.0/24 -> 172.16.2.0/24 TCP/1433 (outbound).

Parser Behavior

Access

  • Allow rules produce flows. The comparison is case-insensitive (allow, ALLOW), and a rule without access is treated as Allow.

  • Deny rules are not evaluated. netbobr analyses allow rules one by one and does not model rule priority, so a Deny cannot be applied to the Allow rules it would shadow. Each file gets one summary warning:

    2 deny rules were not evaluated (netbobr analyses allow rules individually; priority/shadowing is not modelled)

  • Any other access value skips the rule with a warning.

Direction

NSG DirectionFlow SourceFlow Destination
InboundsourceAddressPrefix(es)destinationAddressPrefix(es)
OutboundsourceAddressPrefix(es)destinationAddressPrefix(es)

Source and destination are never swapped. In Azure the rule source is the side that initiates the connection in both directions. For example, the default outbound rule AllowInternetOutBound has source * and destination Internet. The direction is kept in the flow metadata (inbound / outbound). This matches how the AWS, GCP, and Kubernetes parsers treat egress (source = your workload, destination = the remote side). The direction value is case-insensitive.

Protocols

Tcp, Udp, Icmp, and * (any) are analysed. Esp and Ah have no ports and cannot be analysed: those rules are skipped with a warning (protocol "Esp" is not supported -- rule skipped).

Ports

  • Port * (any port) maps to 1-65535.
  • A range starting at 0 (0-65535, 0-1023) is normalised to start at 1 (1-65535, 1-1023).
  • Ranges like 8000-9000 are kept.
  • destinationPortRange and destinationPortRanges are combined (duplicates removed), giving one flow per port or range.
  • A rule with no destination port is analysed as 1-65535, with a warning.
  • For Icmp rules the port field becomes the ICMP type label (* → all).

Addresses

  • sourceAddressPrefix / sourceAddressPrefixes and destinationAddressPrefix / destinationAddressPrefixes are combined and expanded. Each rule produces one flow per source × destination × port combination.
  • Azure NSG is the only native cloud format where both source and destination can be specific CIDRs (Tier 2 support).
  • IPv6 prefixes are passed through and show up as visible error rows ("IPv6 not supported."), because netbobr analyses IPv4 only.

Service Tags and Application Security Groups

* and Internet mean "any address" and become 0.0.0.0/0 without a warning.

Every other service tag stands for Azure-managed IP ranges that netbobr cannot resolve offline. That includes VirtualNetwork, AzureLoadBalancer, AzureCloud, Storage, Sql and the regional forms such as Storage.WestEurope, Sql.EastUS2 and AzureFrontDoor.Backend. The same is true for application security groups (sourceApplicationSecurityGroups / destinationApplicationSecurityGroups). netbobr keeps the rule, uses 0.0.0.0/0 as a placeholder for that side, and always shows a warning naming the rule, the side, and what was replaced:

Azure NSG web-nsg rule "allow-internal": source service tag "VirtualNetwork" cannot be resolved to CIDR -- substituted 0.0.0.0/0

Azure NSG web-nsg rule "web-to-db": source application security group "asg-web" cannot be resolved to CIDR -- substituted 0.0.0.0/0

The placeholder makes the flow look as broad as possible, so treat its risk score as a worst case. You can replace the tag with explicit CIDR ranges in the imported rows, or add the flows manually.

  • Mixed sides. A side that lists both CIDRs and tags keeps the CIDRs and adds the placeholder once. For example, ["10.0.0.0/8", "Storage"] gives 10.0.0.0/8 and 0.0.0.0/0, with one warning for Storage.
  • Both sides unresolvable. If neither the source nor the destination resolves to a CIDR (for example VirtualNetwork → VirtualNetwork, or ASG → ASG), the rule is skipped instead of becoming an any-to-any flow. The warning banner lists it with its own message, for example Azure NSG web-nsg rule "vnet-to-vnet": skipped -- source service tag "VirtualNetwork" and destination service tag "VirtualNetwork" cannot be resolved to CIDR.

Not Modelled

  • Priority and shadowing. Rules are analysed independently. A higher-priority Deny does not suppress a lower-priority Allow for the same traffic.
  • Default security rules. They are skipped in every shape and counted in one summary warning (see Supported JSON Shapes).
  • Deny rules. They are counted in the summary warning but not evaluated.
  • Source ports. sourcePortRange(s) are ignored; flows use the destination port.

Accuracy

MetricValue
CLI match rate97.0% (388/400)
Browser match rate97.0% (388/400)
Tier 1 accuracy100.0% (348/348)
Tier 2 accuracy100.0% (40/40)
Field accuracy100% (risk score, risk level, verdict)

Azure NSG has the highest accuracy among native cloud formats:

  • All baseline flows match. The 400-row baseline contains 388 unique flows (12 rows are duplicates), and all 388 are reproduced. That is why the match rate reads 97.0% (388/400).
  • 100% Tier 2. It is the only native format that supports rules with both specific source and destination CIDRs.
  • 100% field accuracy. When a flow matches, its risk score, risk level, and verdict are identical to the CSV baseline.
  • Direction is checked. The fixture writes outbound rules exactly as Azure does (source = initiator). A parser that swapped outbound source and destination would drop to about 79%.

The parser reads 403 flows from the fixture: one per baseline rule, plus 3 service-tag rules that are substituted with 0.0.0.0/0 and warned.

Troubleshooting

No flows parsed

Check that the file is one of the supported shapes. An ARM export template (az group export, with a top-level resources array) is not supported: export the NSG with az network nsg show or az network nsg list instead. If the NSGs were read but produced no flows (for example, only Deny rules), the warning banner says so: Azure NSG: 1 NSG and 2 custom rules read -- no Allow flows to analyse (default security rules are not modelled).

Flows with 0.0.0.0/0 that are not in my NSG

A service tag or application security group was replaced by the 0.0.0.0/0 placeholder. The warning banner lists every substitution.

Deny rules not showing

By design: netbobr evaluates allowed traffic. The banner shows how many Deny rules were not evaluated.

Rules with multiple address prefixes produce many flows

If a rule uses sourceAddressPrefixes and destinationAddressPrefixes arrays, netbobr creates one flow for each source/destination/port combination. This is correct: it represents the full cross-product of allowed traffic. Imports are capped at 500 rows.

Usage

CLI:

npx @netbobr/cli analyze nsg.json --cloud-format azure-nsg

Browser: Drag and drop the .json file onto the import area. The format is auto-detected.