Navigating the End of Azure VM Direct Internet Access: Part 2 - Implementing Azure Firewall

Azure VM Internet - Azure Firewall

In Part 1 of this series, we explored how to implement a NAT Gateway to manage outbound internet connectivity for Azure VMs. Today, in Part 2, we’ll dive into configuring Azure Firewall, a robust security solution that provides both outbound and inbound network traffic control. Azure Firewall not only offers the required connectivity but also enhances your security posture.

Understanding Azure Firewall

Azure Firewall is a managed, cloud-based network security service that protects your Azure Virtual Network resources. It offers built-in high availability and scalability, as well as features like centralized policy management and dynamic filtering of outbound, inbound, and lateral network traffic.

Key Benefits of Azure Firewall:

  • Comprehensive Threat Protection: Intelligent, real-time threat detection and prevention.
  • Centralized Policy Management: Manage firewall rules across multiple subscriptions and networks.
  • High Availability and Scalability: Automatically scales to meet your network needs.
  • Application and Network Rules: Control access based on fully qualified domain names (FQDNs), IP addresses, and port numbers.
  • Logging and Analytics: Integrated with Azure Monitor for comprehensive logging and analytics.

Architecting the Network Topology

Designing an effective network layout is crucial. Here’s an illustrative topology:

graph LR; A["VM Subnet
(User VMs without PIP)"] -->|UDR points to AzureFW| B[Azure Firewall Subnet]; B -->|AZFW with PIP| C[Internet]
  • Public IP Addresses or Prefixes: Assigns specific public IPs so your outbound connections use predictable and consistent IP addresses.
⚠️
Warning: Azure Firewall Public IP addresses: If you assign multiple front end public addresses then outbound traffic will round-robin between those addresses for outbound connectivity.

Implementation Steps

Let’s set up Azure Firewall to provide secure and reliable outbound internet connectivity for your Azure VMs. We’ll cover both the Azure Portal and Bicep approaches.

Implementation Using the Azure Portal

Step 1: Create a Public IP Address

  1. Navigate to Public IP Addresses:
    • In the Azure Portal, search for “Public IP addresses” and select it.
  2. Create a New Public IP:
    • Click "+ Create".
    • Basics Tab:
      • Subscription: Select your subscription.
      • Resource Group: Choose an existing one or create a new one.
      • Name: Enter a name, e.g., myFirewallPublicIP.
      • Region: Choose the region of your VNet.
    • IP Version: IPv4.
    • SKU: Standard.
    • Assignment: Static.
    • Click “Review + Create”, then “Create”.

Step 2: Create the Azure Firewall

  1. Navigate to Firewalls:
    • Search for “Firewalls” and select it.
  2. Create a New Firewall:
    • Click "+ Create".
    • Basics Tab:
      • Subscription: Your subscription.
      • Resource Group: Same as above.
      • Name: myAzureFirewall.
      • Region: Same as your VNet.
    • Firewall Tier: Standard (choose Premium for advanced features if needed).
  3. Associate Public IP:
    • Firewall Management: Select Firewall public IP configuration.
    • Add a New Public IP: Use myFirewallPublicIP.
  4. Virtual Network Configuration:
    • Virtual Network: Choose or create a VNet.
    • Subnets: Ensure you have a subnet named AzureFirewallSubnet.
  5. Review and Create:
    • Click “Review + Create”, ensure validation passes.
    • Click “Create”.

Step 3: Configure Firewall Rules

  1. Navigate to Your Firewall:
    • Go to Firewalls in the Azure Portal and select myAzureFirewall.
  2. Add Application Rules:
    • Application Rule Collection: Click on “Rules” > Application Rule Collection.
    • Add Rule Collection:
      • Name: AppRuleCollection1.
      • Priority: 200.
      • Action: Allow.
    • Add Rule:
      • Name: AllowInternetAccess.
      • Source IP Addresses: Add the IP range of your subnet or VMs.
      • Target FQDNs: e.g., `` for all internet access.
      • Protocol: HTTP, HTTPS.
    • Click “Add”.
  3. Add Network Rules:
    • Network Rule Collection: Click on “Rules” > Network Rule Collection.
    • Add Rule Collection:
      • Name: NetRuleCollection1.
      • Priority: 100.
      • Action: Allow.
    • Add Rule:
      • Name: AllowOutboundAccess.
      • Source IP Addresses: Add the IP range of your subnet or VMs.
      • Destination IP Addresses: Add `` for all internet access or specific IP ranges.
      • Destination Ports: Add ports like 80 for HTTP, 443 for HTTPS.
      • Protocol: TCP, UDP.
    • Click “Add”.

Step 4: Verify Outbound Connectivity

  1. Connect to a VM in the Subnet:
    • Use Remote Desktop Protocol (RDP) or SSH.
  2. Check Outbound IP Address:
    • Open a web browser and navigate to ifconfig.me or ident.me
    • Verify that the displayed IP matches myFirewallPublicIP.

Implementation Using Bicep

Let’s achieve the same deployment using a Bicep template.

ℹ️

Prerequisites

  • Azure CLI: Ensure you have the latest version.
  • Bicep CLI: Comes integrated with Azure CLI.

Step 1: Write the Bicep Template

Create a file named azureFirewall.bicep.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
@description('Name of the Azure Firewall')
param firewallName string = 'myAzureFirewall'

@description('Name of the Azure Firewall Policy')
param firewallpolicyName string = 'myAzureFirewallPolicy'

@description('Location for all resources')
param location string = resourceGroup().location

@description('Name of the Virtual Network')
param vnetName string = 'firewallVNet'

@description('Address prefix for the Virtual Network')
param vnetAddressPrefix string = '10.0.0.0/16'

@description('Address prefix for the Azure Firewall Subnet')
param firewallSubnetPrefix string = '10.0.1.0/24'

@description('Public IP Name for Azure Firewall')
param firewallPublicIPName string = '${firewallName}-publicIP'

@description('Virtual Network Name for AzureFirewall subnet')
param firewallSubnetName string = 'AzureFirewallSubnet'

@description('Array of IP Configurations for Azure Firewall')
var firewallIpConfigurations = [
  {
    name: '${firewallName}-ipConfig'
    properties: {
      subnet: {
        id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, firewallSubnetName)
      }
      publicIPAddress: {
        id: resourceId('Microsoft.Network/publicIPAddresses', firewallPublicIPName)
      }
    }
  }
]

@description('Azure Firewall Policy')
resource azureFWPolicy 'Microsoft.Network/firewallPolicies@2024-05-01' = {
  name: firewallpolicyName
  location: location
}

resource networkRuleCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2022-01-01' = {
  parent: azureFWPolicy
  name: 'DefaultNetworkRuleCollectionGroup'
  properties: {
    priority: 200
    ruleCollections: [
      {
        ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
        action: {
          type: 'Allow'
        }
        name: 'AllowAllExceptHTTPandHTTPS'
        priority: 200
          rules: [
            {
              name: 'AllowAllExcept80and443'
              description: 'Allow all traffic except ports 80 and 443'
              ruleType: 'NetworkRule'
              ipProtocols: [
                'Any'
              ]
              sourceAddresses: [
                '*'
              ]
              destinationAddresses: [
                '*'
              ]
              destinationPorts: [
                '1-79'
                '81-442'
                '444-60000'
              ]
            }
          ]
        }
    ]
  }
}

resource applicationRuleCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2022-01-01' = {
  parent: azureFWPolicy
  name: 'DefaultApplicationRuleCollectionGroup'
  properties: {
    priority: 300
    ruleCollections: [
      {
        ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
        name: 'AllowHTTPandHTTPS'
        priority: 1000
        action: {
          type: 'Allow'
        }
        rules: [
          {
            ruleType: 'ApplicationRule'
            name: 'AllowHTTPandHTTPS'
              protocols: [
                {
                  protocolType: 'Http'
                  port: 80
                }
                {
                  protocolType: 'Https'
                  port: 443
                }
              ]
              sourceAddresses: [
                '*'
              ]
              targetFqdns: [
                '*'
              ]
            }
          ]
        }
    ]
  }
}

@description('Azure Firewall Resource')
resource azureFirewall 'Microsoft.Network/azureFirewalls@2023-05-01' = {
  name: firewallName
  location: location
  properties: {
    ipConfigurations: firewallIpConfigurations
    firewallPolicy: {
      id: azureFWPolicy.id
    }
  }
}

@description('Public IP for Azure Firewall')
resource firewallPublicIP 'Microsoft.Network/publicIPAddresses@2023-05-01' = {
  name: firewallPublicIPName
  location: location
  sku: {
    name: 'Standard'
    tier: 'Regional'
  }
  properties: {
    publicIPAllocationMethod: 'Static'
  }
}

@description('Virtual Network for Azure Firewall')
resource firewallVNet 'Microsoft.Network/virtualNetworks@2023-05-01' = {
  name: vnetName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        vnetAddressPrefix
      ]
    }
    subnets: [
      {
        name: firewallSubnetName
        properties: {
          addressPrefix: firewallSubnetPrefix
          // AzureFirewall subnet requires 'AzureFirewallSubnet' as the name
          // No network security groups are associated with this subnet
        }
      }
    ]
  }
  dependsOn: [
    firewallPublicIP
  ]
}

Step 2: Deploy the Bicep Template

  1. Login to Azure:

    1
    
    az login
    
  2. Set the Subscription Context:

    1
    
    az account set --subscription 'YourSubscriptionID'
    
  3. Deploy the Template:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    az deployment group create \\
      --resource-group 'YourResourceGroup' \\
      --template-file azureFirewall.bicep \\
      --parameters firewallName='myAzureFirewall' \\
                  firewallpolicyName='myAzureFirewallPolicy' \\
                  publicIPName='myFirewallPublicIP' \\
                  vnetName='myVNet' \\
                  vnetAddressPrefix='10.0.0.0/16' \\
                  subnetName='AzureFirewallSubnet' \\
                  firewallSubnetPrefix='10.0.1.0/24'
    

Step 3: Verify Deployment

Repeat the verification steps from the portal implementation to confirm that your VMs are using Azure Firewall for outbound traffic.


Wrapping Up

Implementing Azure Firewall provides enhanced security and centralized management for outbound internet access, ensuring your Azure VMs remain securely connected as direct internet access is deprecated.

Key Takeaways:

  • Azure Firewall Enhances Security: Comprehensive threat protection and policy management.
  • Flexible Implementation Options: Use the Azure Portal for quick setups or Bicep for repeatable, infrastructure-as

Learn More

Here are some additional resources to help you dive deeper into Azure Firewall: