Networking

Azure NSG: validate a Service Tag rule before rollout

A production runbook for qualifying an Azure NSG rule based on a Service Tag using regional scope, real prefixes, effective rules, flow tests, canary rollout and rollback.

06 Sept 2026 azurenetworkingnsgservice-tagssecuritynetwork-watchervnet-flow-logsautomationobservabilityrunbookrollbackproduction

A team needs to let production subnet workloads reach a public Azure service. The proposed change looks narrow: replace Internet with a Service Tag in the outbound NSG rule. Yet that tag may cover every region, gain prefixes without a deployment, and represent a service rather than one specific resource. A syntactically valid rule can therefore be too broad, incomplete or difficult to explain during an incident.

The use case is a worker pool exporting telemetry to Azure Monitor over HTTPS. The runbook is not satisfied with an Allow result. It must prove the requirement, select the right scope, verify the effective NSG decision, observe a real flow, and then decide whether to promote, revise or roll back the rule.

Freeze the flow contract

Describe the flow without naming the candidate rule first. This keeps the proposed solution from becoming the diagnosis.

yaml nsg-service-tag-change.yml
change:
id: CHG-2847
source: snet-workers-prod
source_resource: vmss-workers-prod
direction: outbound
protocol: tcp
destination_port: 443
business_dependency: telemetry-export
candidate_service_tag: AzureMonitor
candidate_scope: global

current_state:
matching_rule: DenyInternetOutBound
observed_result: denied

required_evidence:
- destination-fqdn-and-resolved-ip
- service-tag-purpose-and-direction
- current-tag-prefix-snapshot
- effective-rule-decision
- application-request-correlation
- rollback-rule-and-owner

The called FQDN, resolved IP and port still matter. A Service Tag groups Microsoft-managed IP prefixes; it does not validate a TLS name, tenant or target resource. When the requirement is limited to one instance or needs FQDN-aware enforcement, an NSG rule based on the tag cannot carry the entire policy.

Verify what the Service Tag actually means

Review the tag definition before reviewing the configuration. Some tags are recommended only for inbound or outbound use, some support a regional suffix, and some depend on other tags. Do not choose AzureCloud merely for convenience: it represents Azure datacenter public addresses, including resources outside your organization.

text service-tag-review.txt
Candidate tag: AzureMonitor

Review
Purpose matches the actual dependency
Recommended direction includes outbound
Regional form exists or does not exist
Required dependent tags are identified
Public cloud matches the workload cloud
Tag is supported on an NSG destination

Hard stops
AzureCloud selected because the dependency is unknown
Inbound-only tag used for an outbound requirement
Service tag treated as a resource or tenant identity
Required protocol or destination port still unknown

Regional scope is not cosmetic. When supported, a form such as Storage.FranceCentral narrows the rule to prefixes for that region. When the tag has no regional form, record that the rule remains global and add appropriate application-layer controls.

Capture the prefixes behind the tag

Microsoft updates Service Tag prefixes automatically. Operations should retain a snapshot at change time and detect later changes instead of assuming the scope is static.

bash 01-capture-service-tag-prefixes.sh
LOCATION="francecentral"
TAG="AzureMonitor"

az network list-service-tags \
--location "$LOCATION" \
--query "values[?name=='$TAG'].{name:name,changeNumber:properties.changeNumber,prefixes:properties.addressPrefixes}" \
--output json > "service-tag-${TAG}-${LOCATION}.json"

jq -e 'length == 1 and .[0].prefixes != null and (. [0].prefixes | length > 0)' \
"service-tag-${TAG}-${LOCATION}.json"

jq -r '.[0].changeNumber, (.[0].prefixes[] | tostring)' \
"service-tag-${TAG}-${LOCATION}.json"

The command location selects where the API is queried; it does not make a global tag regional or a regional tag global. Retain the changeNumber, cloud, timestamp and prefixes. That snapshot supports review, incident diagnosis and drift control.

Read the deployed and effective rules

The proposed template is not the effective state. Verify NSG association on the subnet, priority, any NIC-level NSG, and security admin rules from Azure Virtual Network Manager.

bash 02-review-effective-nsg.sh
RESOURCE_GROUP="rg-network-prod"
NSG="nsg-workers-prod"
NIC="vmss-workers-prod-instance-nic"

az network nsg rule list \
--resource-group "$RESOURCE_GROUP" \
--nsg-name "$NSG" \
--query "[].{priority:priority,name:name,direction:direction,access:access,destination:destinationAddressPrefix,ports:destinationPortRanges}" \
--output table

az network nic list-effective-nsg \
--resource-group "$RESOURCE_GROUP" \
--name "$NIC" \
--output json > effective-nsg-before.json

An allow rule proves nothing when a higher-precedence deny applies. Conversely, a successful test may be using an older, broader rule. The expected evidence is the name of the rule that actually decides the flow, not the presence of the candidate alone.

Simulate the flow before observing it

For a supported VM or NIC, IP Flow Verify evaluates direction, protocol, local and remote IPs and ports, then returns the rule responsible for allowing or denying the packet. Test at least one currently resolved service IP and one control destination that must remain denied.

bash 03-test-ip-flow.sh
VM_ID="/subscriptions/<subscription>/resourceGroups/rg-app-prod/providers/Microsoft.Compute/virtualMachineScaleSets/vmss-workers-prod/virtualMachines/3"
LOCAL_IP="10.42.8.17"
REMOTE_IP="<resolved-azure-monitor-ip>"

az network watcher test-ip-flow \
--vm "$VM_ID" \
--direction Outbound \
--protocol TCP \
--local "${LOCAL_IP}:52000" \
--remote "${REMOTE_IP}:443" \
--output json

# Negative control: this destination must remain denied.
az network watcher test-ip-flow \
--vm "$VM_ID" \
--direction Outbound \
--protocol TCP \
--local "${LOCAL_IP}:52001" \
--remote "203.0.113.10:443" \
--output json

Adapt the target to the resource types supported by the tool. The simulation qualifies NSG policy, not DNS resolution, routing, an intermediate firewall, TLS or service availability. An Allow is necessary evidence, not an end-to-end test.

Canary the rule and observe real traffic

Apply the rule to one bounded subnet or worker group first. Keep the existing deny, insert the allow at a documented priority, and observe for a window that covers normal workload behavior.

text service-tag-canary-gates.txt
Canary scope
One worker group or dedicated subnet
TCP 443 only
Candidate Service Tag only
No parallel firewall or route change

Positive gates
Application export succeeds with correlation ID
Effective NSG result names the intended allow rule
Destination IP belongs to the captured tag snapshot
VNet flow logs show the expected tuple and decision
Error and retry rates return to the accepted baseline

Negative gates
Control destination remains denied
No unexpected ports become reachable
No broader rule masks the candidate
No unexplained destination prefix appears

Use Virtual Network flow logs for new collection. New NSG flow logs can no longer be created and that mechanism is being retired. Network logs prove the observed tuple; correlate them with the application request because an allowed TCP flow does not prove that the intended service accepted the telemetry.

Automate meaningful drift detection

A Service Tag can change outside the IaC pipeline. A useful automation periodically captures the tag, compares its changeNumber and prefix set, and opens a review when scope changes.

text service-tag-drift-policy.txt
On service tag change
Store old and new changeNumber
Produce added and removed CIDR sets
Identify every NSG and UDR using the tag
Re-run positive and negative flow tests
Verify application probes and VNet flow logs
Require review when scope or dependencies change

Do not
Expand custom IP allowlists automatically
Treat a prefix addition as proof of compromise
Remove a prefix before dependent traffic has drained
Skip validation because Microsoft manages the tag

The useful signal is not “the tag changed.” It is “this change alters the scope of a rule protecting this flow.” Link the network inventory to an application owner and a validation test.

Decide promotion, revision or rollback

End the change with an explicit decision.

yaml service-tag-decision.yml
promote:
when:
- purpose-and-direction-match
- effective-rule-is-the-intended-rule
- positive-and-negative-tests-pass
- application-and-flow-evidence-correlate

revise:
when:
- a-regional-tag-can-reduce-scope
- a-required-dependent-tag-is-missing
- another-rule-masks-the-candidate
action: revise-and-repeat-canary

rollback:
when:
- unexpected-destination-becomes-reachable
- application-failure-rate-increases
- effective-decision-cannot-be-explained
action:
- remove-candidate-rule
- restore-previous-priorities
- verify-control-deny
- reconcile-in-flight-exports

Roll back the rule, not the entire network policy. Keep the tag snapshot and before-and-after results so the team can distinguish a scope, priority, route or application issue.

Conclusion

A Service Tag simplifies Azure prefix maintenance, but it does not replace a flow contract or destination identity. An operable NSG rule connects an application requirement, direction, port, possible regional scope, prefixes at change time and the effective rule.

Make the decision after a positive test, a refusal control, a canary and application evidence correlated with network logs. If the scope cannot be explained or another rule decides the flow, revise before promotion. If the canary broadens access or degrades the service, remove the candidate rule alone and return to the captured state.