Business Challenge
Deploy a virtual machine with no outbound configuration and it reaches the internet anyway. That has been true since the beginning and it is not an accident:
“When you deploy a virtual machine (VM) in a virtual network without an explicitly defined outbound connectivity method, Azure automatically assigns it an outbound public IP address… This access is referred to as default outbound access.”
That behaviour is being retired, and the word “retired” is doing a lot of damage. It suggests a cutoff: a date after which something stops working. That is not what happens. What happens is a default value changes, in one specific circumstance:
“For the API version released after March 31, 2026, subnets in new virtual networks have defaultOutboundAccess set to false by default.”
“Existing virtual networks aren’t changed. Both existing virtual machines and new virtual machines created in those networks can continue to receive default outbound IP addresses unless the subnets are explicitly changed to private.”
So it is scoped three ways at once: new virtual networks only, new API versions only, and nothing existing is touched. The documentation states the negative explicitly, which is unusual and worth reading: “This change isn’t a blanket shutdown of default outbound access for existing virtual networks.”
| Situation | Outbound behaviour |
|---|---|
| New VNet, API version after 31 March 2026 | Private — explicit outbound required |
| New VNet via the Azure portal | Private already — “The Azure portal already defaults to private subnets” |
| New VNet, earlier API version | Default outbound still granted |
| New VM in an existing VNet | Default outbound still granted |
| Any subnet you explicitly set nonprivate | Default outbound still granted |
Read that table as an operations problem rather than a migration one. The portal has already moved. A pipeline has not, if it pins an API version — and the documentation names the tool by name: “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.”
A VNet created by hand this morning and a VNet created by your module this afternoon can therefore differ in whether their VMs can reach the internet, with nothing in either deployment reporting a difference. That divergence is the thing to plan around, and it is already live.
Architecture
An implicit behaviour, four explicit replacements, a subnet flag, and a short list of things that stop working when you set it.
What you are actually losing
The default outbound IP is worth describing precisely, because its properties are the argument against it. “This IP, known as the default outbound access IP, is owned by Microsoft and can change without notice. Additionally, default outbound connectivity relies on an implicit platform behavior and may be affected by platform‑level changes.”
Microsoft gives three reasons for disabling it, and they are not equally weighted in practice: “Security: Default internet access contradicts Zero Trust principles. Clarity: Explicit connectivity is preferred over implicit access. Stability: The default outbound IP isn’t customer-owned and can change.”
Stability is the one that bites first, and the specifics are concrete rather than theoretical:
- “Multiple NICs on a VM can yield inconsistent outbound IPs”
- “Scaling Azure Virtual Machine Scale Sets can result in changing outbound IPs”
- “Outbound IPs aren’t consistent or contiguous across Virtual Machine Scale Set instances”
- “Default outbound access IPs don’t support fragmented packets”
- “Default outbound access IPs don’t support ICMP pings”
Those last two are the ones that produce bewildering incidents. A VM that cannot ping anything and whose large UDP payloads vanish is not broken and its NSG is not wrong — it is on default outbound access, which never supported either.
The four explicit methods
| Method | Note |
|---|---|
| NAT gateway on the subnet | “this is the recommended method for most scenarios” |
| Standard load balancer with outbound rules | Requires the VM in the backend pool |
| Standard public IP on the NIC | Per-machine, and per-machine cost |
| Firewall or NVA via a UDR | The hub-and-spoke pattern from #62 |
One shape is already compliant without anyone doing anything: “Flexible scale sets are secure by default. Any instances created via Flexible scale sets don’t have the default outbound access IP associated with them, so an explicit outbound method is required.” If you have been running Flexible orchestration, you have already met this requirement — or already hit it.
Finding out where you stand
There is a per-interface flag and a report, which together make this auditable rather than guesswork. “There’s a NIC-level parameter (defaultOutboundConnectivityEnabled) which tracks if default outbound IP is allocated to a VM/Virtual Machine Scale Set instance. This is used to generate an Azure portal banner…”
And in Advisor, under “Operational Excellence”, two separate recommendations — “Add explicit outbound method to disable default outbound and/or Add explicit outbound method to disable default outbound for Virtual Machine Scale Sets (note these are two different items)”. Checking one and declaring the estate clean is a documented mistake.
One confusing signal to expect: “In some cases, a default outbound IP is still assigned to virtual machines in a nonprivate subnet, even when an explicit outbound method… is configured. This doesn’t mean the default outbound IPs are used for egress unless those explicit methods are removed.” The banner can therefore be correct and alarming at the same time. Clearing it requires making the subnet private and stopping the machines.
Why This Architecture Holds Up
Because the divergence is already in your estate
This is the part to act on. The portal defaults to private; a pinned API version does not. Those are not future states, they are current ones. So the question is not “when do we migrate” but “do our deployment paths agree today” — and the honest answer for most organisations with both a portal and a module is no.
What makes it hard to notice is that neither outcome is an error. A private subnet is a valid subnet; a nonprivate one is too, and remains supported deliberately: “You can still configure subnets as nonprivate using any supported method… This ensures compatibility for infrastructures that rely on default outbound IPs and aren’t yet ready to transition.” Both paths succeed. Only the workload tells you which one you got, and only when it tries to reach something.
The remedy is boring and effective: set defaultOutboundAccess explicitly in your templates, to whichever value you intend, rather than letting an API version decide. An explicit false is a decision; an explicit true is also a decision; null is a deferral to a default that has now moved.
Because the UDR pattern that breaks is the one everybody uses
The sharpest technical consequence has nothing to do with new deployments. It is what happens when you make an existing subnet private, and it is specific: “In configurations using User Defined Routes (UDRs), any configured routes with next hop type Internet break in a Private subnet.”
That matters because of which pattern uses it. The documentation describes it exactly: “A common example is the use of a UDR to steer traffic to an upstream network virtual appliance/firewall, with exceptions for certain Azure Service Tags to bypass inspection. This is done by configuring routes to these Service Tags with next hop type Internet… attempts to connect to these destinations fail, because default outbound access isn’t available by default in a Private subnet.”
So the firewall-with-exceptions design — everything through the NVA except a few named Azure services — depends on default outbound access for exactly those exceptions, without saying so anywhere. Make the subnet private and the bypass routes stop working while the default route through the firewall keeps working, which is a failure mode that looks like “some Azure services broke” rather than “we changed outbound access”.
Service endpoints are explicitly not affected — “This limitation doesn’t apply to the use of Service Endpoints, which use a different next hop type VirtualNetworkServiceEndpoint” — which is a real argument for preferring them over tag-based bypass routes, and ties back to #63. They also cannot be overridden: “Routes with a virtual network service endpoint as the next hop type can’t be overridden, even when you use a route table.”
Because the change needs an outage on machines that already exist
Flipping the flag is not enough, and this is the operational cost people miss when planning: “Existing virtual machines must be stopped and deallocated for the change of a subnet (either from non-private to private, or the reverse) to take effect on their network interfaces.”
Both directions. So the rollback is as disruptive as the change, which means this is not a setting to toggle during an incident to test a theory. It belongs in a maintenance window with the explicit outbound method already in place beforehand — which is exactly the documented sequence: “For an existing nonprivate subnet, plan and configure explicit outbound connectivity before you change the subnet to private.”
Because two exemptions mean an audit will not come out clean
Two things stay on default outbound access regardless of intent, and both are worth knowing before promising anyone a zero-default-outbound estate.
The first is a bug, stated as one: “When a load balancer backend pool is configured by IP address, it uses default outbound access due to an ongoing known issue.” The recommended mitigation is to attach a NAT gateway to the backend VMs anyway.
The second is by design and connects straight to #63: “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.” A delegated subnet is not yours to make private, which is consistent with everything #63 said about handing a subnet to a service.
Because the thing it replaces was a system route, and that route is still there
Worth being clear about what default outbound access is not. It is not the route. Every subnet gets a route to the internet whether or not it has default outbound access: “Whenever a virtual network is created, Azure automatically creates the following default system routes for each subnet”, including 0.0.0.0/0 with next hop type Internet.
The route says where traffic goes; default outbound access supplied the address it went out from. Making a subnet private removes the address, not the route — which is why the symptom is a connection that fails rather than a packet with nowhere to go, and why the route table still looks entirely normal afterwards.
The same page is also where the other half of the private-subnet story lives. Routing precedence is “User-defined route, BGP route, System route” with selection by “the longest prefix match algorithm”, and a route table holds “up to 400 UDRs… with the Azure Virtual Network Manager routing configuration, this number can expand to 1,000 UDRs per route table” — of which “25 or fewer routes with service tags”. That service-tag cap is the ceiling on the bypass pattern above, which is a second reason not to build on it.
Because “private” does not mean isolated, and one exception proves it
A private subnet is not an air gap, and the exception is specific enough to rely on: “Virtual machines are still able to access Azure Storage accounts in the same region in a private subnet without an explicit method of outbound. NSGs are recommended to control egress connectivity.”
Which is a useful fact and a slightly uncomfortable one. Same-region storage remains reachable from a subnet whose whole purpose is to have no default egress, and the recommended control is the NSG — back to #64. Making a subnet private is an outbound-address decision, not a data-exfiltration control, and conflating the two is how a private subnet ends up on a compliance slide it does not deserve.
The other thing a private subnet costs is mundane and will be noticed within a day: “Certain services don’t function on a virtual machine in a private subnet without an explicit method of egress (examples are Windows Activation and Windows Updates).”
Key Architecture Decisions
| Decision point | Choose | Because |
|---|---|---|
| Reading “retirement” as a cutoff date | Don’t — it is a default change | It isn't a blanket shutdown of existing networks. |
defaultOutboundAccess in your templates |
Set it explicitly, either way | null defers to a default that has moved. |
| Portal and pipeline producing different subnets | Assume they already do | The portal defaults private; pinned API versions don't. |
| Picking an explicit outbound method | NAT gateway on the subnet | Microsoft's recommended method for most scenarios. |
| Making an existing subnet private | Configure explicit outbound first | That is the documented transition order. |
| Scheduling the flag change | A maintenance window | VMs must be stopped and deallocated to take effect. |
| Planning a rollback | Expect the same outage | The reverse change also needs stop and deallocate. |
A UDR bypass to service tags via Internet |
Replace it before going private | Those routes break in a private subnet. |
| Bypassing a firewall for Azure services | Prefer service endpoints | Their next hop type is unaffected. |
| Relying on many tag-based routes | Don’t | 25 or fewer service-tag routes per route table. |
| A VM that cannot ping anything | Check for default outbound access | Those IPs don't support ICMP pings. |
| Large UDP payloads failing | Same check | Those IPs don't support fragmented packets. |
| Needing a stable egress address | Any explicit method | The default IP is Microsoft's and can change. |
| Auditing the estate | Advisor, both recommendations | VMs and scale sets are two separate items. |
| A banner on a VM that has a NAT gateway | Expected — not in use for egress | The address is assigned but unused. |
| Clearing that banner | Make the subnet private, then deallocate | Both steps are required. |
| A load balancer backend pool by IP | Attach a NAT gateway anyway | It uses default outbound due to a known issue. |
| Making a delegated subnet private | Not applicable | The service manages its own egress. |
| Flexible orchestration scale sets | Already require explicit outbound | They never get a default outbound IP. |
| Calling a private subnet isolated | Don’t | Same-region Storage is still reachable. |
| Windows VMs in a private subnet | Provide egress for activation and updates | Both are named as non-functional without it. |
Closing Thought
The interesting thing about this change is how little of it is a change. No date switches anything off. No existing virtual network is modified. New machines in old networks keep behaving exactly as they did. What actually happened is that a property which used to be left unset now gets set to false for new networks on new API versions, and the portal got there first.
Which converts a migration into a consistency problem, and consistency problems are worse because nothing fails loudly. A subnet created one way has internet egress and a subnet created another way does not, both are valid, both deploy successfully, and the difference surfaces later as an application that cannot reach a licence server. The fix is to stop leaving the property unset — not because false is right for every subnet, but because a deployment that does not state its intent inherits whatever the platform most recently decided.
And the detail I would carry into any plan to adopt private subnets is the UDR one, because it is the only item here that breaks something that currently works. Routing everything through a firewall while excepting a handful of Azure service tags with next hop Internet is an extremely common design, and those exception routes quietly depend on default outbound access. Make the subnet private and they stop. The symptom will look like several Azure services failing at once, nobody will connect it to a subnet property, and the route table will look perfectly correct throughout.
#69 takes the explicit methods seriously, starting with the simplest one: public IPs — Basic versus Standard, static versus dynamic, what the SKU decides that cannot be changed afterwards, and why the Basic SKU's own retirement makes this a shorter decision than it used to be.
Official Azure Reference
- Default outbound access in Azure — the API-version mechanism and its scope, the three reasons to disable it, the unsupported packet types, the private-subnet limitations including the UDR break, the stop-and-deallocate requirement, and the Advisor recommendations
- Azure virtual network traffic routing — the default system routes including the internet route, the next hop types, route precedence and longest prefix match, and the UDR and service-tag route limits
- What is Azure NAT Gateway? — the recommended explicit outbound method, covered properly in #70 (no claims drawn from it)
- Outbound rules for Azure Load Balancer — the second explicit method (no claims drawn from it)
Comments