Networking
Azure App Service: validate all-traffic VNet routing before production cutover
A production runbook for validating App Service all-traffic VNet routing across application calls, image pulls, content storage, backups, managed identity, firewall evidence and rollback.
An App Service application already reaches a private API through VNet Integration. The network team now wants every outbound flow to traverse the integration subnet and its controlled egress path. Enabling outboundVnetRouting.allTraffic looks like a single routing change. It also moves configuration traffic such as container image pulls, content share access, backup operations and managed identity token acquisition into that path.
The application can therefore pass its health check while the next deployment, token refresh or backup fails. This runbook builds a cutover decision from the deployed routing properties, the subnet controls, a dependency matrix and a canary. The objective is to enable all-traffic routing without confusing application reachability with platform configuration reachability, and to retain a precise rollback.
Freeze the routing contract
Start with the reason for the change and the flows that must remain healthy. Do not reduce the requirement to “send everything through the firewall”. Name the sources of evidence and the conditions that stop the cutover.
app: app-orders-prod
region: westeurope
integration_subnet: snet-appservice-integration
change: enable-outbound-vnet-routing-all-traffic
required_flows:
application:
- private-orders-api:443
- public-payment-api:443
configuration:
- container-image-pull
- content-share-access
- backup-restore
- managed-identity-token-acquisition
cutover_requires:
- canary-starts-with-the-approved-image
- application-probes-pass
- managed-identity-call-succeeds
- expected-firewall-egress-is-visible
rollback_on:
- image-pull-or-startup-failure
- token-acquisition-regression
- missing-egress-proof
- dependency-latency-above-budget Record the application artifact, infrastructure commit, current outbound IPs and the last known successful deployment. A successful HTTP probe alone cannot qualify configuration traffic that only appears during startup, deployment or backup.
Read the effective App Service properties
App Service exposes application-only routing and all-traffic routing under properties.outboundVnetRouting. Legacy vnetRouteAllEnabled and WEBSITE_VNET_ROUTE_ALL settings can still exist, so inventory both the current properties and old settings before changing anything.
RG="rg-orders-prod"
APP="app-orders-prod"
az resource show --resource-group "$RG" --name "$APP" --resource-type "Microsoft.Web/sites" --query "{subnet:properties.virtualNetworkSubnetId,routing:properties.outboundVnetRouting,legacyRouteAll:properties.vnetRouteAllEnabled}" --output json > app-routing-before.json
az webapp config appsettings list --resource-group "$RG" --name "$APP" --query "[?name=='WEBSITE_VNET_ROUTE_ALL' || name=='WEBSITE_CONTENTOVERVNET'].{name:name,value:value}" --output table
az webapp vnet-integration list --resource-group "$RG" --name "$APP" --output table Keep app-routing-before.json with the change record. If IaC declares one model while the deployed resource still carries a legacy setting, reconcile that drift before the cutover; otherwise a later deployment can silently restore another route behavior.
Map application and configuration traffic separately
Application traffic is generated by the workload. Configuration traffic supports the platform lifecycle. The two can fail at different moments and can require different destination rules.
Flow When it appears Proof
Private API request steady state DNS, TCP/TLS, app trace
Public SaaS request steady state response, observed egress IP
Container image pull deploy and restart successful pull and startup
Content share access start and file access mount/read/write canary
Backup and restore scheduled or manual completed canary backup
Managed identity token startup and token refresh token plus target API success
For every row capture
destination FQDN or service boundary
resolved address from the app context
expected route and next hop
NSG and firewall policy owner
expected source IP or private path
log source and validation window Private Endpoint, when present for a dependency, only defines that dependency’s private destination. It does not prove the integration subnet route, DNS answer, firewall allowance or configuration traffic path.
Prove the integration subnet controls
The routing property decides what enters the VNet. The subnet then decides where that traffic can go. Read its delegation, route table, NSG and NAT attachment as one control surface.
VNET_RG="rg-network-prod"
VNET="vnet-prod-spoke"
SUBNET="snet-appservice-integration"
az network vnet subnet show --resource-group "$VNET_RG" --vnet-name "$VNET" --name "$SUBNET" --query "{prefix:addressPrefix,delegations:delegations[].serviceName,routeTable:routeTable.id,nsg:networkSecurityGroup.id,natGateway:natGateway.id}" --output json > integration-subnet.json
ROUTE_TABLE_ID=$(jq -r '.routeTable // empty' integration-subnet.json)
if [ -n "$ROUTE_TABLE_ID" ]; then
az network route-table route list --ids "$ROUTE_TABLE_ID" --query "[].{name:name,prefix:addressPrefix,nextHop:nextHopType,nextHopIp:nextHopIpAddress}" --output table
fi A 0.0.0.0/0 UDR toward Azure Firewall or an NVA must be matched by DNS, destination rules and return routing. A NAT Gateway attached to the subnet does not override a UDR that sends traffic to a virtual appliance. Predict the next hop before generating test traffic.
Exercise the path from the application context
Run probes from the application console or a diagnostic endpoint in the canary application. A VM elsewhere in the VNet may use different routes, DNS servers and NSG rules.
PRIVATE_HOST="orders-api.internal.example"
PUBLIC_HOST="payments.example"
printenv | grep WEBSITE_PRIVATE_IP
nslookup "$PRIVATE_HOST"
nslookup "$PUBLIC_HOST"
curl --fail --silent --show-error --connect-timeout 5 "https://$PRIVATE_HOST/health"
curl --fail --silent --show-error --connect-timeout 5 "https://$PUBLIC_HOST/health"
# Call a bounded application endpoint that acquires its managed identity token
# and reads one non-sensitive value from the approved target service.
curl --fail --silent --show-error "https://app-orders-canary.azurewebsites.net/ops/identity-probe" On native Windows apps, use App Service diagnostic tools such as nameresolver.exe and tcpping.exe instead of assuming common network utilities are available. Capture timestamps for every probe so application, firewall and destination logs can be correlated.
Cut over one canary with the production route shape
Use an equivalent non-production app on a subnet with the same route, NSG, DNS and egress policies. Deploy the approved artifact first, capture the baseline, then change only the routing property.
RG="rg-orders-canary"
APP="app-orders-canary"
az resource update --resource-group "$RG" --name "$APP" --resource-type "Microsoft.Web/sites" --set properties.outboundVnetRouting.allTraffic=true --output none
az resource show --resource-group "$RG" --name "$APP" --resource-type "Microsoft.Web/sites" --query "properties.outboundVnetRouting" --output json
# Restart only the canary to force startup and image-pull paths.
az webapp restart --resource-group "$RG" --name "$APP" Re-run the application probes, force a deployment or restart that proves the image path, exercise the managed identity probe and execute a non-critical backup test when backup is in scope. Do not promote after observing only an already-running process.
Correlate failures with the route change
The change must create a readable timeline: property update, restart or deployment, runtime outcome and network decision. Adapt table names to the diagnostics enabled in the environment.
let Start = datetime(2026-09-14T06:30:00Z);
let End = datetime(2026-09-14T07:30:00Z);
union isfuzzy=true
(
AzureActivity
| where TimeGenerated between (Start .. End)
| where ResourceProviderValue =~ "MICROSOFT.WEB"
| where ResourceGroup =~ "rg-orders-canary"
| project TimeGenerated, Source="AzureActivity", Detail=OperationNameValue, Result=ActivityStatusValue
),
(
AppServiceConsoleLogs
| where TimeGenerated between (Start .. End)
| where _ResourceId has "/sites/app-orders-canary"
| project TimeGenerated, Source="AppServiceConsoleLogs", Detail=ResultDescription, Result="runtime"
)
| order by TimeGenerated asc For firewall logs, filter the same window by the integration subnet and expected destinations. An explicit deny gives a policy finding. No firewall event for a timestamped request points back to DNS, routing, local filtering or a test that never left the process.
Decide promotion, partial routing or rollback
promote_all_traffic:
when:
- application_and_configuration_matrix_passes
- expected_firewall_or_nat_egress_is_proven
- restart_and_deployment_complete
- managed_identity_refresh_is_validated
use_selective_routing:
when:
- application_traffic_requires_vnet
- one_configuration_flow_is_not_ready
action:
- keep_applicationTraffic_true
- enable_only_qualified_configuration_properties
- open_a_bounded_follow_up_for_missing_flow
hold:
when:
- destination_inventory_is_incomplete
- dns_or_next_hop_is_ambiguous
- firewall_evidence_is_missing
rollback:
action:
- restore_app-routing-before.json_through_iac
- restart_the_canary
- repeat_the_same_probe_matrix
- confirm_deployment_token_and_backup_return_to_baseline Selective routing is a controlled intermediate state, not an undocumented exception. Keep the chosen applicationTraffic, imagePullTraffic, contentShareTraffic, backupRestoreTraffic and managedIdentityTraffic properties in IaC with an owner and an exit condition.
Conclusion
Enabling App Service all-traffic VNet routing changes more than the path of HTTP calls emitted by the application. It can move image pulls, content access, backups and managed identity token acquisition behind the integration subnet’s DNS, UDR, NSG, firewall and NAT controls.
Promote the change only after a canary proves both application and configuration traffic on the production route shape. If one lifecycle flow is not ready, keep routing selective and visible. If startup, identity or deployment regresses, restore the captured property set and replay the same matrix before closing the change.