Azure Firewall and Web Application Firewall
What Is Azure Firewall?
Azure Firewall is a managed, cloud-native network security service that protects your Azure virtual network resources. It is a stateful firewall-as-a-service with built-in high availability and unrestricted cloud scalability. Azure Firewall operates at Layers 3–7, providing network-level and application-level filtering.
Official Documentation: Azure Firewall overview
Azure Firewall vs Application Gateway WAF
These two services serve different purposes and are often used together:
Azure Firewall
Application Gateway WAF
When to Use Each
Internet
↓
Azure Firewall (network security, threat intel, FQDN filtering)
↓
Application Gateway + WAF (Layer 7 routing, OWASP protection)
↓
Backend Services (APIM, App Services, VMs)
- Azure Firewall: Controls what traffic enters and leaves your network, blocks known threats, filters by FQDN
- Application Gateway WAF: Inspects HTTP request content for SQL injection, XSS, and other web exploits
Best practice: Use both together — Azure Firewall for network-level security and Application Gateway WAF for application-level protection.
Azure Firewall SKUs
| SKU | Key Features | Use Case |
|---|---|---|
| Basic | Network/application rules, threat intelligence | Small workloads, dev/test |
| Standard | All Basic features + FQDN tags, service tags, DNS proxy | Most production workloads |
| Premium | All Standard features + TLS inspection, IDPS, URL filtering, web categories | High-security environments, compliance |
Architecture — Hub-and-Spoke
Azure Firewall is typically deployed in a hub-and-spoke network topology:
Internet
↓
┌──────────────────────────────────────────┐
│ Hub VNet │
│ ┌────────────────────────────────────┐ │
│ │ Azure Firewall │ │
│ │ (AzureFirewallSubnet) │ │
│ └────────────────────────────────────┘ │
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ VPN Gateway │ │ ExpressRoute GW │ │
│ └─────────────┘ └──────────────────┘ │
└──────────┬──────────────────┬────────────┘
│ │
┌──────┴──────┐ ┌──────┴──────┐
│ Spoke 1 │ │ Spoke 2 │
│ Workloads │ │ Workloads │
│ (APIM, │ │ (App Svc, │
│ Logic App)│ │ VMs) │
└─────────────┘ └─────────────┘
Deploying Azure Firewall
Bicep Deployment
param location string = resourceGroup().location
param environment string
// Azure Firewall requires a dedicated subnet named exactly 'AzureFirewallSubnet'
resource hubVnet 'Microsoft.Network/virtualNetworks@2023-09-01' = {
name: 'vnet-hub-${environment}'
location: location
properties: {
addressSpace: {
addressPrefixes: [ '10.0.0.0/16' ]
}
subnets: [
{
name: 'AzureFirewallSubnet' // Must be this exact name
properties: {
addressPrefix: '10.0.1.0/26' // Minimum /26
}
}
{
name: 'AzureFirewallManagementSubnet' // Required for Basic SKU
properties: {
addressPrefix: '10.0.2.0/26'
}
}
]
}
}
// Public IP for Firewall
resource firewallPublicIp 'Microsoft.Network/publicIPAddresses@2023-09-01' = {
name: 'pip-fw-${environment}'
location: location
sku: { name: 'Standard' }
properties: {
publicIPAllocationMethod: 'Static'
}
}
// Firewall Policy
resource firewallPolicy 'Microsoft.Network/firewallPolicies@2023-09-01' = {
name: 'fwpol-${environment}'
location: location
properties: {
sku: {
tier: 'Standard'
}
threatIntelMode: 'Deny'
threatIntelWhitelist: {
fqdns: []
ipAddresses: []
}
dnsSettings: {
enableProxy: true
}
insights: {
isEnabled: true
logAnalyticsResources: {
defaultWorkspaceId: {
id: logAnalyticsWorkspace.id
}
}
}
}
}
// Azure Firewall
resource firewall 'Microsoft.Network/azureFirewalls@2023-09-01' = {
name: 'fw-hub-${environment}'
location: location
properties: {
sku: {
name: 'AZFW_VNet'
tier: 'Standard'
}
firewallPolicy: {
id: firewallPolicy.id
}
ipConfigurations: [
{
name: 'fw-ipconfig'
properties: {
subnet: {
id: resourceId('Microsoft.Network/virtualNetworks/subnets', hubVnet.name, 'AzureFirewallSubnet')
}
publicIPAddress: {
id: firewallPublicIp.id
}
}
}
]
}
}
output firewallPrivateIp string = firewall.properties.ipConfigurations[0].properties.privateIPAddress
Rule Types
Azure Firewall evaluates rules in this order:
- DNAT rules (inbound) — Translate destination IP/port
- Network rules — Allow/deny by IP, port, protocol
- Application rules — Allow/deny by FQDN, URL, web category
Rule Collection Groups
resource ruleCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2023-09-01' = {
parent: firewallPolicy
name: 'DefaultRuleCollectionGroup'
properties: {
priority: 200
ruleCollections: [
// DNAT Rules — Inbound traffic translation
{
ruleCollectionType: 'FirewallPolicyNatRuleCollection'
name: 'dnat-rules'
priority: 100
action: { type: 'Dnat' }
rules: [
{
ruleType: 'NatRule'
name: 'inbound-to-apim'
sourceAddresses: [ '*' ]
destinationAddresses: [ firewallPublicIp.properties.ipAddress ]
destinationPorts: [ '443' ]
ipProtocols: [ 'TCP' ]
translatedAddress: '10.0.10.5' // APIM private IP
translatedPort: '443'
}
]
}
// Network Rules — Layer 3/4 filtering
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
name: 'network-rules'
priority: 200
action: { type: 'Allow' }
rules: [
{
ruleType: 'NetworkRule'
name: 'allow-dns'
sourceAddresses: [ '10.0.0.0/8' ]
destinationAddresses: [ '*' ]
destinationPorts: [ '53' ]
ipProtocols: [ 'TCP', 'UDP' ]
}
{
ruleType: 'NetworkRule'
name: 'allow-ntp'
sourceAddresses: [ '10.0.0.0/8' ]
destinationAddresses: [ '*' ]
destinationPorts: [ '123' ]
ipProtocols: [ 'UDP' ]
}
{
ruleType: 'NetworkRule'
name: 'allow-spoke-to-spoke'
sourceAddresses: [ '10.1.0.0/16' ]
destinationAddresses: [ '10.2.0.0/16' ]
destinationPorts: [ '*' ]
ipProtocols: [ 'Any' ]
}
]
}
// Application Rules — Layer 7 FQDN filtering
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
name: 'application-rules'
priority: 300
action: { type: 'Allow' }
rules: [
{
ruleType: 'ApplicationRule'
name: 'allow-azure-services'
sourceAddresses: [ '10.0.0.0/8' ]
protocols: [
{ protocolType: 'Https', port: 443 }
]
fqdnTags: [
'AzureKubernetesService'
'WindowsUpdate'
'AzureBackup'
]
}
{
ruleType: 'ApplicationRule'
name: 'allow-microsoft-apis'
sourceAddresses: [ '10.0.0.0/8' ]
protocols: [
{ protocolType: 'Https', port: 443 }
]
targetFqdns: [
'*.microsoft.com'
'*.azure.com'
'*.azure-api.net'
'*.servicebus.windows.net'
'*.vault.azure.net'
]
}
{
ruleType: 'ApplicationRule'
name: 'allow-third-party-apis'
sourceAddresses: [ '10.1.0.0/16' ]
protocols: [
{ protocolType: 'Https', port: 443 }
]
targetFqdns: [
'api.partner.example.com'
'*.github.com'
]
}
]
}
]
}
}
Threat Intelligence
Azure Firewall can automatically block traffic to/from known malicious IP addresses and FQDNs using Microsoft's threat intelligence feed:
| Mode | Behaviour |
|---|---|
| Off | Threat intelligence disabled |
| Alert only | Logs alerts for malicious traffic but allows it |
| Alert and deny | Logs and blocks malicious traffic (recommended) |
firewallPolicy: {
properties: {
threatIntelMode: 'Deny' // Alert and deny
threatIntelWhitelist: {
fqdns: [ 'test.example.com' ] // Whitelist specific FQDNs
ipAddresses: [ '10.0.0.1' ] // Whitelist specific IPs
}
}
}
DNAT (Destination Network Address Translation)
DNAT rules translate inbound traffic from the Firewall's public IP to private backend addresses:
Internet → Firewall Public IP:443 → DNAT → Backend Private IP:443
(203.0.113.1:443) (10.0.10.5:443)
This is useful for:
- Exposing internal services without a public IP on each service
- Centralising inbound traffic through a single public IP
- Applying network rules before traffic reaches backends
DNS Proxy
Enable the DNS proxy to allow spoke VNets to use Azure Firewall as their DNS resolver, enabling FQDN-based rules:
dnsSettings: {
enableProxy: true
servers: [ '168.63.129.16' ] // Azure DNS (default)
}
Configure spoke VNet DNS to point to the Firewall's private IP:
resource spokeVnet 'Microsoft.Network/virtualNetworks@2023-09-01' = {
// ...
properties: {
dhcpOptions: {
dnsServers: [ firewall.properties.ipConfigurations[0].properties.privateIPAddress ]
}
}
}
Route Tables (UDR)
Force traffic through Azure Firewall using User Defined Routes:
resource routeTable 'Microsoft.Network/routeTables@2023-09-01' = {
name: 'rt-spoke-${environment}'
location: location
properties: {
disableBgpRoutePropagation: false
routes: [
{
name: 'route-to-firewall'
properties: {
addressPrefix: '0.0.0.0/0'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: firewall.properties.ipConfigurations[0].properties.privateIPAddress
}
}
{
name: 'route-spoke-to-spoke'
properties: {
addressPrefix: '10.2.0.0/16'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: firewall.properties.ipConfigurations[0].properties.privateIPAddress
}
}
]
}
}
// Associate route table with spoke subnet
resource spokeSubnet 'Microsoft.Network/virtualNetworks/subnets@2023-09-01' = {
parent: spokeVnet
name: 'snet-workload'
properties: {
addressPrefix: '10.1.1.0/24'
routeTable: {
id: routeTable.id
}
}
}
Azure Firewall Premium Features
| Feature | Description |
|---|---|
| TLS Inspection | Decrypt, inspect, and re-encrypt HTTPS traffic |
| IDPS | Intrusion Detection and Prevention System — signature-based |
| URL Filtering | Filter by full URL path (not just FQDN) |
| Web Categories | Allow/block by category (social media, gambling, etc.) |
TLS Inspection
resource premiumPolicy 'Microsoft.Network/firewallPolicies@2023-09-01' = {
name: 'fwpol-premium-${environment}'
location: location
properties: {
sku: { tier: 'Premium' }
transportSecurity: {
certificateAuthority: {
keyVaultSecretId: 'https://kv-fw-certs.vault.azure.net/secrets/intermediate-ca'
name: 'fw-intermediate-ca'
}
}
intrusionDetection: {
mode: 'Deny'
configuration: {
signatureOverrides: [
{
id: '2024897'
mode: 'Off' // Disable specific signature causing false positives
}
]
bypassTrafficSettings: [
{
name: 'bypass-internal-health'
protocol: 'TCP'
sourceAddresses: [ '10.0.0.0/8' ]
destinationAddresses: [ '10.0.0.0/8' ]
destinationPorts: [ '8080' ]
}
]
}
}
}
}
URL Filtering (Premium)
{
ruleType: 'ApplicationRule'
name: 'block-specific-urls'
sourceAddresses: [ '10.0.0.0/8' ]
protocols: [
{ protocolType: 'Https', port: 443 }
]
targetUrls: [
'www.example.com/malware/*'
'downloads.example.com/blocked-file.exe'
]
terminateTLS: true // Requires TLS inspection
}
Web Categories (Premium)
{
ruleType: 'ApplicationRule'
name: 'block-social-media'
sourceAddresses: [ '10.1.0.0/16' ]
protocols: [
{ protocolType: 'Https', port: 443 }
]
webCategories: [
'SocialNetworking'
'Gambling'
'PeerToPeer'
]
terminateTLS: true
}
Create via Azure CLI
# Create Firewall Policy
az network firewall policy create \
--name fwpol-prod \
--resource-group rg-networking-prod \
--location uksouth \
--sku Standard \
--threat-intel-mode Deny
# Create Azure Firewall
az network firewall create \
--name fw-hub-prod \
--resource-group rg-networking-prod \
--location uksouth \
--vnet-name vnet-hub-prod \
--firewall-policy fwpol-prod \
--sku AZFW_VNet \
--tier Standard
# Configure Firewall IP
az network firewall ip-config create \
--firewall-name fw-hub-prod \
--resource-group rg-networking-prod \
--name fw-ipconfig \
--public-ip-address pip-fw-prod \
--vnet-name vnet-hub-prod
# Get Firewall private IP (for route tables)
az network firewall show \
--name fw-hub-prod \
--resource-group rg-networking-prod \
--query 'ipConfigurations[0].privateIPAddress' -o tsv
# Add network rule collection
az network firewall policy rule-collection-group create \
--name DefaultRuleCollectionGroup \
--policy-name fwpol-prod \
--resource-group rg-networking-prod \
--priority 200
# Add application rule
az network firewall policy rule-collection-group collection add-filter-collection \
--name allow-azure \
--policy-name fwpol-prod \
--resource-group rg-networking-prod \
--rule-collection-group-name DefaultRuleCollectionGroup \
--collection-priority 300 \
--action Allow \
--rule-name allow-microsoft \
--rule-type ApplicationRule \
--source-addresses '10.0.0.0/8' \
--protocols Https=443 \
--fqdn-tags AzureKubernetesService WindowsUpdate
Diagnostic Settings
resource fwDiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
name: 'fw-diagnostics'
scope: firewall
properties: {
workspaceId: logAnalyticsWorkspace.id
logs: [
{ category: 'AZFWNetworkRule', enabled: true }
{ category: 'AZFWApplicationRule', enabled: true }
{ category: 'AZFWNatRule', enabled: true }
{ category: 'AZFWThreatIntel', enabled: true }
{ category: 'AZFWIdpsSignature', enabled: true }
{ category: 'AZFWDnsQuery', enabled: true }
{ category: 'AZFWFqdnResolveFailure', enabled: true }
]
metrics: [
{ category: 'AllMetrics', enabled: true }
]
}
}
KQL: Blocked Traffic
AZFWNetworkRule
| where TimeGenerated > ago(1h)
| where Action == "Deny"
| summarize BlockedCount = count() by SourceIp, DestinationIp, DestinationPort, Protocol
| order by BlockedCount desc
| take 20
KQL: Application Rule Hits
AZFWApplicationRule
| where TimeGenerated > ago(24h)
| summarize
Allowed = countif(Action == "Allow"),
Denied = countif(Action == "Deny")
by Fqdn
| order by Denied desc
| take 20
KQL: Threat Intelligence Hits
AZFWThreatIntel
| where TimeGenerated > ago(7d)
| project TimeGenerated, SourceIp, DestinationIp, DestinationPort, ThreatDescription, Action
| order by TimeGenerated desc
KQL: DNS Query Analysis
AZFWDnsQuery
| where TimeGenerated > ago(1h)
| summarize QueryCount = count() by QueryName, QueryType
| order by QueryCount desc
| take 50
Naming Conventions
fw-{workload}-{environment} // Azure Firewall
fwpol-{environment} // Firewall Policy
pip-fw-{workload}-{environment} // Firewall Public IP
rt-{spoke}-{environment} // Route Table
Examples:
fw-hub-prod/fwpol-prodpip-fw-hub-prodrt-spoke-workload-prod
Best Practices
- Deploy in a hub-and-spoke topology with Firewall in the hub VNet
- Use Firewall Policy (not classic rules) for centralised management
- Enable threat intelligence in Deny mode for production
- Use FQDN-based rules instead of IP-based where possible — IPs change, FQDNs don't
- Enable DNS proxy on the Firewall and point spoke DNS to the Firewall private IP
- Use service tags and FQDN tags to simplify rules for Azure services
- Combine with Application Gateway WAF — Firewall for network security, WAF for OWASP
- Force tunnel spoke traffic through the Firewall using UDR (0.0.0.0/0 → Firewall)
- Use Premium SKU when TLS inspection or IDPS is required for compliance
- Monitor with Log Analytics — set alerts on threat intelligence hits and denied traffic spikes
- Organise rules into collection groups by function (platform, workload, security)
- Use IP Groups to simplify management of large IP address sets across rules