Goal
You want URL filtering, web categories on full URLs, and IDPS signatures that see inside HTTPS. That needs TLS inspection. The firewall terminates each TLS session, makes a certificate for the destination on the fly, signs it with your intermediate CA, and opens a new session to the server.
Done carelessly, it breaks every client that doesn't trust the CA, every app that pins certificates, and anything using mutual TLS, all at the moment you enable it. Use this checklist in order.
Scope: TLS inspection on Azure Firewall Premium covers outbound and east-west traffic. For inbound traffic from the internet, terminate TLS on Application Gateway or Front Door in front.
1. Intermediate CA certificate
- Issued by your enterprise PKI for production. A self-signed root is for labs only.
- A single intermediate CA certificate, not the root and not a full chain.
- RSA key of at least 4,096 bits, exportable.
-
BasicConstraintscritical,CA:TRUE, path length of 1 or more. -
KeyUsagecritical withkeyCertSign. - Valid for at least a year forward.
- Packaged as a password-less PFX (PKCS#12) holding the certificate and private key. PEM isn't supported.
Request extensions for a CSR you send to the enterprise CA (OpenSSL config fragment):
[ v3_fw_intermediate ]
basicConstraints = critical, CA:TRUE, pathlen:1
keyUsage = critical, keyCertSign, cRLSign
subjectKeyIdentifier = hash
openssl req -new -newkey rsa:4096 -nodes -keyout fw-int.key \
-subj "/CN=Contoso Azure Firewall TLS Intermediate" -out fw-int.csr
# ...submit to the enterprise CA with the extensions above, receive fw-int.crt...
openssl x509 -in fw-int.crt -noout -text | grep -A1 -E "Basic Constraints|Key Usage"
openssl pkcs12 -export -inkey fw-int.key -in fw-int.crt -out fw-int.pfx -password pass:
Caution
This private key can impersonate any website to every client that trusts your root. Generate it on a controlled host, import it straight into Key Vault, and securely delete the local key and PFX files. Restrict who can read the Key Vault secret as tightly as you restrict Domain Admins.
2. Key Vault and the firewall's identity
- Use a dedicated Key Vault, or one limited to network security, with soft delete and purge protection enabled.
- Create a user-assigned managed identity for the firewall policy.
- Give the identity Get and List on secrets. Certificate permissions alone don't work. With RBAC, use
Key Vault Secrets User. - If the vault firewall is on, allow trusted Microsoft services. Azure Firewall Premium is a Key Vault trusted service.
RG=rg-hub-net
KV=kv-hub-fwtls
az identity create -g $RG -n id-fw-tls
ID_PRINCIPAL=$(az identity show -g $RG -n id-fw-tls --query principalId -o tsv)
ID_RESOURCE=$(az identity show -g $RG -n id-fw-tls --query id -o tsv)
az keyvault certificate import --vault-name $KV -n fw-tls-intermediate --file fw-int.pfx
az role assignment create --assignee-object-id $ID_PRINCIPAL --assignee-principal-type ServicePrincipal \
--role "Key Vault Secrets User" \
--scope "$(az keyvault show -n $KV --query id -o tsv)/secrets/fw-tls-intermediate"
az keyvault update -n $KV --bypass AzureServices
Importing as a certificate creates a secret with the same name, and that secret is what the firewall reads. Use the versionless secret ID in the policy so a renewed certificate is picked up without editing the policy. Still update the policy's TLS settings explicitly after a renewal, as Microsoft recommends.
3. Firewall policy TLS settings
- The policy is the Premium tier, attached to a Premium firewall.
- The identity, certificate name and Key Vault secret ID are set on the policy.
SECRET_ID="https://$KV.vault.azure.net/secrets/fw-tls-intermediate"
az network firewall policy update -g $RG -n fwp-hub-premium \
--identity $ID_RESOURCE \
--cert-name fw-tls-intermediate \
--key-vault-secret-id $SECRET_ID
az network firewall policy show -g $RG -n fwp-hub-premium \
--query "{sku:sku.tier, tls:transportSecurity.certificateAuthority, idps:intrusionDetection.mode}" -o jsonc
Some CLI builds mark these parameters as preview. Check az network firewall policy update --help. If the policy update fails with a Key Vault error, the problem is nearly always identity permissions or the vault's network rules. Check those before the certificate.
4. Client trust, for every runtime
The OS trust store is only part of the job. The rollout breaks in the runtimes that bring their own trust store.
- Windows: deploy the enterprise root (and intermediate) through GPO or Intune.
- Linux VMs:
update-ca-certificates(Debian/Ubuntu) orupdate-ca-trust(RHEL family) through your config management. - Java: import into the JVM's
cacerts, or use the OS store where your JDK supports it. - Python:
requestsusescertifi, not the OS. SetREQUESTS_CA_BUNDLEorSSL_CERT_FILE. - Node.js:
NODE_EXTRA_CA_CERTS=/path/to/root.pem. - Containers and AKS nodes: bake the CA into base images, or mount it. Check node-level pulls from registries that go through the firewall.
- Azure CLI and SDK tooling: the CLI bundles its own Python. Set
REQUESTS_CA_BUNDLEfor it too.
Test from each client type before you enable inspection on a rule:
# Through the firewall, after a rule with TLS inspection is enabled for a test FQDN
openssl s_client -connect www.example.com:443 -servername www.example.com </dev/null 2>/dev/null \
| openssl x509 -noout -issuer
# Issuer should be your firewall intermediate. Then confirm the client trusts it:
curl -sS -o /dev/null -w "%{http_code}\n" https://www.example.com
5. Bypass list: don't inspect what will break
TLS inspection is enabled per application rule (terminateTLS). Keep separate rules, ordered by rule collection priority, so traffic that must not be inspected matches an allow rule without TLS termination first.
- Apps that pin certificates: many mobile and desktop agents, some backup and EDR agents.
- Mutual TLS clients, where the firewall can't present the client certificate.
- Platform endpoints your OS, management and monitoring agents depend on. Start with patching, the monitoring agent and Azure management endpoints.
- Health, legal or privacy-sensitive categories your security and legal teams have excluded from inspection.
Bicep, with one inspected rule and one bypass rule:
resource rcg 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2023-09-01' = {
parent: fwPolicy
name: 'rcg-web-egress'
properties: {
priority: 300
ruleCollections: [
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
name: 'allow-no-inspect'
priority: 310
action: { type: 'Allow' }
rules: [
{
ruleType: 'ApplicationRule'
name: 'pinned-agents'
sourceAddresses: [ '10.20.0.0/16' ]
protocols: [ { protocolType: 'Https', port: 443 } ]
targetFqdns: [ '*.agent-vendor.example' ]
terminateTLS: false
}
]
}
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
name: 'allow-inspect'
priority: 320
action: { type: 'Allow' }
rules: [
{
ruleType: 'ApplicationRule'
name: 'general-web'
sourceAddresses: [ '10.20.0.0/16' ]
protocols: [ { protocolType: 'Https', port: 443 } ]
webCategories: [ 'ComputersAndTechnology', 'Business' ]
terminateTLS: true
}
]
}
]
}
}
Web category names vary by firewall build. Run what-if before you deploy, and check the category list your policy accepts.
6. IDPS alongside inspection
- Start IDPS in Alert, not Deny, for at least a week after inspection goes live.
- Set private ranges in the policy to match your real address space, so IDPS treats direction correctly.
- Review signature hits, then switch to Deny or override specific signatures.
az network firewall policy update -g $RG -n fwp-hub-premium --idps-mode Alert
7. Phased rollout and validation
- Ring 0: one test subnet, one FQDN. Ring 1: IT and engineering workstations. Ring 2: server subnets by application owner. Ring 3: everything else.
- Check throughput and latency after each ring. Inspection costs firewall capacity, so check the current Premium performance figures and your firewall's
ThroughputandDataProcessedmetrics. - Watch logs for drops and handshake failures after each ring:
AZFWApplicationRule
| where TimeGenerated > ago(1h)
| summarize hits = count() by Action, Fqdn, RuleCollection, Rule
| order by hits desc
AZFWIdpsSignature
| where TimeGenerated > ago(24h)
| summarize hits = count() by SignatureId, Description, Action, Severity
| order by hits desc
A sudden rise in client-side TLS errors with no matching firewall deny means a client doesn't trust the CA, or the destination is pinned. Add it to the bypass rule or fix the trust store. Don't turn inspection off globally.
Warning
Put the intermediate CA's expiry date in your certificate monitoring, with an alert well ahead of it. If it expires, every inspected HTTPS connection starts failing certificate validation at once. Renew it, re-import it into the same Key Vault secret name, update the policy, and confirm the issuer on a test client.
Prevent it next time
- Keep the bypass list and the inspected rule collections in IaC, with an owner for each bypass entry and a review date.
- Make CA trust distribution part of base image builds for VMs and containers, not a follow-up ticket.
- Track the intermediate CA's renewal like the internet-facing TLS certificates, and practise a renewal in non-production every year.