On March 31, 2026, Microsoft will change how new Azure Virtual Networks handle outbound internet connectivity. The announcements mostly talk about VMs — but I keep getting the same question from colleagues and customers who run primarily PaaS workloads: does this affect us?
I’ve spent a lot of time digging into the official docs, testing in lab environments, and talking this through with people who work on these things daily. This post is my attempt to lay it all out — from the networking fundamentals up to the per-service impact — so you can make your own assessment.
The short answer for most PaaS-heavy organizations: probably not. But the devil is in the details, and there are a few gotchas that will absolutely bite you if you’re not paying attention.
Quick Assessment: Am I Affected?
Before we go deep, here’s the cheat sheet:
flowchart TD
Q1{"Your scenario?"} -->|Existing VNets<br/>created before March 31, 2026| A1["✅ NOT AFFECTED<br/>Nothing changes. Not now, not later."]
Q1 -->|New VNets + VMs<br/>without explicit outbound| A2["❌ AFFECTED<br/>Subnets will be private by default."]
Q1 -->|PaaS services with<br/>delegated/managed subnets| A3["✅ NOT AFFECTED<br/>Microsoft manages outbound for those."]
Q1 -->|Hub-and-spoke with<br/>UDR to Azure Firewall| A4["✅ NOT AFFECTED<br/>You already have explicit outbound."]
Q1 -->|AKS clusters| A5["⚠️ MOSTLY FINE<br/>AKS handles it, but read the details."]
Q1 -->|100% PaaS<br/>no VMs, no VMSS| A6["✅ NOT AFFECTED<br/>Almost certainly no impact."]
style A1 fill:#d4edda,stroke:#28a745
style A2 fill:#f8d7da,stroke:#dc3545
style A3 fill:#d4edda,stroke:#28a745
style A4 fill:#d4edda,stroke:#28a745
style A5 fill:#fff3cd,stroke:#ffc107
style A6 fill:#d4edda,stroke:#28a745
Now let’s understand why.
Azure Networking Fundamentals: It’s All Software
Does a VNet Actually “Exist”?
This trips up even experienced engineers. A VNet is a software construct. There’s no physical switch, no VLAN, no dedicated cable in a datacenter that represents it. It’s configuration metadata enforced by Azure’s Software-Defined Networking (SDN) stack.
When you create a VNet with address space 10.0.0.0/16, you’re telling Azure: “Enforce this address space as an isolation boundary. Let NICs placed here communicate within it, and make traffic crossing its boundaries follow my rules.”
The enforcement happens through something called the Virtual Filtering Platform (VFP) — think of it as an invisible programmable firewall and router sitting between every VM and the physical network. VFP runs on every Azure host inside the Hyper-V virtual switch, and every single packet from every VM passes through it. It checks NSG rules, evaluates route tables, handles VNET encapsulation, and decides whether a packet goes to the internet or gets dropped. If you’ve ever used Accelerated Networking, the fast path is VFP offloading its rules to the SmartNIC hardware — same logic, just faster 1.
You don’t need to know VFP exists to use Azure networking. But it helps to understand that when I say “Azure evaluates your route table” — it’s VFP doing that work, per packet, on the host where your VM runs. There’s no separate routing appliance somewhere.
What Is a Subnet?
A subnet is a logical partition within a VNet — a range of IPs you use to organize and apply policy. What makes subnets meaningful is what you attach to them: NSGs, route tables, service endpoints, delegations, and — the star of this post — the defaultOutboundAccess property.
How Azure Routing Works (and Which Route Wins)
Routing in Azure is evaluated per subnet, per packet 3. When a packet leaves a NIC, Azure looks at the destination IP and consults the effective route table for that subnet. This table is built from multiple sources, and the selection logic matters when you’re troubleshooting.
Route Precedence
flowchart TD
pkt["Packet leaves VM NIC<br/>Destination: X.X.X.X"] --> lpm{"Step 1: Longest Prefix Match<br/>(LPM) — always wins first"}
lpm -->|Multiple routes<br/>same prefix length| src{"Step 2: Route source<br/>precedence"}
lpm -->|Single longest<br/>match found| done["✅ Use that route"]
src -->|"1️⃣ User-Defined Route (UDR)"| udr["UDR wins<br/>(highest priority)"]
src -->|"2️⃣ BGP route<br/>(from VPN/ER gateway)"| bgp["BGP wins<br/>(if no UDR)"]
src -->|"3️⃣ System route"| sys["System route wins<br/>(lowest priority)"]
style pkt fill:#fff,stroke:#333
style done fill:#d4edda,stroke:#28a745
style udr fill:#cce5ff,stroke:#004085
style bgp fill:#e2e3e5,stroke:#383d41
style sys fill:#f8f9fa,stroke:#6c757d
A /28 beats a /16 beats a /0, regardless of source. Longest prefix match always wins first. Only when prefix lengths are equal does the source type matter.
Two important exceptions from the docs 3:
- System routes for VNet traffic, VNet peering, and virtual network service endpoints are preferred routes — they take precedence even if a BGP route has a more specific prefix.
- Routes with a service endpoint next-hop type (
VirtualNetworkServiceEndpoint) cannot be overridden even by UDRs. They sit at the absolute top of Azure’s routing hierarchy. This matters when you’re designing routing policies — you can’t accidentally break service endpoint traffic with a UDR.
Default System Routes
Every subnet gets these automatically 3:
| Prefix | Next Hop | Purpose |
|---|---|---|
| VNet address space (e.g. 10.0.0.0/16) | VNet | Traffic within the VNet stays in the VNet |
| 0.0.0.0/0 | Internet | Default route — anything not matched goes to the internet |
| 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 | None | RFC 1918 ranges not in your VNet are dropped |
| 100.64.0.0/10 | None | Shared address space (RFC 6598 / CGNAT) is dropped |
The 0.0.0.0/0 → Internet system route is what gives VMs internet access. But how that access works depends on whether the VM has explicit outbound or relies on default outbound.
What Default Outbound Access Actually Is
Here’s what happens today when you deploy a VM with only a private IP — no public IP, no NAT Gateway, no load balancer, no UDR to a firewall:
sequenceDiagram
participant VM as VM (10.0.1.4)<br/>Private IP only
participant VFP as VFP on Host
participant Azure as Azure Platform
participant Internet as Internet
VM->>VFP: Packet to 8.8.8.8
VFP->>VFP: Route lookup: 0.0.0.0/0 → Internet
VFP->>VFP: No explicit outbound method found
VFP->>Azure: Apply "Default Outbound Access"
Note over Azure: Assigns ephemeral public IP<br/>from Microsoft-owned pool:<br/>• NOT visible to you<br/>• NOT static (can change)<br/>• Shared with other tenants<br/>• ZERO control
Azure->>Internet: Packet exits with source:<br/>20.x.x.x (ephemeral)
This “hidden public IP” is what Microsoft calls default outbound access 4. It was convenient — your VM just worked — but it was always a problem:
- Security risk: You can’t see or control the IP. It contradicts Zero Trust.
- Unreliable: The IP can change without warning, breaking things that depend on a stable source IP.
- Multi-NIC chaos: VMs with multiple NICs can get different default outbound IPs, causing asymmetric routing.
What Happens in a Private Subnet?
When defaultOutboundAccess = false, the same scenario plays out very differently:
sequenceDiagram
participant VM as VM (10.0.1.4)<br/>Private IP only
participant VFP as VFP on Host
VM->>VFP: Packet to 8.8.8.8
VFP->>VFP: Route lookup: 0.0.0.0/0 → Internet
VFP->>VFP: No explicit outbound method found
VFP->>VFP: Subnet is private<br/>(defaultOutboundAccess = false)
Note over VFP: 🚫 DROP<br/>No ephemeral IP assigned.<br/>Packet silently dropped.<br/><br/>Windows Update fails.<br/>Defender can't phone home.<br/>KMS activation fails.
VFP--xVM: No connectivity
No safety net. No outbound. Period.
The Azure Storage Same-Region Exception
There is one documented exception worth knowing about: VMs in a private subnet can still reach Azure Storage accounts in the same region without explicit outbound 4. This is a platform-level behavior. Microsoft recommends using NSGs to control it. If your security posture requires all outbound through a firewall, you need to be aware that this path exists.
What Changes on March 31, 2026
Let’s be precise 4:
What Changes
- New VNets created using API versions released after March 31, 2026 will have
defaultOutboundAccess = falseon their subnets by default. - This applies to all configuration methods: Azure Portal, ARM templates, Bicep, PowerShell, CLI.
What Does NOT Change
- Existing VNets — completely unaffected. Both existing VMs and new VMs in these VNets continue to get default outbound IPs, unless the subnets are manually modified to become private 4.
- Subnets with explicit outbound (NAT Gateway, Public IP, Load Balancer outbound rules, UDR to firewall) — unaffected.
- You can still opt out — set
defaultOutboundAccess = trueexplicitly if you need the old behavior.
The API Version Detail
If you use Bicep or ARM templates and pin an older API version, the old behavior continues. The official FAQ confirms this includes Terraform: “Earlier versions of ARM templates (or tools like Terraform that can specify older versions) will continue to set defaultOutboundAccess as null, which implicitly allows outbound access” 4. The AzureRM Terraform provider pins API versions internally per resource type — which version you get depends on the provider release. Newer releases will adopt the post-March 2026 API and the new default. If you need explicit control, use the AzAPI provider.
In Bicep, I’d recommend being explicit regardless:
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2024-05-01' = {
properties: {
addressPrefix: '10.0.1.0/24'
defaultOutboundAccess: false // Be explicit. Use NAT Gateway or UDR.
}
}
The Key Sentence Everyone Should Know
Buried in the Microsoft Learn documentation for default outbound access 4, there’s one critical sentence that should be the headline of every discussion about this change:
“Private subnets aren’t applicable to delegated or managed subnets used for hosting PaaS services. In these scenarios, outbound connectivity is managed by the individual service.”
If your subnet is delegated to a PaaS service, the defaultOutboundAccess property does not apply. The PaaS service manages its own connectivity. This eliminates the concern for most PaaS-heavy organizations.
But — and this is important — “managed by the individual service” doesn’t mean the same thing for every service. Some services truly own their egress. Others expect you to provide it. And one major service doesn’t even support private subnets at all. Let me break this down.
PaaS Services and Delegated Subnets — How They Actually Work
Pattern A: Fully Managed by Microsoft (No Customer VNet)
Services like Azure SQL Database, Azure Storage, Cosmos DB, Key Vault — they run entirely in Microsoft’s infrastructure. You connect to them via public endpoints, private endpoints, or service endpoints. You don’t see a VNet. There’s nothing for you to configure outbound on.
Impact: None. Not even close.
Pattern B: Delegated Subnet (Microsoft Deploys Into Your VNet)
These services require a delegated subnet in your VNet 5. You create the subnet, delegate it to the service, and Microsoft deploys its instances into it.
flowchart TD
subgraph vnet["Your VNet: 10.0.0.0/16"]
subgraph delegated["snet-sqlmi · 10.0.1.0/24<br/>Delegation: Microsoft.Sql/managedInstances"]
sqlmi["SQL MI Instance<br/>(Microsoft-managed NICs)"]
end
subgraph workload["snet-workload · 10.0.2.0/24<br/>No delegation — your VMs"]
vm["Your VMs"]
end
end
delegated -. "Delegated subnet:<br/>exempt from private<br/>subnet setting [4]" .-> note1["But check per-service<br/>support carefully!"]
workload -. "Your subnet:<br/>IS affected<br/>in new VNets" .-> note2["Needs explicit outbound"]
style delegated fill:#d4edda,stroke:#28a745
style workload fill:#fff3cd,stroke:#856404
style note1 fill:#fff3cd,stroke:#856404
style note2 fill:#f8d7da,stroke:#dc3545
style vnet fill:#f8f9fa,stroke:#333
The important thing: each service handles this differently. The exemption from the private subnet setting is documented, but what “managed by the individual service” means varies:
- Azure SQL Managed Instance uses Network Intent Policies — automatically injected NSG rules and routes 6. But here’s the critical part: SQL MI does not support private subnets. The docs state deploying SQL MI in a private subnet (where default outbound access is disabled) is currently not supported. SQL MI still relies on default outbound access for management traffic, and NAT Gateway isn’t supported on SQL MI subnets. If you’re running SQL MI, this is the service you need to watch most closely as the deadline approaches. Monitor the docs for updates.
- Azure NetApp Files has no internet egress at all — it operates entirely within the VNet over NFS/SMB using private network interfaces. Not affected, and there’s nothing to configure.
- Azure DB for PostgreSQL Flexible Server doesn’t use Network Intent Policies. The service requires access to Azure Storage for WAL file archival — the recommended approach is adding a
Microsoft.Storageservice endpoint to the delegated subnet 7. - Azure Databricks — customer owns egress on the data plane. Use Secure Cluster Connectivity.
Pattern C: VNet Integration (Outbound-Only Delegation)
App Service and Azure Functions (Premium/Dedicated) use this model 8. They delegate a subnet purely so that the service’s outbound traffic can originate from your VNet.
flowchart TD
subgraph vnet["Your VNet"]
subgraph intsubnet["snet-app-int<br/>Delegation: Microsoft.Web/serverFarms"]
vnics["App Service mounts vNICs here<br/>for OUTBOUND traffic"]
end
end
subgraph appservice["App Service (Microsoft-managed)"]
app["Your Web App"]
end
app -- "Outbound calls route<br/>through this subnet" --> vnics
vnics -- "Follows your UDR<br/>and NAT Gateway" --> internet(("Internet /<br/>other services"))
style intsubnet fill:#e2e3e5,stroke:#383d41
style appservice fill:#cce5ff,stroke:#004085
style vnet fill:#f8f9fa,stroke:#333
One thing I got wrong initially and want to be clear about: by default, App Service with VNet integration does not route all outbound traffic through your VNet. Internet-bound traffic still exits through App Service’s own platform IPs unless you explicitly set outboundVnetRouting.allTraffic = true 8. This catches people off guard — you think you’re controlling egress because you configured VNet integration, but only RFC 1918 traffic goes through the VNet by default.
Service Endpoints and Private Endpoints
Quick clarification since these come up in every conversation:
- Service endpoints modify the routing path at the platform level so traffic to Azure services stays on the backbone. They use the
VirtualNetworkServiceEndpointnext-hop type, which is immune to UDR overrides 3. No delegation, no injection, no impact from the default outbound change. - Private endpoints place a NIC in your subnet with a private IP mapped to a PaaS resource. Traffic is VNet-internal. Not affected by the default outbound change because the traffic never goes outbound.
Service-by-Service Impact Table
The “Egress Owner” column is the one that matters — if Microsoft owns egress, the retirement doesn’t affect that service.
No Customer VNet Involvement
| Service | Egress Owner | Impact |
|---|---|---|
| Azure SQL Database | Microsoft | None |
| Azure Storage | Microsoft | None |
| Cosmos DB, Key Vault, Event Hubs, Service Bus | Microsoft | None |
| Azure Monitor / Log Analytics | Microsoft | None |
| Azure Functions (Consumption) | Microsoft | None |
Delegated Subnet / VNet Injection
| Service | Delegation | Egress Owner | Impact |
|---|---|---|---|
| Azure SQL Managed Instance | Microsoft.Sql/managedInstances |
Microsoft (Network Intent Policy) | ⚠️ HIGH RISK — private subnets not supported; still relies on default outbound 6 |
| Azure Databricks | Microsoft.Databricks/workspaces |
Customer (data plane) | Low — use Secure Cluster Connectivity |
| Azure NetApp Files | Microsoft.NetApp/volumes |
N/A — no internet egress | None |
| Azure DB for PostgreSQL Flex | Microsoft.DBforPostgreSQL/flexibleServers |
Partially Microsoft | Low — needs Storage service endpoint 7 |
| Azure DB for MySQL Flex | Microsoft.DBforMySQL/flexibleServers |
Partially Microsoft | Low — similar to PostgreSQL |
| Azure Container Instances | Microsoft.ContainerInstance/containerGroups |
Customer | Low — use NAT Gateway |
| Azure Spring Apps | Microsoft.AppPlatform/Spring |
Customer | Low — use NAT Gateway or UDR |
| Power Platform | Microsoft.PowerPlatform/enterprisePolicies |
Customer | Low — attach NAT Gateway |
VNet Integration (Outbound Delegation)
| Service | Delegation | Egress Owner | Impact |
|---|---|---|---|
| App Service (VNet Integration) | Microsoft.Web/serverFarms |
Customer | Low — provide NAT GW or UDR; remember to enable allTraffic 8 |
| Azure Functions (Premium/Dedicated) | Microsoft.Web/serverFarms |
Customer | Low — same as App Service |
| Azure Machine Learning (managed VNet) | N/A (managed) | Microsoft | None |
Compute (Directly Affected)
| Service | Egress Owner | Impact |
|---|---|---|
| Virtual Machines | Customer | YES — must provide explicit outbound in new VNets 4 |
| Virtual Machine Scale Sets | Customer | YES — Flexible VMSS already defaults to private 4 |
| Azure Kubernetes Service | Customer (but AKS configures it) | YES — see next section |
| Azure Batch | Customer | YES — use NAT Gateway |
| Azure Virtual Desktop | Customer | YES — use NAT Gateway or Azure Firewall |
| Windows 365 (ANC) | Customer | YES — see dedicated section below |
| Windows 365 (Microsoft-hosted) | Microsoft | None — but see the trade-offs |
AKS — The One That Catches People Off Guard
I’ll be upfront: AKS is not my deepest area of expertise. My day-to-day is more on the network security and platform architecture side. But I’ve gone through the docs carefully and spoken with colleagues who live in the Kubernetes world, so here’s what I can tell you with confidence.
AKS sits in a gray area — it’s a PaaS service, but it deploys VMs (nodes) into a VNet. That’s why it’s affected by this change at all.
What Changes for AKS
Starting March 31, 2026, AKS clusters using the AKS-managed VNet option will place cluster subnets into private subnets by default 9. But AKS has always required an explicit outbound type. Every cluster gets one of these:
| Outbound Type | What It Does |
|---|---|
loadBalancer (default) |
AKS creates a Standard Load Balancer with outbound rules and a public IP |
managedNATGateway |
AKS provisions a NAT Gateway on the cluster subnet 10 |
userAssignedNATGateway |
You provide a NAT Gateway (BYO VNet only) 10 |
userDefinedRouting |
You provide a UDR pointing to your firewall/NVA (BYO VNet only) |
none |
No egress infrastructure — for network-isolated clusters only 9 |
block (Preview) |
Actively blocks all egress via NSG rules — for fully isolated clusters 9 |
Because AKS always configures one of these, the docs state: “This setting doesn’t impact AKS-managed cluster traffic, which uses explicitly configured outbound paths” 9. Clusters using BYO VNets are unaffected entirely.
Where It Could Bite You
The risk is in unsupported scenarios: if you deploy non-AKS resources (test VMs, sidecar workloads) into the AKS-managed subnet, those resources relied on default outbound and will lose internet access.
flowchart TD
start{"AKS VNet mode?"} -->|AKS-managed VNet| managed["AKS creates VNet"]
start -->|BYO VNet| byo["You provide VNet + subnet"]
managed --> out1{"Outbound type?"}
byo --> out2{"Outbound type?"}
out1 -->|loadBalancer / managedNATGateway| ok1["Explicit outbound ✅"]
out2 -->|userAssignedNATGateway / userDefinedRouting / loadBalancer| ok2["Explicit outbound ✅"]
ok1 --> safe["✅ Supported config: NOT impacted"]
ok2 --> safe
safe --> warning["⚠️ Do NOT deploy non-AKS resources<br/>into AKS-managed subnets"]
style safe fill:#d4edda,stroke:#28a745
style warning fill:#fff3cd,stroke:#856404
My recommendation (take it for what it’s worth from a non-AKS-specialist): for production, use BYO VNet with userDefinedRouting through your firewall. For dev/test, managedNATGateway is simple and cheap. And regardless — don’t put non-AKS resources in AKS subnets.
Windows 365 — Two Models, Very Different Security Postures
Windows 365 has two networking models 11, and they couldn’t be more different in how they relate to this change — and to your security architecture.
Microsoft-Hosted Network
With Microsoft-hosted network, your Cloud PC’s NIC sits in a Microsoft-managed VNet that you never see or touch. Microsoft handles all egress. You don’t configure a VNet, subnet, route table, or firewall. The retirement of default outbound access has zero impact on this model.
Azure Network Connection (ANC)
With ANC, the Cloud PC’s NIC is injected into your VNet and subnet. You own the routing, NSGs, firewall rules — everything. If you create a new VNet after March 31, 2026, and don’t configure explicit outbound, Cloud PC provisioning will fail because the ANC health checks can’t reach the required endpoints.
Microsoft published guidance for Windows 365 ANC customers specifically about this change in February 2026 12 — they recommend NAT Gateway as the simplest fix.
flowchart TD
w365{"Windows 365<br/>network model?"} -->|Microsoft-hosted<br/>network| mhn["Microsoft manages everything<br/>No customer VNet"]
w365 -->|Azure Network<br/>Connection| anc["Cloud PC NIC in<br/>YOUR VNet"]
mhn --> mhn_impact["✅ Not affected<br/>by retirement"]
anc --> anc_q{"New VNet<br/>after March 31?"}
anc_q -->|Yes| anc_new["❌ Needs explicit outbound<br/>(NAT GW, Firewall, etc.)<br/>or provisioning fails"]
anc_q -->|No| anc_existing["✅ Existing VNet<br/>continues working"]
mhn_impact --> tradeoff["⚠️ But: no network-level<br/>visibility or control"]
style mhn_impact fill:#d4edda,stroke:#28a745
style anc_new fill:#f8d7da,stroke:#dc3545
style anc_existing fill:#d4edda,stroke:#28a745
style tradeoff fill:#fff3cd,stroke:#856404
My Take: ANC Is the Right Choice for Security-Conscious Organizations
Microsoft positions the Microsoft-hosted network as the recommended default, and I understand why — it’s simpler. But from a Zero Trust perspective, I’d push back on that recommendation for any organization that takes network security seriously.
With Microsoft-hosted network, your only visibility into Cloud PC traffic is at the endpoint level — Microsoft Defender for Endpoint, Defender for Cloud Apps, Windows Firewall. That’s not nothing, but it means you have no VNet flow logs (or still using NSG flow logs), no Azure Firewall logs, no network-level DLP, no way to inspect or control egress at the network layer. You’re trusting Microsoft’s managed infrastructure to handle traffic you can’t see.
With ANC, your Cloud PCs are on your VNet. Their traffic flows through your Azure Firewall (or whatever NVA you run). You get full logging, full inspection, and full control — the same way you’d handle any other workload in your environment. Yes, it’s more work. Yes, you need to manage the networking. But you also get to actually see what your Cloud PCs are doing on the network, which in my experience matters a lot when something goes wrong or when compliance asks for evidence.
The trade-off in a table:
| Capability | Microsoft-Hosted Network | ANC (Your VNet) |
|---|---|---|
| Network-level traffic inspection | ❌ No | ✅ Yes (Azure Firewall, NVA) |
| VNet flow logs | ❌ No | ✅ Yes |
| Azure Firewall / NVA logs | ❌ No | ✅ Yes |
| Network-level DLP | ❌ No | ✅ Yes |
| Endpoint-level visibility (MDE) | ✅ Yes | ✅ Yes |
| On-prem access via ExpressRoute/VPN | ❌ Requires VPN on Cloud PC | ✅ Native via VNet |
| Hybrid Entra join | ❌ Not supported | ✅ Supported |
| Operational complexity | Low | High |
| Affected by this retirement | No | Yes (new VNets only) |
If you’re already running a hub-and-spoke with Azure Firewall, adding Windows 365 Cloud PCs via ANC is just another spoke. The pattern is the same. And if your organization requires that all outbound traffic is inspected and logged — and most regulated industries do — then Microsoft-hosted network simply doesn’t meet the bar.
Hub-and-Spoke with Azure Firewall — You’re Already Fine
If you’re running hub-and-spoke with a UDR sending 0.0.0.0/0 to Azure Firewall, you already have explicit outbound 3. The retirement doesn’t affect you.
flowchart LR
subgraph spoke["Spoke VNet"]
subgraph sub["snet-workload<br/>UDR: 0.0.0.0/0 → 10.100.0.4"]
vm["VM: 10.0.1.4"]
end
end
subgraph hub["Hub VNet"]
fw["Azure Firewall<br/>10.100.0.4<br/>SNAT → Public IP"]
end
vm -- "UDR match: 0.0.0.0/0<br/>→ Virtual Appliance<br/>(explicit outbound ✅)" --> fw
fw --> internet(("Internet"))
style spoke fill:#f8f9fa,stroke:#333
style hub fill:#e8f4fd,stroke:#0078d4
style sub fill:#fff,stroke:#333
Still, make subnets private as defense-in-depth. If someone accidentally removes the route table, a non-private subnet falls back to default outbound — uncontrolled internet access through a random Microsoft IP. Private subnet = fail-closed.
To clear the Azure Advisor alert, you need to make the subnet private AND stop/deallocate the VMs 4. Even with a UDR, Azure may still assign the default outbound IP in non-private subnets — it just won’t be used unless your explicit method is removed.
The UDR “Internet” Next-Hop Trap
This one has caught people I know personally. Some organizations use UDR service tag routes with next-hop type Internet to bypass the firewall for specific Azure services:
flowchart TD
vm["VM in spoke subnet"] --> rt{"Route table lookup"}
rt -->|"AzureMonitor / Storage tag<br/>→ Internet"| bypass["Bypass firewall<br/>→ Direct to Azure"]
rt -->|"0.0.0.0/0<br/>→ Virtual Appliance"| fw["Azure Firewall"]
bypass --> problem{"Subnet is private?"}
problem -->|"Yes"| drop["🚫 DROPPED<br/>'Internet' next-hop<br/>has no path"]
problem -->|"No (legacy)"| works["✅ Works via<br/>default outbound"]
style drop fill:#f8d7da,stroke:#dc3545
style works fill:#d4edda,stroke:#28a745
In a private subnet, the Internet next-hop type breaks 4. No default outbound means no path for that traffic.
The docs are explicit that this doesn’t apply to Service Endpoints, which use a completely different next-hop type (VirtualNetworkServiceEndpoint) 4. Service endpoints continue to work in private subnets — that’s the proper replacement.
| Before (Non-Private Subnet) | After (Private Subnet) |
|---|---|
UDR: AzureMonitor → Internet |
Use Service Endpoints or Private Endpoints |
UDR: Storage → Internet |
Use Service Endpoints or Private Endpoints |
UDR: AzureActiveDirectory → Internet |
Route through the firewall, or use Service Endpoints |
If you must keep the Internet next-hop type, attach a NAT Gateway to the subnet — it provides the explicit outbound that makes it work again.
Practical Recommendations
For Existing Environments
-
Run Azure Advisor. Look for “Add explicit outbound method to disable default outbound” under Operational Excellence.
-
Audit your subnets:
Get-AzVirtualNetwork | ForEach-Object { $vnet = $_ $_.Subnets | Where-Object { $_.DefaultOutboundAccess -ne $false -and -not $_.Delegations } | Select-Object @{N='VNet';E={$vnet.Name}}, Name, @{N='DefaultOutbound';E={ if ($null -eq $_.DefaultOutboundAccess) {'null (implicit allow)'} else {$_.DefaultOutboundAccess} }} } -
Make subnets private proactively — even with a UDR to Azure Firewall. Defense-in-depth. Remember to stop/deallocate VMs for the change to take effect on their NICs 4.
-
Check for UDR
Internetnext-hop bypasses. If you use service tag bypass routes, migrate to Service Endpoints or Private Endpoints before making subnets private. -
If you run Azure SQL Managed Instance — pay close attention. SQL MI doesn’t support private subnets today 6. Monitor the docs for updates.
For New Deployments
-
Be explicit in your IaC. Don’t rely on defaults:
resource vnet 'Microsoft.Network/virtualNetworks@2024-05-01' = { name: 'vnet-spoke-001' location: location properties: { addressSpace: { addressPrefixes: ['10.0.0.0/16'] } subnets: [ { name: 'snet-workload' properties: { addressPrefix: '10.0.1.0/24' defaultOutboundAccess: false routeTable: { id: routeTable.id } networkSecurityGroup: { id: nsg.id } } } ] } } -
NAT Gateway is the simplest option for workloads that just need outbound internet. ~$32/month plus data processing.
-
Watch for Load Balancer backend pools configured by IP address. There’s a documented known issue: when configured by IP (not NIC), VMs still use default outbound access instead of the LB’s outbound rules 4. Associate a NAT Gateway as a workaround.
-
Don’t blanket-apply private subnets to delegated subnets. Check delegations first:
Get-AzVirtualNetwork | ForEach-Object { $_.Subnets | Where-Object { $_.Delegations.Count -gt 0 } | Select-Object Name, @{N='Delegation';E={$_.Delegations[0].ServiceName}} } -
Watch your Terraform provider version. Newer AzureRM releases will use post-March 2026 API versions and default to private subnets. Set
default_outbound_access_enabledexplicitly. For full API version control, use the AzAPI provider 4.
Summary
This retirement is a meaningful security improvement — it pushes Azure toward Zero Trust for network egress. But it’s narrower than the announcements suggest:
- Existing VNets: Not affected.
- PaaS services with delegated subnets: Generally not affected — Microsoft manages their egress. Except SQL MI, which doesn’t support private subnets yet.
- Hub-and-spoke with Azure Firewall: Not affected — you already have explicit outbound.
- AKS in supported configurations: Not affected — AKS always configures explicit outbound.
- New VMs in new VNets without explicit outbound: Affected. This is the target audience.
The organizations most at risk are those spinning up new VNets for dev/test, PoC, or lab environments where VMs are deployed quickly without proper networking. If you have established patterns — hub-and-spoke, NAT Gateway, load balancers — you’re already compliant.
References
Ref 1
1 D. Firestone et al., “VFP: A Virtual Switch Platform for Host SDN in the Public Cloud,” NSDI ‘17, USENIX, 2017. USENIX paper Back to text
Ref 2
2 Microsoft Research, “Azure Virtual Filtering Platform (VFP).” Microsoft Research article Back to text
Ref 3
3 Microsoft Learn, “Azure virtual network traffic routing.” Microsoft Learn article Back to text
Ref 4
4 Microsoft Learn, “Default outbound access in Azure.” Microsoft Learn article Back to text
Ref 5
5 Microsoft Learn, “Subnet delegation overview.” Microsoft Learn article Back to text
Ref 6
6 Microsoft Learn, “Service-aided subnet configuration — Azure SQL Managed Instance.” Microsoft Learn article Back to text
Ref 7
7 Microsoft Learn, “Networking with Private Access — Azure Database for PostgreSQL Flexible Server.” Microsoft Learn article Back to text
Ref 8
8 Microsoft Learn, “Integrate your app with an Azure virtual network — Azure App Service.” Microsoft Learn article Back to text
Ref 9
9 Microsoft Learn, “Customize cluster egress with outbound types in AKS.” Microsoft Learn article Back to text
Ref 10
10 Microsoft Learn, “Create a managed or user-assigned NAT gateway for your AKS cluster.” Microsoft Learn article Back to text
Ref 11
11 Microsoft Learn, “Deployment options for Windows 365.” Microsoft Learn article Back to text
Ref 12
12 Microsoft Tech Community, “Azure Default Outbound Access Changes: Guidance for Windows 365 ANC Customers.” Tech Community post Back to text
If you find errors or services that behave differently than described here, I’d love to hear about it. This is a living document and I’ll update it as Microsoft updates their docs.