Skip to main content

Azure Firewall

Azure Firewall is a managed, stateful network firewall that runs inside a virtual network. In the Azure Enterprise Baseline it sits in the hub as the one egress point to the internet and the one transit point between stages, and its policy keeps prod and nonprod apart.

What it does​

The firewall filters traffic with network rules (addresses, ports and protocols, including service tags) and application rules (fully qualified domain names, FQDNs, over HTTP and HTTPS), held in a firewall policy of rule collection groups. Network rules are evaluated before application rules, whatever the group priority. Threat intelligence alerts on or denies traffic to known malicious addresses, and the DNS proxy lets the firewall resolve the FQDNs its rules name the same way clients do. Three SKUs exist: Basic (250 Mbps, no DNS proxy, alert-only threat intelligence), Standard (FQDN filtering, DNS proxy, threat intelligence deny, autoscaling to 30 Gbps) and Premium (adds intrusion detection and prevention, IDPS).

How BuiltForProd uses it​

Two units in acme-core-network build it from the switches in network.hcl:

Azure/acme-azure-platform-baseline/environments/core/network/eastus2/network.hcl (lines 23-35)
# Firewall SKU. Standard (default): FQDN filtering, DNS proxy, threat intel deny, autoscaling to
# 30 Gbps (~$912/month + $0.016/GB). Basic: 250 Mbps fixed, threat intel alert-only, no DNS proxy (~$288/month) --
# the cost switch for single-stage deployments. Premium (~$1,277/month): IDPS, in Alert mode here; no TLS inspection.
firewall_sku = "Standard" # @optional: Standard | Basic | Premium
firewall_zones = ["1", "2", "3"] # @optional: availability zones of the firewall, its public IPs and the VPN gateway public IP

# Isolation domains: spokes in different domains cannot reach each other through the firewall.
isolation_domains = { # @optional: a stage belongs to exactly one domain; "shared" is reserved
prod = ["prod"]
nonprod = ["sandbox", "dev", "staging"]
}

enable_firewall_threat_intel_deny = false # @optional: Standard+ only; switches threat intelligence from Alert to Deny (no cost)

The firewall (firewall unit) is acme-eus2-network-fw, zone-redundant across zones 1 to 3, with every public address taken from one /30 public IP prefix, so partners can allow-list a single block. Two addresses are used for SNAT by default (public_ip_count, about $7 per address per month). Firewall logs go to the audit workspace in resource-specific tables: network, application and NAT rules, threat intelligence, DNS queries and FQDN resolution failures, plus IDPS signatures on Premium. It exists only when hub_egress = "azure_firewall", the default; in the spoke_nat mode the unit creates nothing and the NAT gateway of each spoke carries the egress.

SKUPrice (unit comment)Notes
Standard (default)$1.25/hour (~$912/month) + $0.016/GBFQDN rules, DNS proxy through the resolver, threat intelligence deny
Basic$0.395/hour (~$288/month) + $0.065/GB250 Mbps, alert-only threat intelligence, needs the management subnet
Premium$1.75/hour (~$1,277/month) + $0.016/GBIDPS in Alert mode; no TLS inspection

The policy (firewall-policy unit) is acme-eus2-network-fw-policy, generated from the address plan and network.hcl:

PriorityGroupWhat it does
200platform-allowNTP; TCP 443 to Azure platform service tags (Monitor, Entra ID, Key Vault, the registries, AzureFrontDoor.Backend, regional AzureCloud and Storage); the AKS tunnel ports and FQDN tag; the Databricks control plane; the runners' FQDN allow-list (GitHub, OpenTofu and package registries)
250vpn-accessOne rule per point-to-site address pool, to the stages its groups may reach; only with enable_client_vpn
300isolation-domainsAllow inside a domain and to and from the hub and runner networks; deny everything else inside the organization range
400egress-webTCP 80 and 443 to the internet, or an FQDN allow-list (egress_allowed_fqdns, Standard and Premium)

The isolation domains are prod = ["prod"] and nonprod = ["sandbox", "dev", "staging"]; a plan fails if a stage of the address plan belongs to no domain. Because network rules run first, the cross-domain deny applies before any FQDN rule. Threat intelligence is in Alert mode, and enable_firewall_threat_intel_deny = true switches it to Deny at no cost. With the DNS Private Resolver on, the policy's DNS proxy forwards to the resolver's inbound address, so FQDN rules resolve private names too.

Terms you will see​

TermMeaning
Firewall policyThe rule set attached to the firewall: acme-eus2-network-fw-policy.
Rule collection groupA prioritized block of network or application rule collections.
Isolation domainA group of stages that may reach each other: prod and nonprod.
SNATSource address translation of outbound traffic to the firewall's public IPs.
Service tagA Microsoft-maintained name for a service's address ranges.
FQDN tagA Microsoft-maintained list of FQDNs for a service, such as AzureKubernetesService.

Where to read more​