Upcoming changes to outbound access for Azure VMs – Exploring the Terraform Options

As you may be aware, default outbound access for Azure VMs will change on 30th September 2025 – and the recommendation is to transition to an explicit outbound method. It’s worth noting that existing VMs will continue to work – however, there is still a recommendation to transition:

Default outbound access changes on the Azure Updates Page
Default outbound access changes on the Azure Updates Page

You can read more about this announcement here: https://azure.microsoft.com/en-us/updates/default-outbound-access-for-vms-in-azure-will-be-retired-transition-to-a-new-method-of-internet-access/

Default outbound access can be removed by transitioning to an explicit method of public connectivity – which is also detailed here: https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/default-outbound-access#how-can-i-transition-to-an-explicit-method-of-public-connectivity-and-disable-default-outbound-access


In short – you will need to transition to one of the following methods, with NAT Gateway being the recommended approach:

NAT gateway is the recommended approach
https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/default-outbound-access#if-i-need-outbound-access-what-is-the-recommended-way
  • A NAT Gateway
  • A Standard Load Balancer, with outbound rules.
  • Associate a Standard Public IP to a Virtual Machine NIC
  • Azure Firewall

Within this post I will explore the Azure Terraform Options for all the above, and provide links, samples, and reference material to help with the transition.

NAT Gateway

Creation of a NAT Gateway in Terraform is straightforward and can be done using the following block:

# NAT Gateway Resources
resource "azurerm_resource_group" "nat-rg" {
  name     = "nat-rg"
  location = "UK South"
}

resource "azurerm_public_ip" "nat-pip" {
  name                = "nat-pip"
  location            = azurerm_resource_group.nat-rg.location
  resource_group_name = azurerm_resource_group.nat-rg.name
  allocation_method   = "Static"
  sku                 = "Standard"
}

resource "azurerm_nat_gateway" "nat-gw" {
  name                = "nat-gw"
  location            = azurerm_resource_group.nat-rg.location
  resource_group_name = azurerm_resource_group.nat-rg.name
  sku_name            = "Standard"
}

resource "azurerm_nat_gateway_public_ip_association" "example" {
  nat_gateway_id       = azurerm_nat_gateway.nat-gw.id
  public_ip_address_id = azurerm_public_ip.nat-pip.id
}

Once the NAT Gateway is created – you can then associate this to a Subnet – this can be done in Terraform using the block below:

resource "azurerm_subnet_nat_gateway_association" "nat-assoc1" {
  subnet_id      = azurerm_subnet.example.id
  nat_gateway_id = azurerm_nat_gateway.nat-gw.id
}

If the NAT Gateway is not assigned, you will see a message as per the below screenshot – and you can also assign subnets within the Portal too:

Azure NAT Gateway showing how to configure Subnets


Standard Load Balancer

Creation of a Standard Load Balancer can be done using the Block below:

resource "azurerm_resource_group" "lb-rg" {
  name     = "lb-rg"
  location = "UK South"
}

resource "azurerm_public_ip" "lb-pip" {
  name                = "lb-pip"
  location            = azurerm_resource_group.lb-rg.location
  resource_group_name = azurerm_resource_group.lb-rg.name
  allocation_method   = "Static"
}

resource "azurerm_lb" "lb" {
  name                = "standard-lb"
  location            = azurerm_resource_group.lb-rg.location
  resource_group_name = azurerm_resource_group.lb-rg.name

  frontend_ip_configuration {
    name                 = "PublicIPAddress"
    public_ip_address_id = azurerm_public_ip.lb-pip.id
  }
}

Once the Load Balancer has been created – to achieve the desired outbound NAT we would need to use Outbound Rules. Note that for Secure by Default configuration and applications with demanding outbound requirements a NAT Gateway is recommended in addition to the Load Balancer. See here for further details: https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/default-outbound-access#how-can-i-transition-to-an-explicit-method-of-public-connectivity-and-disable-default-outbound-access

To create Outbound Rules using Terraform – the following AzureRM Resource can be used: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/lb_outbound_rule


Public IP Address

Creation of a Public IP using Terraform is very simple – the block below creates a Resource Group and a Standard SKU Public IP:

resource "azurerm_resource_group" "vm-pip" {
  name     = "pip-rg"
  location = "UK South"
}

resource "azurerm_public_ip" "vm-pip" {
  name                = "vm-pip"
  location            = azurerm_resource_group.lb-rg.location
  resource_group_name = azurerm_resource_group.lb-rg.name
  allocation_method   = "Static"
}

Once the Public IP is created, we’d then need to allocate this to a Network Interface – this can be done using the block below:

resource "azurerm_network_interface" "nic-pip" {
  name                = "example-nic"
  location            = azurerm_resource_group.lb-rg.location
  resource_group_name = azurerm_resource_group.lb-rg.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.subnet-pip.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id = azurerm_public_ip.pip.id
  }
}

Once this is done – your Network Interface will have a Public IP associated with it:

Azure Network Interface showing Public IP


Azure Firewall

Last, but by no means least is Azure Firewall. Compared to the other options above, Azure Firewall is more involved in terms of setup required – but allows centralisation of outbound access and brings additional control in the form of Firewall Policies. To create an Azure Firewall Resource, a Virtual Network, Subnet, and Public IP are required, however, other configuration (Virtual WAN for example) are also available.

I would recommend spending time considering your requirements and topology before deploying Azure Firewall – there are usually more factors involved than the other methods above!

If you are interested in reading more about Azure Firewall using Terraform – I have a written a couple of blog posts that may be of additional interest:

To get started, the following code block can be used to deploy Azure Firewall:

resource "azurerm_resource_group" "rg-fw" {
  name     = "rg-fw"
  location = "UK South"
}

resource "azurerm_virtual_network" "vnet-fw" {
  name                = "vnet-fw"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg-fw.location
  resource_group_name = azurerm_resource_group.rg-fw.name
}

resource "azurerm_subnet" "subnet-fw" {
  name                 = "AzureFirewallSubnet"
  resource_group_name = azurerm_resource_group.rg-fw.name
  virtual_network_name = azurerm_virtual_network.vnet-fw.name
  address_prefixes     = ["10.0.1.0/24"]
}

resource "azurerm_public_ip" "pip-fw" {
  name                = "pip-fw"
  location            = azurerm_resource_group.rg-fw.location
  resource_group_name = azurerm_resource_group.rg-fw.name
  allocation_method   = "Static"
  sku                 = "Standard"
}

resource "azurerm_firewall" "example" {
  name                = "fw1"
  location            = azurerm_resource_group.rg-fw.location
  resource_group_name = azurerm_resource_group.rg-fw.name
  sku_name            = "AZFW_VNet"
  sku_tier            = "Standard"

  ip_configuration {
    name                 = "configuration"
    subnet_id            = azurerm_subnet.subnet-fw.id
    public_ip_address_id = azurerm_public_ip.pip-fw.id
  }
}

Once the Firewall is deployed – you will also need to consider Route Tables, Firewall Policies, and Rule Collections. I’ve included links to the Registry items for those below:


I hope this post has been useful – please do feel free to reach out or connect with me if you need any further assistance!

Skip to content