← Back to Guides

Azure Firewall and Web Application Firewall

IntermediateAzure Networking2026-03-14

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

Network security — control outbound/inbound traffic
LayerLayer 3–7
Traffic typeAll protocols (TCP, UDP, ICMP, HTTP)
DirectionInbound + outbound + east-west
OWASP rulesNo
FQDN filteringYes (outbound)
Threat intelligenceYes — block known malicious IPs/FQDNs
TLS inspectionPremium
URL-based routingNo
DNATYes
Typical positionHub VNet — all traffic flows through
🛡

Application Gateway WAF

Web app protection — OWASP, bot protection
LayerLayer 7 only
Traffic typeHTTP/HTTPS only
DirectionInbound only
OWASP rulesYes (SQL injection, XSS, etc.)
FQDN filteringNo
Threat intelligenceNo
TLS inspectionSSL termination
URL-based routingYes
DNATNo
Typical positionIn front of web backends

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:

  1. DNAT rules (inbound) — Translate destination IP/port
  2. Network rules — Allow/deny by IP, port, protocol
  3. 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-prod
  • pip-fw-hub-prod
  • rt-spoke-workload-prod

Best Practices

  1. Deploy in a hub-and-spoke topology with Firewall in the hub VNet
  2. Use Firewall Policy (not classic rules) for centralised management
  3. Enable threat intelligence in Deny mode for production
  4. Use FQDN-based rules instead of IP-based where possible — IPs change, FQDNs don't
  5. Enable DNS proxy on the Firewall and point spoke DNS to the Firewall private IP
  6. Use service tags and FQDN tags to simplify rules for Azure services
  7. Combine with Application Gateway WAF — Firewall for network security, WAF for OWASP
  8. Force tunnel spoke traffic through the Firewall using UDR (0.0.0.0/0 → Firewall)
  9. Use Premium SKU when TLS inspection or IDPS is required for compliance
  10. Monitor with Log Analytics — set alerts on threat intelligence hits and denied traffic spikes
  11. Organise rules into collection groups by function (platform, workload, security)
  12. Use IP Groups to simplify management of large IP address sets across rules

Official Microsoft Resources