Secure Your VNets with Private Subnets

A couple of weeks ago we talked about how Microsoft is deprecating default internet access for VMs](https://samcogan.com/retiring-default-internet-access-for-vms/) in September 2025. This change is being done because it doesn’t meet Microsoft’s “secure by design” approach. However, you don’t need to wait till 2025 if you want to get ahead of the game and secure your outbound traffic due to the introduction of private subnets.

By declaring your subnet as private you disable default outbound access and out of the box any VMs in that subnet will not have access to the internet. If you do want these VMs to have access you need to explicitly define an egress method as described in my previous post, using a NAT Gateway, Firewall, load balancer or Public IP. By taking this approach you ensure that VMs only get outbound internet access when you want it, and not because you took the default options or you forgot to enable some routing. It also ensures that any outbound routing is via IP addresses you own and not the random IP Microsoft assigns.

Private Subnets are currently in preview and should not be used for production workloads yet

Enabling a Private Subnet

There are a few things to be aware of when looking to use Private Subnets:

  • Private subnets can only be enabled at subnet creation time
  • Making a subnet private is a one-way change, it cannot be changed later

Portal

When creating a subnet in the portal, tick the box in the Private Subnet section to make the subnet private.

Portal UI

CLI

az network vnet subnet create --name "subnet1" --default-outbound false

Bicep

The docs for Private Subnet state that defaultOutboundAccess is the property to set, however, the Bicep specs haven’t yet been updated with a new API version to support this, so it may not work. I will update this doc when the MS docs have been updated.

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-05-01' = {
  name: virtualNetworkName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.0.0.0/16'
      ]
    }
    subnets: [
      {
        name: subnetName
        properties: {
          defaultOutboundAccess: false
          addressPrefix: '10.0.2.0/24'
        }
      }
    ]
  }
}

Pulumi

Pulumi hasn’t yet been updated due to the new ARM API version not being released yet.

Terraform

Terraform hasn’t yet been updated due to the new ARM API version not being released yet.