Networking
Azure Network Watcher: validate Flow Logs before opening an NSG rule
A production runbook for qualifying a blocked Azure flow with Network Watcher, NSG Flow Logs, effective security rules, routes, firewall, KQL evidence, validation and rollback before opening a broad rule.
When an Azure workload can no longer reach an internal API, Storage account, SQL database or partner endpoint, the request often arrives in a simple form: “open the NSG”. That is the risky moment. Without evidence, the team may add a broad rule that hides a wrong UDR, a firewall block, an unexpected DNS answer, subnet drift or a workload that is no longer sending the flow at all.
The use case is an orders-api-prod service running on a VM or another workload attached to an application subnet. Since a network change, it cannot reach payments-api-prod on 443. The runbook goal is to decide whether to fix an NSG rule, route, firewall policy, source NAT, target configuration or roll back the network change before widening access.
Freeze the expected flow
Start by describing the flow as an operable contract. An NSG rule is not only a source IP and a port. You need the real source, target, protocol, expected path, inspection point and evidence required to close the incident.
flow:
name: orders-to-payments-prod
source:
workload: orders-api-prod
subnet: snet-app-prod
expected_private_ip: 10.42.12.24
destination:
service: payments-api-prod
fqdn: payments.internal.contoso.local
expected_private_ip: 10.44.8.15
port: 443
protocol: Tcp
expected_path:
vnet: vnet-prod-spoke-a
route_next_hop: AzureFirewall
inspection: azfw-prod-weu
destination_zone: internal-api
control_points:
- source_nsg
- destination_nsg
- effective_route
- azure_firewall_policy
- dns_answer_from_workload
- target_service_log
decision_needed:
- fix_nsg_rule
- fix_route_or_firewall_policy
- fix_dns_or_target
- rollback_network_change This contract prevents every failure from becoming an NSG rule request. A flow can be blocked by the source NSG, destination NSG, a route sending traffic elsewhere, Azure Firewall or a target service rejecting the connection.
Check effective rules before declared rules
Declared configuration is not enough. Azure evaluates priorities, default rules, subnet and NIC associations, and sometimes multiple NSGs along the path. The useful state is the effective state seen by the network interface.
SUBSCRIPTION="00000000-0000-0000-0000-000000000000"
RG="rg-app-prod"
VM="orders-api-prod-01"
NIC="orders-api-prod-01-nic"
az account set --subscription "$SUBSCRIPTION"
az network nic list-effective-nsg --resource-group "$RG" --name "$NIC" --output table
az network watcher test-ip-flow --resource-group "$RG" --vm "$VM" --direction Outbound --protocol TCP --local 10.42.12.24:49152 --remote 10.44.8.15:443 --output json test-ip-flow gives an initial decision: allowed or denied, with the responsible rule when it can identify one. If the test allows the flow while the application still fails, keep the diagnosis open. The issue is probably elsewhere in the path or at the target.
Read the effective routes
A flow allowed by the NSG can still disappear into a wrong route. A UDR may send traffic to a firewall, an NVA, the Internet, an unexpected peering or a next hop that no longer exists. Before opening a rule, prove the next hop.
RG="rg-app-prod"
NIC="orders-api-prod-01-nic"
DESTINATION="10.44.8.15"
az network nic show-effective-route-table --resource-group "$RG" --name "$NIC" --output table
az network watcher show-next-hop --resource-group "$RG" --vm "orders-api-prod-01" --source-ip 10.42.12.24 --dest-ip "$DESTINATION" --output json If the observed next hop does not match the contract, the right fix is not an NSG rule. It is a route correction, route table association fix, peering fix or rollback of the UDR change.
Correlate Flow Logs with KQL
NSG Flow Logs and Traffic Analytics-enriched logs are useful when they answer a precise question: did the flow leave, in which direction, with which decision, during which time window and from which real IP?
let StartTime = datetime(2026-07-29T08:00:00Z);
let EndTime = datetime(2026-07-29T10:00:00Z);
let SourceIp = "10.42.12.24";
let DestinationIp = "10.44.8.15";
AzureNetworkAnalytics_CL
| where TimeGenerated between (StartTime .. EndTime)
| where SrcIP_s == SourceIp or DestIP_s == SourceIp
| where SrcIP_s == DestinationIp or DestIP_s == DestinationIp or DestPort_d == 443
| project TimeGenerated,
FlowDirection_s,
SrcIP_s,
SrcPort_d,
DestIP_s,
DestPort_d,
L4Protocol_s,
FlowStatus_s,
NSGRule_s,
NSGList_s,
Subnet_s,
VM_s
| order by TimeGenerated desc Adapt the table to the collection mode actually enabled in the environment. The evidence stays the same: a Deny decision at the NSG does not have the same cause as no flow at all, or a flow allowed by the NSG and then blocked by the firewall.
Separate missing evidence from a proven block
A common trap is opening a rule because no log shows the flow. Missing logs can come from disabled telemetry, a short collection window, a missing diagnostic setting, DNS resolving to another IP or an application that no longer emits the call.
Flow Log shows Deny on the expected rule
Fix the narrowest possible rule
Validate with test-ip-flow and an application call
Flow Log shows Allow but the call fails
Check Azure Firewall, proxy, TLS, target and application logs
Do not widen the NSG
No flow observed
Check Diagnostic Settings, collection window and DNS from the workload
Prove that the application really emits the traffic
Unexpected source IP
Check NAT, load balancer, subnet, revision or active host
Fix inventory before adding any rule
Unexpected destination IP
Check DNS, Private Resolver, private zone or application configuration
Fix resolution before opening the network This separation protects production. A rule opened on the wrong IP fixes nothing and creates durable exception debt.
Build a bounded correction
If an NSG block is confirmed, the correction must be minimal, named, testable and reversible. Avoid VirtualNetwork to Any rules or wide port ranges when the expected flow is known.
RG_NET="rg-network-prod"
NSG="nsg-snet-app-prod"
RULE="allow-orders-to-payments-443"
az network nsg rule create --resource-group "$RG_NET" --nsg-name "$NSG" --name "$RULE" --priority 320 --direction Outbound --access Allow --protocol Tcp --source-address-prefixes 10.42.12.24 --source-port-ranges "*" --destination-address-prefixes 10.44.8.15 --destination-port-ranges 443 --description "Temporary validated flow orders-api-prod to payments-api-prod, evidence INC-20260729-014" The rule should carry an evidence reference and an owner. If the target is a managed service whose IP can change, use the service-appropriate mechanism instead of freezing a private IP without governance.
Validate and prepare rollback
Incident closure is more than “the connection works again”. You need to prove that the exact rule is used, the application flow is healthy, the path still matches the contract and the return path is ready.
Final validation
test-ip-flow returns Allow for the exact flow
Flow Logs show the expected rule, not a broader rule
The next hop still matches the contract
Firewall or target logs show a correlated success
The application validates the business scenario
IaC or the network PR reflects the correction
Rollback
Remove the temporary rule or revert the previous network commit
Verify the expected block returns if the rule was temporary
Restore the previous UDR association if routing was the cause
Keep evidence in the incident ticket If the correction was applied manually, it remains provisional until it is captured in IaC or removed after stabilization.
Conclusion
Before opening an NSG rule, the right sequence is to freeze the expected flow, read effective rules, prove the next hop, correlate Flow Logs, separate missing evidence from a proven block, then apply a bounded correction.
The final decision should be explicit: open a targeted rule when the NSG really blocks the flow, fix route, DNS or firewall when evidence points elsewhere, or roll back the network change when the expected path is no longer respected. That turns an urgent access request into an operable and reversible production change.