Get prepared for default outbound access retirement

This post is part of the Azure Spring Clean event organised by Joe Carlyle and Thomas Thornton. I’m happy to be contributing to this annual event once again this year which is packed full with over 60 sessions this year!

My topic this time, is to discuss the retirement of default outbound access connectivity, why this is happening and why and how you should prepare for this.

Firstly, don’t let the fact that this is over 18 months away at the time of writing put you off taking action now. The retirement date of 30th September 2025 is an important deadline in Azure networking as it will also see the retirement of basic SKU public IP addresses and basic SKU Azure load balancers.

This is a very significant change from Microsoft that in some cases will require architectural re-designs and it is better to plan for these changes now before you potentially end up with a more complex environment in 18 months time as you continue to increase your Azure footprint over time.

What is happening and why?

In a nutshell, the answer is security. Up to now, whenever you created a new Azure virtual machine you could always access the Internet and any Azure services that use a public endpoint directly without an additional configuration. This works via something called default outbound access which means that if you don’t set up an explicit outbound connectivity method yourself then Microsoft will instead apply an implicit dynamic public IP address to your virtual machine. This was primarily for ease of use so that you could quickly adopt Azure services and connect to Internet accessible services without much effort…and that was great.

Modern day threats however have really forced a re-think on this approach and Microsoft and other leading Cloud vendors are adopting a secure-by-default approach which means that you will soon need to explicitly configure an outbound connectivity route if your virtual machine requires it for any new virtual machines.

Take note of that last part, new virtual machines. Yes, this does mean that existing virtual machines won’t be affected but a warning here, you should still start planning now for these existing virtual machines to adopt an explicit connectivity method.

For starters, it’s going to be more secure but importantly, there may well come a time where these virtual machine resources need to be recreated for some reason.

Some examples of this would be:

  • Restoring a virtual machine from backup
  • Moving your virtual machine to an availability zone
  • Replicating your virtual machine to another Azure region
  • Scale-out of new virtual machines in a VM scale set

The point is, at some point in time it’s very likely that you will need to implement explicit outbound connectivity even for existing virtual machines so it is better to plan for this now and implement it before the deadline.

One additional point here, this change is only affecting Azure virtual machines. PaaS services that use implicit outbound connectivity methods will still continue to work undeterred by this retirement.

What are my options for explicit outbound access?

If we look at the documentation on Microsoft Learn we can see some options defined for providing an explicit outbound connection. Essentially, the concept here is to use Source Network Address Translation (SNAT) to route Internet bound traffic through a public IP address that you own, manage and assign via one of the below options:

Assign a public IP address directly to a NIC on the virtual machine

Fine for very small environments if secured properly but very difficult to manage at scale so generally this is not a preferred option

Note: If you are using basic SKU public IP addresses, please note these are also retiring on 30th September 2025 so you will need to upgrade to the standard SKU before then

Route the outbound traffic through a public Azure load balancer

Generally an OK option but you need to look out for SNAT port exhaustion if you have a lot of concurrent outbound connections and there are some specific configuration requirements as per the notes below

Notes:

  • Virtual machines need to be associated as a NIC configuration in the backend pool of the Azure load balancer
  • You will also need to associate a load balancing rule if using the basic SKU or an outbound rule if using the standard SKU Azure load balancer
  • The basic SKU Azure load balancer is also retiring on 30th September 2025 so make sure to upgrade to the standard SKU if using this method currently

Route the outbound traffic through a NAT gateway

Microsoft’s recommended method and very easy to set up but this service doesn’t scale easily across availability zones currently if that’s a requirement for you

…or Route the outbound traffic through an Azure Firewall or network virtual appliance

This will be the most secure option as you are filtering all of the outbound traffic through a firewall. It’s a bit more complicated to set up as you need to create user-defined routes for your virtual machines and define a next hop for Internet traffic to the virtual appliance but it is the most secure (and costly) option overall

As you can see, there are multiple options available to you and it’s important to choose the correct option(s) for your own environments. It is best to plan ahead now and re-architect where required as the longer you wait, the more complex your environment is likely to become over time.

Identifying resources that are currently using default outbound access

Depending on the size of your environment and how well you know the existing architecture this might be a very straightforward task for you to perform manually. You will need to audit all of your existing Azure virtual machines and ensure that they all use one of the methods mentioned earlier for explicit outbound access.

If you are a consultant or you are working with larger environments then this could be quite an extensive task to perform manually however. It is likely that Microsoft will develop a process to help identify these affected Azure virtual machines and indeed in my research I did come across a property called defaultOutboundAccess in the Network Interface resource of the Resource Graph Explorer. Unfortunately, this had a value of null for each of my resources so it looks like Microsoft hasn’t started to populate this value just yet but one to keep an eye on.

For now, I have written a fairly comprehensive KQL query below that can help you to identify resources where default outbound access is currently in use.

The query will first fetch all virtual machine network interface (NIC) resources in the selected scope. It will then check if that NIC has a public IP address assignment, is part of a public Azure load balancer backend pool, is in a subnet assigned to a NAT gateway or is in a subnet that has a default route with a next hop type of virtual appliance.

Some additional notes for use:

  • If you have multiple NICs on a virtual machine then these will each be evaluated by the query individually. If using direct IP address assignments then only one NIC per virtual machine requires an IP address to avoid using default outbound access
  • The Azure load balancer check will only detect NIC assigned backend pool configurations, however IP address based configurations will still use default outbound access due to a known issue with this configuration type
  • For the NVA check, I am checking that a default route of 0.0.0.0/0 is in place on the NIC subnet and that it has a next hop type of “Virtual Appliance”. If you are using more specific Internet routes or if your Internet routing is misconfigured, please note that these scenarios are not checked by this query

Disclaimer: I do not assume any responsibility for the accuracy of the results obtained from using this query. This query is provided solely as a tool to assist in identifying instances where default outbound access is in use. Please use the information obtained from this query at your own discretion and risk.

// Selecting all network interfaces
resources
| where type == "microsoft.network/networkinterfaces"

// Expand each network interface's IP configurations
| mv-expand ipconfig = properties.ipConfigurations

// Expand each backend pool associated with the IP configurations
| mv-expand backendPool = ipconfig.properties.loadBalancerBackendAddressPools

// Extracting relevant information and filtering for network interfaces associated with virtual machines
| extend backendPoolId = tostring(backendPool.id)
| where isnotempty(properties.virtualMachine)
| extend vmIdArray = split(properties.virtualMachine, "/")
| extend vmName = tostring(vmIdArray[-1])
| extend virtualMachine = substring(vmName, 0, strlen(vmName) - 2)
| extend subnetArray = split(tostring(ipconfig.properties.subnet), "/")
| extend virtualNetwork = tostring(subnetArray[8])
| extend subnetName = tostring(ipconfig.properties.subnet)
| extend subnet = tostring(subnetArray[10])
| extend subnet = substring(subnet, 0, strlen(subnet) - 2)
| extend pipArray = split(tostring(ipconfig.properties.publicIPAddress.id), "/")
| extend publicIp = tostring(pipArray[8])
| extend publicIp = iff(isnotempty(publicIp), "Yes", "No")

// Projecting relevant columns for network interfaces
| project id, resourceGroup, nicName = name, virtualMachine, virtualNetwork, subnetName, subnet, publicIp, backendPoolId

// Left outer join with load balancers to check if a network interface is associated with a load balancer
| join kind=leftouter (
    resources
    | where type == "microsoft.network/loadbalancers"
    | mv-expand backendPools = properties.backendAddressPools
    | extend hasPublicIpConfig = iff(isnotempty(properties.frontendIPConfigurations), "Yes", "No")
    | where hasPublicIpConfig == "Yes"
    | project lbName = name, backendPoolId = tostring(backendPools.id)
) on backendPoolId
| extend loadBalancer = iff(isnotempty(lbName), "Yes", "No")

// Left outer join with NAT gateways to check if a network interface is associated with a NAT gateway
| join kind=leftouter (
    resources
    | where type == "microsoft.network/natgateways"
    | where array_length(properties['subnets']) > 0
    | mv-expand subnetName = properties.subnets
    | extend subnetName = tostring(subnetName)
    | project subnetName, natGwName = name
) on subnetName
| extend natGateway = iff(isnotempty(natGwName), "Yes", "No")

// Left outer join with route tables to check if a network interface has a default route to a virtual appliance
| join kind=leftouter (
    resources
    | where type == "microsoft.network/routetables"
    | mv-expand rules = properties.routes    
    | extend addressPrefix = tostring(rules.properties.addressPrefix)
    | extend nextHopType = tostring(rules.properties.nextHopType)
    | extend nextHopIpAddress = tostring(rules.properties.nextHopIpAddress)    
    | mv-expand subnetName = properties.subnets
    | extend subnetName = tostring(subnetName)
    | extend nextHopIpAddress = tostring(rules.properties.nextHopIpAddress)
    | where addressPrefix == "0.0.0.0/0"
    | where nextHopType == "VirtualAppliance"
    | project subnetName, nextHopIpAddress
) on subnetName
| extend defaultRouteisNVA = iff(isnotempty(nextHopIpAddress), "Yes", "No")

// Determining if a network interface has default outbound access
| extend defaultOutboundAccess = iff(publicIp == "No" and loadBalancer == "No" and natGateway == "No" and defaultRouteisNVA == "No", "Yes", "No")

// Final projection of columns and ordering by default outbound access status
| project id, resourceGroup, nicName, virtualMachine, virtualNetwork, subnet, publicIp, loadBalancer, natGateway, defaultRouteisNVA, defaultOutboundAccess
| order by defaultOutboundAccess

You can run this query directly in the Azure Resource Graph Explorer of the Azure portal and you should receive a table output similar to the below example.

Each row in the table represents a network interface (NIC) with associated properties such as NIC name, virtual machine, virtual network, subnet, presence of: public IP, load balancer, NAT gateway and default route to a virtual appliance (NVA). For each property, a “Yes” or “No” value indicates its presence or absence. The defaultOutboundAccess column provides an overall evaluation of the default outbound access status.

I have published the KQL code along with an Azure workbook to a repository on my GitHub account. For the workbook, you can just create a new Azure workbook and copy the JSON code into the advanced editor.

Futureproofing

Now that you have hopefully identified and prepared your environments for the service retirement, how do you futureproof and ensure that you don’t fall into the trap of deploying any new virtual machines that will use default outbound access in future?

Microsoft have released a new feature for this called private subnets (still in public preview at the time of writing). The concept is simple enough, any virtual machines within a private subnet will never be able to use default outbound access to connect to the Internet. You will have to use one of the explicit methods mentioned earlier if Internet access is required.

Note: For Windows virtual machines, it’s important to note that you will need to provide Internet access for Windows activations and Windows updates to function

At this time, you cannot convert an existing subnet to a private subnet but you can create a new private subnet on an existing virtual network and then move your virtual machine NIC to this private subnet. This is achieved quite easily by using PowerShell or CLI.

The below is a PowerShell example of how to create a new private subnet with an address space of 10.7.1.0/24 on an existing virtual network. Note the ‘DefaultOutboundAccess’ flag is set to ‘$false’.

$virtualNetwork = Get-AzVirtualNetwork -ResourceGroupName privatesubnettest -Name vnet-privatesubnettest

Add-AzVirtualNetworkSubnetConfig -Name "privateSubnet" -VirtualNetwork $virtualNetwork -AddressPrefix "10.7.1.0/24" -DefaultOutboundAccess $false 

$virtualNetwork | Set-AzVirtualNetwork

Conclusion

While all of this might seem like an inconvenience, it is being introduced in for the correct reasons and that is to ensure that you are in full control of managing secure outbound connections for your virtual machines. The deadline gives you plenty of time to prepare for this but to reiterate my point, this is not something to sit on until the last minute.

You should start planning for this as soon as possible, especially if you are not too familiar with Azure networking and implementing any of these mitigation options which will require a new Azure networking service deployment.

So start your planning today and as always please feel free to contact me if you have any questions about my post.

One thought on “Get prepared for default outbound access retirement

  1. Hello, I have been looking at your blog for a little while and I was wondering if you would do a post on how you became an MVP, it is something I’d like to do in my career.

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.