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:

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:

-
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
-
Terraform Registry Link: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/nat_gateway
-
Microsoft Docs: https://learn.microsoft.com/en-us/azure/nat-gateway/nat-overview
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:
Standard Load Balancer
-
Terraform Registry Link: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/lb
-
Microsoft Docs: https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-overview
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
-
Terraform Registry Link: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/public_ip
-
Microsoft Docs: https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/public-ip-addresses
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 Firewall
-
Terraform Registry Link: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/firewall
-
Microsoft Docs: https://learn.microsoft.com/en-us/azure/firewall/overview
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:
-
Deploying and Configuring Azure Firewall using Terraform
-
Deploying Azure Virtual WAN using Terraform
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:
- https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/firewall_policy
- https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/firewall_policy_rule_collection_group
- https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/route_table
I hope this post has been useful – please do feel free to reach out or connect with me if you need any further assistance!