Part 6 of 1638 min read · 8 diagramsAI-assisted

Networking: Application Delivery

Table of Contents#

  1. Application Delivery — Choosing the Right Layer
  2. Azure Load Balancer — Architecture and SKUs
  3. Public vs. Internal Load Balancers
  4. Regional vs. Cross-Region Load Balancers
  5. Load Balancing Rules and Health Probes
  6. Inbound NAT Rules
  7. Outbound Rules and SNAT Port Exhaustion
  8. Gateway Load Balancer — Transparent NVA Insertion
  9. Application Gateway — Layer 7 Architecture
  10. Application Gateway v2 Autoscaling and Zone Redundancy
  11. Backend Pools, HTTP Settings, Listeners, and Routing Rules
  12. Health Probes for Application Gateway
  13. URL Rewrite and Header Manipulation
  14. TLS Termination and End-to-End TLS
  15. Web Application Firewall on Application Gateway
  16. Session Affinity and Multi-Site Hosting
  17. Azure Front Door — Global Layer 7 Delivery
  18. Front Door Tiers — Standard and Premium
  19. Front Door Routing, Origins, and Endpoints
  20. Front Door Caching
  21. Front Door Traffic Acceleration
  22. Front Door Rules Engine — Rewrite and Redirect
  23. Securing an Origin With Private Link in Front Door
  24. Custom Domains and Managed Certificates on Front Door
  25. Azure Traffic Manager — DNS-Based Global Routing
  26. Traffic Manager Routing Methods
  27. Application Gateway for Containers — AKS-Native Ingress
  28. Choosing Between Load Balancer, Application Gateway, Front Door, and Traffic Manager
  29. A Full Worked Application Delivery Bootstrap for Meridian Freight
  30. Part 6 CLI Cheat Sheet
  31. Common Mistakes and Interview Traps
  32. Worked Practice Problems
  33. Summary and What's Next

Application Delivery — Choosing the Right Layer#

Azure offers four genuinely distinct application delivery services, and choosing among them is one of the most common real architectural decisions this series covers — each operates at a different layer, with different scope (regional vs. global) and different Layer 4/7 awareness.

Diagram
ServiceLayerScopeUnderstands HTTP?
Azure Load Balancer4 (TCP/UDP)RegionalNo
Application Gateway7 (HTTP/HTTPS)RegionalYes
Azure Front Door7 (HTTP/HTTPS)GlobalYes
Traffic ManagerDNS-levelGlobalNo — routes DNS responses, protocol-agnostic

Meridian Freight's shipment-api uses several of these together across this chapter: an internal Load Balancer for backend VM traffic, Application Gateway with WAF for regional ingress, and Front Door for the global carrier-partner-facing endpoint — Part 16 covers the full multi-region assembly.


Azure Load Balancer — Architecture and SKUs#

Azure Load Balancer distributes Layer 4 (TCP/UDP) traffic across backend instances — VMs, VM Scale Set instances — based purely on connection-level information, with no awareness of HTTP content.

az network lb create --name lb-driver-portal --resource-group rg-driver-portal-prod \
  --sku Standard --frontend-ip-name fe-driver-portal \
  --public-ip-address pip-lb-driver-portal --backend-pool-name bepool-driver-portal

A genuinely important, current fact worth stating explicitly: the Basic SKU was retired on September 30, 2025 — any design still referencing Basic Load Balancer (in documentation, old IaC templates, or institutional habit) needs to move to Standard, the only currently supported SKU for regular load balancing. Basic instances that still exist keep running but carry no SLA and are explicitly unsupported.

SKUStatusZone redundancyBackend pool size
BasicRetired Sept 2025 — unsupported, no SLANoUp to 300
StandardCurrent, only supported SKU for regular LBYesUp to 1,000
GatewaySpecialized — NVA traffic insertion (later in this chapter)YesN/A

Public vs. Internal Load Balancers#

# Public — internet-facing frontend
az network public-ip create --name pip-lb-driver-portal --resource-group rg-driver-portal-prod --sku Standard

# Internal — frontend IP lives inside the VNet, never internet-reachable
az network lb create --name lb-shipment-api-internal --resource-group rg-shipment-api-prod \
  --sku Standard --frontend-ip-name fe-internal --private-ip-address 10.1.1.100 \
  --vnet-name vnet-shipment-api --subnet snet-app

An internal load balancer is the correct choice for distributing traffic between internal tiers — shipment-api's app tier calling its database tier's replicas, for instance — where the frontend should never be reachable from outside the VNet at all, distinct from a public load balancer's internet-facing role.


Regional vs. Cross-Region Load Balancers#

A cross-region (global) Load Balancer — a genuinely distinct resource type, not just a configuration option on a regional one — uses Anycast to route traffic to the closest healthy REGIONAL Standard Load Balancer, providing a single, stable frontend IP spanning multiple regions.

az network cross-region-lb create --name lb-global-shipment-api --resource-group rg-networking-prod \
  --sku Standard --tier Global \
  --backend-pool-name bepool-regional-lbs

# Add a regional Load Balancer's frontend as a backend of the global one
az network cross-region-lb address-pool address add --lb-name lb-global-shipment-api \
  --resource-group rg-networking-prod --pool-name bepool-regional-lbs \
  --name backend-eastus --frontend-ip-address "<regional-lb-frontend-ip>"

Why this matters concretely for Part 16's multi-region design, worth previewing here: a cross-region Load Balancer's backend pool contains OTHER (regional) Load Balancers, not VMs directly — it's a load balancer of load balancers, providing Layer 4 global distribution as an alternative to Front Door's Layer 7 global routing for workloads that specifically need Layer 4 (non-HTTP) global traffic distribution.


Load Balancing Rules and Health Probes#

az network lb probe create --lb-name lb-driver-portal --resource-group rg-driver-portal-prod \
  --name probe-http --protocol Http --port 80 --path /healthz --interval 5 --threshold 2

az network lb rule create --lb-name lb-driver-portal --resource-group rg-driver-portal-prod \
  --name rule-http --protocol Tcp --frontend-port 80 --backend-port 80 \
  --frontend-ip-name fe-driver-portal --backend-pool-name bepool-driver-portal \
  --probe-name probe-http

# Check which backend instances the Load Balancer currently
# considers healthy, rather than assuming based on rule configuration alone
az network lb address-pool show --lb-name lb-driver-portal --resource-group rg-driver-portal-prod \
  --name bepool-driver-portal --query "loadBalancerBackendAddresses[].ipAddress"

A genuinely important, non-obvious detail worth stating precisely: a Load Balancer's health probe operates at the LOAD BALANCER level, checking each backend instance directly — it is NOT the same probe mechanism, and doesn't share configuration with, an Application Gateway's own health probes (covered later in this chapter) even in a design using both together. Each layer needs its own health check configured independently.


Inbound NAT Rules#

An inbound NAT rule maps a specific frontend port to a specific backend instance's specific port — commonly used for direct management access (SSH/RDP) to individual VMs behind a load balancer, without exposing each VM with its own public IP.

az network lb inbound-nat-rule create --lb-name lb-driver-portal --resource-group rg-driver-portal-prod \
  --name nat-ssh-vm01 --protocol Tcp --frontend-port 50001 --backend-port 22 \
  --frontend-ip-name fe-driver-portal

# List every inbound NAT rule currently open on a Load Balancer —
# a worthwhile periodic audit, since these are easy to forget about
az network lb inbound-nat-rule list --lb-name lb-driver-portal --resource-group rg-driver-portal-prod \
  --query "[].{name:name, frontendPort:frontendPort, backendPort:backendPort}"

Worth stating a genuinely better current alternative for this specific use case: Azure Bastion (Part 7) provides browser-based, RBAC-governed remote access without opening any inbound NAT rule at all — inbound NAT rules for SSH/RDP remain a valid pattern for legacy scenarios, but a new design should default to Bastion instead.


Outbound Rules and SNAT Port Exhaustion#

Part 4 covered NAT Gateway as the current recommended outbound mechanism; a Standard Load Balancer's own outbound rules are the older pattern worth understanding for existing designs.

az network lb outbound-rule create --lb-name lb-driver-portal --resource-group rg-driver-portal-prod \
  --name outbound-rule --frontend-ip-configs fe-driver-portal \
  --backend-pool bepool-driver-portal --allocated-outbound-ports 1024

# Monitor actual SNAT port usage against the allocated pool —
# the metric that would have caught the exhaustion below before it happened
az monitor metrics list --resource "<lb-resource-id>" --metric "SNATConnectionCount" \
  --interval PT1M

From the Trenches: A team relying on a Load Balancer's default outbound rules for a VMSS making many outbound connections per instance (calling an external payment API at high volume) hit SNAT port exhaustion — the pool of outbound ports allocated per instance ran out under real load, causing new outbound connections to fail intermittently in a pattern that looked like a flaky external API rather than the team's own outbound port allocation. The fix, matching Part 4's own recommendation, was migrating to NAT Gateway, which provides a dramatically larger outbound port pool per subnet than Load Balancer outbound rules ever practically allocate per instance.


Gateway Load Balancer — Transparent NVA Insertion#

Gateway Load Balancer solves a specific, narrower problem than a regular Load Balancer: transparently inserting third-party Network Virtual Appliances (an IDS/IPS, a custom traffic-inspection appliance) into a traffic path, without the traffic's source/destination IPs being altered — the NVA sees the same original packet a regular Load Balancer's mechanics would otherwise obscure.

Diagram

Why "transparent" is worth stating precisely as the key differentiator: the backend application never needs to know an NVA is inspecting its traffic — no source IP rewriting breaks the application's own IP-based logic (rate limiting, geo-based decisions) the way a naive NVA-in-the-path design otherwise would.

az network lb create --name gwlb-meridian --resource-group rg-networking-prod --sku Gateway

az network lb frontend-ip create --lb-name gwlb-meridian --resource-group rg-networking-prod \
  --name fe-gwlb --vnet-name vnet-meridian-hub --subnet snet-gwlb

Chaining a Gateway Load Balancer into another Load Balancer or Application Gateway's traffic path is done via a paired frontend IP configuration — the traffic flows through the Gateway Load Balancer's NVA fleet transparently as an extra hop, invisible to both the client and the ultimate backend.


Application Gateway — Layer 7 Architecture#

Application Gateway operates at Layer 7 — it understands HTTP/HTTPS content directly, enabling routing decisions based on URL path, host header, and other application-level attributes that a Layer 4 Load Balancer simply cannot see.

Diagram
az network application-gateway create --name appgw-shipment-api --resource-group rg-shipment-api-prod \
  --sku Standard_v2 --capacity 2 --vnet-name vnet-shipment-api --subnet snet-appgw \
  --public-ip-address pip-appgw-shipment-api

# Confirm the gateway's own operational status before assuming it's
# actually serving traffic correctly
az network application-gateway show --name appgw-shipment-api --resource-group rg-shipment-api-prod \
  --query "{state: operationalState, provisioningState: provisioningState}"

Application Gateway v2 Autoscaling and Zone Redundancy#

az network application-gateway update --name appgw-shipment-api --resource-group rg-shipment-api-prod \
  --min-capacity 2 --max-capacity 10 --zones 1 2 3

# Check current instance count against the configured autoscale range
az network application-gateway show --name appgw-shipment-api --resource-group rg-shipment-api-prod \
  --query "autoscaleConfiguration"

Worth stating a genuine v2 improvement over the original v1 SKU, no longer requiring a workaround this series' AWS chapter had to design around for its own load balancer equivalent: v2 SKU Application Gateways span multiple Availability Zones BY DEFAULT, removing the older need to provision separate per-zone instances behind a Traffic Manager just to achieve zone redundancy — v2 also removes the requirement to guess an instance count upfront, autoscaling instead based on actual traffic load between a configured minimum and maximum.


Backend Pools, HTTP Settings, Listeners, and Routing Rules#

az network application-gateway address-pool create --gateway-name appgw-shipment-api \
  --resource-group rg-shipment-api-prod --name pool-api --servers 10.1.1.10 10.1.1.11 vmss-shipment-api

az network application-gateway http-settings create --gateway-name appgw-shipment-api \
  --resource-group rg-shipment-api-prod --name settings-api --port 443 --protocol Https \
  --cookie-based-affinity Disabled --timeout 30

az network application-gateway http-listener create --gateway-name appgw-shipment-api \
  --resource-group rg-shipment-api-prod --name listener-https --frontend-port 443 \
  --ssl-cert cert-shipment-api

az network application-gateway rule create --gateway-name appgw-shipment-api \
  --resource-group rg-shipment-api-prod --name rule-api --http-listener listener-https \
  --address-pool pool-api --http-settings settings-api --rule-type PathBasedRouting
ComponentRole
Backend poolThe actual servers receiving traffic
HTTP settingsHow the gateway talks to the backend (protocol, port, timeout, cookie affinity)
ListenerWhat the gateway listens for (port, hostname, TLS certificate)
Routing ruleTies a listener to a backend pool, optionally with path-based routing

A backend pool can target VMs by IP, a VM Scale Set directly by resource reference, or an external FQDN — worth knowing the pool isn't limited to IP addresses alone, since referencing a VMSS directly means new instances are picked up automatically as the set scales, with no manual pool membership update required.


Health Probes for Application Gateway#

az network application-gateway probe create --gateway-name appgw-shipment-api \
  --resource-group rg-shipment-api-prod --name probe-api --protocol Https \
  --host-name-from-http-settings true --path /healthz --interval 30 --threshold 3 \
  --match-status-codes 200-399

# Check current backend health as the gateway sees it — the
# fastest way to confirm a probe is actually configured correctly
az network application-gateway show-backend-health --name appgw-shipment-api \
  --resource-group rg-shipment-api-prod

Why matching a specific status code RANGE, rather than only exact 200, is worth calling out explicitly: a backend legitimately returning a 3xx redirect on its health endpoint would be marked UNHEALTHY by a probe expecting exactly 200 — a genuinely common, easy-to-miss misconfiguration that takes healthy backends out of rotation for a reason that has nothing to do with actual backend health.


URL Rewrite and Header Manipulation#

az network application-gateway rewrite-rule set create --gateway-name appgw-shipment-api \
  --resource-group rg-shipment-api-prod --name rewrite-set-security-headers

az network application-gateway rewrite-rule create --gateway-name appgw-shipment-api \
  --resource-group rg-shipment-api-prod --rule-set-name rewrite-set-security-headers \
  --name add-hsts --response-headers "Strict-Transport-Security=max-age=31536000"

# URL rewrite — rewriting a legacy path to a new backend route,
# without touching the backend application's own routing code
az network application-gateway rewrite-rule create --gateway-name appgw-shipment-api \
  --resource-group rg-shipment-api-prod --rule-set-name rewrite-set-security-headers \
  --name rewrite-legacy-path --request-url "/v1/{var_uri_path}" \
  --condition-variable "var_uri_path" --pattern "^/legacy/(.*)"

A genuinely useful, easy-to-overlook capability worth naming explicitly: injecting security response headers (HSTS, X-Content-Type-Options) at the gateway means every backend automatically gets them without each application team needing to remember to add them in their own code — a centralized, structurally enforced security baseline rather than a per-team convention.


TLS Termination and End-to-End TLS#

Diagram
ModeClient-to-gatewayGateway-to-backendBest fit
TLS termination onlyEncryptedUnencrypted HTTPBackend and gateway share a trusted, isolated network (still worth pairing with Part 4's VNet encryption)
End-to-end TLSEncryptedRe-encrypted, separate certificateCompliance requiring encryption at every hop, zero-trust network assumptions
az network application-gateway http-settings update --gateway-name appgw-shipment-api \
  --resource-group rg-shipment-api-prod --name settings-api \
  --protocol Https --host-name-from-backend-pool true

Why end-to-end TLS is worth treating as the safer default despite the added certificate management overhead, worth stating explicitly: TLS termination only trusts the internal network segment between the gateway and backend to be inherently safe — an assumption that doesn't hold under a genuine zero-trust security model (Part 12), where no network segment is assumed trusted by default.


Web Application Firewall on Application Gateway#

az network application-gateway waf-policy create --name waf-policy-shipment-api \
  --resource-group rg-shipment-api-prod

az network application-gateway waf-policy managed-rule rule-set add \
  --policy-name waf-policy-shipment-api --resource-group rg-shipment-api-prod \
  --type OWASP --version 3.2

The WAF_v2 SKU embeds the WAF engine directly into the request pipeline, evaluating every request against the OWASP Core Rule Set (currently 3.2/3.1) before it ever reaches the backend — Part 7 and Part 12 cover WAF policy tuning, custom rules, and bot management in full depth; this chapter's scope is knowing it attaches at the Application Gateway (and Front Door) layer specifically.

# Set the WAF to Prevention mode (block) rather than Detection mode
# (log only) once confident in the managed rule set's behavior for
# this specific application — Detection first, Prevention once tuned
az network application-gateway waf-policy update --name waf-policy-shipment-api \
  --resource-group rg-shipment-api-prod --set policySettings.mode=Prevention

Why starting in Detection mode before switching to Prevention is worth stating as a deliberate rollout sequence, not unnecessary caution: the OWASP Core Rule Set is tuned for a broad range of applications and can produce false positives against a specific application's legitimate traffic patterns — running in Detection mode first, reviewing what WOULD have been blocked, and tuning exclusions before switching to Prevention avoids a WAF rollout that blocks real customer traffic on day one.


Session Affinity and Multi-Site Hosting#

Two practical Application Gateway capabilities worth knowing precisely. Session affinity (cookie-based) ensures a client's subsequent requests land on the SAME backend instance that served their first request — necessary for a backend that keeps in-memory session state rather than externalizing it.

az network application-gateway http-settings update --gateway-name appgw-shipment-api \
  --resource-group rg-shipment-api-prod --name settings-api \
  --cookie-based-affinity Enabled

Worth stating as a real architectural tradeoff, not a free feature: session affinity undermines even load distribution across backends over time — a backend that happens to receive many "sticky" long-lived sessions early carries a disproportionate share of ongoing load compared to one that started with fewer. The stronger long-term fix, where the application allows it, is externalizing session state (to a cache like Redis, covered in Part 11) so affinity isn't needed at all — session affinity is a legitimate stopgap for an application not yet re-architected that way, not a permanent design goal.

Multi-site hosting lets ONE Application Gateway serve multiple distinct hostnames, each routed to a different backend pool via host-header-based listeners — a genuine cost and management consolidation versus provisioning a separate gateway per site.

az network application-gateway http-listener create --gateway-name appgw-shipment-api \
  --resource-group rg-shipment-api-prod --name listener-admin \
  --host-name admin.meridianfreight.com --frontend-port 443 --ssl-cert cert-shipment-api

Azure Front Door — Global Layer 7 Delivery#

Azure Front Door provides global, Layer 7 HTTP/HTTPS delivery — the natural fit for Meridian Freight's carrier-partner-facing shipment-api endpoint, which needs a single global entry point routing to the nearest healthy regional deployment.

Diagram

A genuinely important, current fact worth stating explicitly: Azure Front Door (classic) stopped accepting new profile creation on March 31, 2025, with full retirement scheduled for March 31, 2027 — any new design should use the current Standard/Premium tiers exclusively; an existing classic profile needs an active migration plan well before the retirement date, not a wait-and-see approach.


Front Door Tiers — Standard and Premium#

TierIncludesBest fit
StandardCDN caching, global load balancing, basic WAFContent delivery and global routing without advanced security needs
PremiumEverything in Standard, plus advanced WAF (bot protection, Private Link origin support)Anything needing Private Link-secured origins or advanced bot/security features

Why Premium is the correct tier for Meridian Freight's shipment-api specifically, worth stating the underlying reasoning rather than defaulting to cost-minimizing Standard: carrier-partner-facing APIs are a realistic target for credential-stuffing and bot-driven abuse, and Premium's advanced bot protection plus Private Link origin support (covered later in this chapter) directly address that threat model — Standard would leave a real security gap for this specific, externally-exposed, high-value endpoint.


Front Door Routing, Origins, and Endpoints#

az afd profile create --profile-name fd-meridian --resource-group rg-networking-prod --sku Premium_AzureFrontDoor

# Confirm the profile provisioned successfully before configuring endpoints
az afd profile show --profile-name fd-meridian --resource-group rg-networking-prod \
  --query provisioningState

az afd endpoint create --endpoint-name shipment-api --profile-name fd-meridian \
  --resource-group rg-networking-prod

az afd origin-group create --origin-group-name og-shipment-api --profile-name fd-meridian \
  --resource-group rg-networking-prod --probe-request-type GET --probe-path /healthz \
  --probe-protocol Https --probe-interval-in-seconds 30

az afd origin create --origin-group-name og-shipment-api --profile-name fd-meridian \
  --resource-group rg-networking-prod --origin-name origin-eastus \
  --host-name appgw-shipment-api.eastus.cloudapp.azure.com --priority 1 --weight 1000

# A second origin, lower priority, for the multi-region failover
# design Part 16 builds out in full
az afd origin create --origin-group-name og-shipment-api --profile-name fd-meridian \
  --resource-group rg-networking-prod --origin-name origin-westeurope \
  --host-name appgw-shipment-api.westeurope.cloudapp.azure.com --priority 2 --weight 1000

Front Door Caching#

az afd route create --endpoint-name shipment-api --profile-name fd-meridian \
  --resource-group rg-networking-prod --route-name route-static --origin-group og-shipment-api \
  --patterns-to-match "/static/*" --enable-caching true --query-string-caching-behavior IgnoreQueryString

# A separate route for the dynamic API, explicitly with caching disabled
az afd route create --endpoint-name shipment-api --profile-name fd-meridian \
  --resource-group rg-networking-prod --route-name route-api --origin-group og-shipment-api \
  --patterns-to-match "/api/*" --enable-caching false

Why caching applies naturally to /static/* but should generally be disabled for API routes serving dynamic, per-partner data — a real, common configuration mistake worth naming explicitly: caching a dynamic API response at the edge can serve one carrier partner's data to a completely different partner making a similarly-shaped request, a genuine data-leakage risk, not just a staleness inconvenience — caching rules need to be scoped deliberately per route pattern, never applied blanket across an entire endpoint.


Front Door Traffic Acceleration#

Front Door's edge network uses Anycast routing and Microsoft's own private global backbone between edge and origin — a request from a carrier partner in Singapore reaching a shipment-api origin in eastus travels the shortest path to the NEAREST Front Door edge over the public internet, then the rest of the way to the origin over Microsoft's private network, rather than the public internet the entire distance.

Why this matters concretely, worth stating the underlying reasoning: the public internet segment of the request — the genuinely unpredictable part, subject to congestion and suboptimal routing — is minimized to just the "last mile" to the nearest edge, with the long-haul portion running over Microsoft's own optimized, private backbone — a real, measurable latency improvement for geographically distant clients, not just a caching benefit.


Front Door Rules Engine — Rewrite and Redirect#

az afd rule-set create --rule-set-name rules-security --profile-name fd-meridian --resource-group rg-networking-prod

az afd rule create --rule-set-name rules-security --profile-name fd-meridian --resource-group rg-networking-prod \
  --rule-name redirect-http-to-https --order 1 \
  --match-variable RequestScheme --operator Equal --match-values HTTP \
  --action-name UrlRedirect --redirect-protocol Https

# Associate the rule set with the actual route it should apply to
az afd route update --endpoint-name shipment-api --profile-name fd-meridian \
  --resource-group rg-networking-prod --route-name route-api --rule-sets rules-security

The Rules Engine lets URL rewrite, redirect, and header manipulation logic run at the edge, before a request ever reaches an origin — genuinely useful for enforcing HTTPS-only access, or restructuring a legacy URL scheme without touching backend application code.

Rate Limiting at the Edge#

A Premium-tier WAF capability worth naming explicitly, since it directly protects shipment-api's carrier-partner-facing endpoint against the abuse pattern this chapter's tier recommendation already flagged: rate limiting blocks a client exceeding a defined request threshold, enforced at Front Door's edge before the request ever reaches an origin.

az network front-door waf-policy rule create --policy-name waf-policy-fd-shipment-api \
  --resource-group rg-networking-prod --name rate-limit-partners \
  --rule-type RateLimitRule --rate-limit-duration 1 --rate-limit-threshold 100 \
  --priority 1 --action Block

Why enforcing this at the EDGE, rather than in application code, matters concretely: a request blocked by Front Door's rate limiting never consumes any origin compute at all — the origin (and any backend WAF at the Application Gateway layer) never even sees the excess requests, which is both a stronger security posture and a genuine cost saving compared to letting abusive traffic reach the origin before being rejected.

Managed Rule Set Exclusions#

A genuinely common tuning need worth covering explicitly: a specific, known-legitimate request pattern (a large file upload's multipart form field, for instance) can trigger a false positive against a specific OWASP rule.

az network front-door waf-policy managed-rule-set add --policy-name waf-policy-fd-shipment-api \
  --resource-group rg-networking-prod --type Microsoft_DefaultRuleSet --version 2.1 \
  --rule-group-override "REQUEST-942-APPLICATION-ATTACK-SQLI" \
  --exclusion "RequestBodyPostArgNames:document_content"

Why a targeted exclusion for one specific field is worth preferring over disabling the entire rule group, worth stating the underlying reasoning: disabling a whole SQL-injection rule group to fix one false positive on one form field removes real protection for every OTHER field and request that rule group would otherwise still correctly defend — a scoped exclusion fixes the specific false positive without weakening the broader protection.


A Premium-tier-only capability worth calling out explicitly, since it directly justifies the tier recommendation made earlier: an origin can be reached via Private Link rather than a public endpoint, meaning shipment-api's actual backend never needs a public IP at all.

az afd origin create --origin-group-name og-shipment-api --profile-name fd-meridian \
  --resource-group rg-networking-prod --origin-name origin-private \
  --enabled-state Enabled --private-link-resource "<app-gateway-resource-id>" \
  --private-link-location eastus --private-link-request-message "Front Door origin request"

Why this is worth treating as a meaningful security upgrade, not just a networking convenience: an origin with NO public endpoint at all cannot be attacked directly, bypassing Front Door entirely — a real, documented attack pattern against organizations that expose both a CDN/Front Door AND a public origin, where an attacker simply targets the origin's public IP directly, skipping Front Door's WAF and rate limiting altogether.

# Confirm the origin's Application Gateway actually has NO public IP
# attached once the Private Link migration is complete
az network application-gateway show --name appgw-shipment-api --resource-group rg-shipment-api-prod \
  --query "frontendIPConfigurations[].publicIPAddress"

Custom Domains and Managed Certificates on Front Door#

az afd custom-domain create --custom-domain-name shipment-api-domain --profile-name fd-meridian \
  --resource-group rg-networking-prod --host-name shipment-api.meridianfreight.com \
  --certificate-type ManagedCertificate

az afd route update --endpoint-name shipment-api --profile-name fd-meridian \
  --resource-group rg-networking-prod --route-name route-api \
  --custom-domains shipment-api-domain

Why Front Door's managed certificates are worth defaulting to over a manually procured and uploaded certificate, worth stating explicitly: Microsoft handles issuance AND automatic renewal entirely — eliminating an entire class of "the certificate expired and nobody noticed" outage that has caused real, documented incidents across the industry when certificate renewal was left as a manual, easy-to-forget process. A custom (bring-your-own) certificate remains supported for organizations with a specific certificate authority requirement, but the managed option removes real, recurring operational risk for the common case.


Azure Traffic Manager — DNS-Based Global Routing#

Traffic Manager operates at the DNS level — it doesn't proxy traffic at all, instead returning a DNS response pointing the client directly at the most appropriate endpoint, based on a chosen routing method.

az network traffic-manager profile create --name tm-meridian --resource-group rg-networking-prod \
  --routing-method Priority --unique-dns-name meridian-freight-global \
  --ttl 30 --protocol HTTPS --port 443 --path /healthz

# Add endpoints with explicit priority order
az network traffic-manager endpoint create --profile-name tm-meridian --resource-group rg-networking-prod \
  --type azureEndpoints --name endpoint-eastus --target-resource-id "<eastus-endpoint-id>" --priority 1

Why Traffic Manager is worth knowing as genuinely distinct from Front Door, not a redundant older alternative, worth stating precisely: because Traffic Manager only returns a DNS answer and never proxies traffic itself, it works for ANY protocol — not just HTTP/HTTPS — making it the right choice for a non-web TCP/UDP service needing global routing, which Front Door (HTTP/HTTPS-only) cannot serve at all.


Traffic Manager Routing Methods#

MethodBehavior
PriorityPrimary endpoint always preferred; failover to the next priority only if primary is unhealthy
WeightedDistributes traffic proportionally across endpoints by assigned weight
PerformanceRoutes to the endpoint with lowest network latency from the client's location
GeographicRoutes based on the client's geographic origin — for data-residency-driven routing
MultivalueReturns multiple healthy endpoint IPs in one DNS response, letting the client choose
SubnetRoutes based on the client's IP address range, mapped to specific endpoints

Nested Traffic Manager Profiles#

A genuinely powerful capability worth knowing exists: Traffic Manager profiles can be nested, combining routing methods for a compound policy no single method achieves alone — the classic pattern is an outer Weighted or Priority profile whose "endpoints" are themselves other Traffic Manager profiles using Performance routing.

Diagram
az network traffic-manager endpoint create --profile-name tm-meridian-outer \
  --resource-group rg-networking-prod --type nestedEndpoints --name endpoint-na-region \
  --target-resource-id "<nested-profile-resource-id>" --priority 1 --min-child-endpoints 1

Why this matters concretely for a genuinely global, multi-region design: it lets Meridian Freight route by lowest latency WITHIN a geographic tier (Performance routing across North American regions) while still enforcing a hard geographic failover boundary at the outer level (Priority routing falling back to Europe only if the entire North American tier is unhealthy) — a compound policy neither routing method alone could express, and the exact pattern Part 16's full multi-region design builds on.


Application Gateway for Containers — AKS-Native Ingress#

A brief, forward-looking preview worth flagging here since it's a genuinely new application-delivery mechanism, ahead of Part 10's full AKS coverage: Application Gateway for Containers (AGC) is a managed Layer 7 ingress specifically for AKS, routing HTTP/HTTPS/gRPC (and AI inference) traffic into a cluster while Azure operates the actual data plane outside the cluster itself.

Diagram

A genuinely important, current fact worth stating explicitly: AGC is the direct evolution of the older Application Gateway Ingress Controller (AGIC), and AKS's managed NGINX ingress add-on (based on the older Ingress API) stops receiving Azure support after November 2026 — a design being built today for AKS ingress should target the Kubernetes Gateway API standard, which AGC implements, rather than the legacy Ingress API pattern many existing tutorials and examples still show. Part 10 covers AKS itself and this ingress choice in full depth; this chapter's job is flagging that "how traffic reaches an AKS cluster" is itself a genuine application-delivery decision, not something to bolt on as an afterthought once the cluster already exists.


Choosing Between Load Balancer, Application Gateway, Front Door, and Traffic Manager#

Diagram
NeedRecommendation
Distribute TCP/UDP traffic across VMs in one regionAzure Load Balancer
URL/host-based routing, WAF, within one regionApplication Gateway
Global HTTP delivery, caching, WAF, single entry pointAzure Front Door
Global routing for a non-HTTP protocol, or DNS-level failoverTraffic Manager
Global HTTP delivery AND regional path-based routingFront Door in front of regional Application Gateways (the combined pattern this chapter's bootstrap uses)

A Full Worked Application Delivery Bootstrap for Meridian Freight#

# 1. Internal Load Balancer for shipment-api's app-to-database tier traffic
az network lb create --name lb-shipment-api-internal --resource-group rg-shipment-api-prod \
  --sku Standard --frontend-ip-name fe-internal --private-ip-address 10.1.1.100 \
  --vnet-name vnet-shipment-api --subnet snet-app

# 2. Regional Application Gateway with WAF for path-based routing and TLS termination
az network application-gateway create --name appgw-shipment-api --resource-group rg-shipment-api-prod \
  --sku WAF_v2 --min-capacity 2 --max-capacity 10 --zones 1 2 3 \
  --vnet-name vnet-shipment-api --subnet snet-appgw --public-ip-address pip-appgw-shipment-api

# 3. Global Front Door Premium in front of the regional Application Gateway,
#    with the origin secured via Private Link
az afd profile create --profile-name fd-meridian --resource-group rg-networking-prod --sku Premium_AzureFrontDoor
az afd origin create --origin-group-name og-shipment-api --profile-name fd-meridian \
  --resource-group rg-networking-prod --origin-name origin-eastus \
  --private-link-resource "<appgw-resource-id>" --private-link-location eastus

# 4. Force HTTPS via the Rules Engine
az afd rule create --rule-set-name rules-security --profile-name fd-meridian --resource-group rg-networking-prod \
  --rule-name redirect-http-to-https --match-variable RequestScheme --operator Equal \
  --match-values HTTP --action-name UrlRedirect --redirect-protocol Https

Part 6 CLI Cheat Sheet#

AreaCommandPurpose
Load Balanceraz network lb create --sku StandardCreate a Standard SKU Load Balancer
LB rulesaz network lb rule createCreate a load balancing rule
LB probesaz network lb probe createCreate a health probe
Cross-regionaz network cross-region-lb createCreate a global Layer 4 load balancer
App Gatewayaz network application-gateway create --sku WAF_v2Create a v2 Application Gateway with WAF
App Gatewayaz network application-gateway rewrite-rule set createConfigure header/URL rewrite
App Gatewayaz network application-gateway waf-policy createCreate a WAF policy
Front Dooraz afd profile create --sku Premium_AzureFrontDoorCreate a Premium Front Door profile
Front Dooraz afd origin create --private-link-resourceSecure an origin with Private Link
Front Dooraz afd route create --enable-caching trueConfigure edge caching for a route
Traffic Manageraz network traffic-manager profile createCreate a DNS-based global routing profile
Traffic Manageraz network traffic-manager endpoint create --type nestedEndpointsNest one profile inside another for compound routing
WAF rate limitaz network front-door waf-policy rule create --rule-type RateLimitRuleBlock clients exceeding a request threshold at the edge
WAF exclusionaz network front-door waf-policy managed-rule-set add --exclusionScope a managed rule exclusion to a specific field
WAF modeaz network application-gateway waf-policy update --set policySettings.modeSwitch between Detection and Prevention
Gateway LBaz network lb create --sku GatewayCreate a Gateway Load Balancer for NVA insertion
Session affinityaz network application-gateway http-settings update --cookie-based-affinityEnable sticky sessions
Multi-siteaz network application-gateway http-listener create --host-nameHost multiple sites on one gateway
Custom domainaz afd custom-domain create --certificate-type ManagedCertificateAdd a custom domain with an auto-renewing certificate

Common Mistakes and Interview Traps#

MistakeWhy It's WrongFix
Referencing Basic SKU Load Balancer in a new designRetired September 2025 — unsupported, no SLAUse Standard SKU exclusively for any new deployment
Assuming a Load Balancer and an Application Gateway share health probe configuration in a combined designEach layer's health probes are entirely independentConfigure and verify health probes separately at each layer
Relying on default Load Balancer outbound rules for a high-outbound-connection-volume workloadCan hit SNAT port exhaustion under real loadUse NAT Gateway (Part 4) for outbound connectivity instead
Creating a new Azure Front Door (classic) profileClassic stopped accepting new profiles March 2025, full retirement March 2027Use the current Standard/Premium tiers for any new profile
Caching a dynamic, per-partner API response at the Front Door edgeCan serve one partner's data to a different partner making a similarly-shaped requestScope caching rules deliberately per route pattern, never applied blanket
Exposing an origin with a public IP behind Front DoorAn attacker can bypass Front Door's WAF and rate limiting by hitting the origin directlyUse Private Link (Premium tier) to give the origin no public endpoint at all
Choosing Front Door for a non-HTTP protocol's global routing needsFront Door is HTTP/HTTPS-onlyUse Traffic Manager instead for any non-HTTP protocol
Matching only exact HTTP 200 in an Application Gateway health probeA legitimate 3xx response marks a healthy backend as unhealthyMatch a status code range appropriate to the actual healthy response set
Enabling session affinity as a permanent architectural defaultUndermines even load distribution across backends over timeTreat it as a stopgap; externalize session state (Part 11) when possible
Manually procuring and uploading a Front Door certificate without a renewal processA missed renewal causes a real, entirely avoidable outageDefault to Front Door's managed certificates, which renew automatically
Building new AKS ingress on the legacy Ingress API / managed NGINX add-onLoses Azure support after November 2026Target the Kubernetes Gateway API via Application Gateway for Containers for new designs
Switching a new WAF policy directly to Prevention mode without a Detection-mode tuning periodCan block legitimate customer traffic on day one from an untuned rule setRun in Detection mode first, review findings, tune exclusions, then switch to Prevention
Disabling an entire WAF rule group to fix one false positive on one specific fieldRemoves real protection for every other field and request that rule group would otherwise defendUse a scoped exclusion targeting only the specific field causing the false positive
Enforcing rate limits only in application code, not at the edgeAbusive requests still consume backend/origin compute before being rejectedEnforce rate limiting at Front Door's edge so blocked requests never reach the origin at all

Worked Practice Problems#

Problem 1: Meridian Freight's docs-processor VMSS, processing a high volume of outbound calls to an external document-classification API, begins experiencing intermittent outbound connection failures under peak load. The team initially suspects the external API is unreliable. What's the more likely Azure-side cause, and how would you confirm it?

Answer: SNAT port exhaustion on the Standard Load Balancer's default outbound rules is the more likely cause — each backend instance has a limited pool of outbound ports allocated by the Load Balancer's outbound rule configuration, and a high volume of concurrent outbound connections per instance can exhaust that pool, causing new outbound connections to fail intermittently in a pattern that looks identical to an unreliable external API. Confirming it involves checking Azure Monitor metrics for SNATConnectionCount and AllocatedSNATPorts against actual usage — a fix is migrating to NAT Gateway (Part 4), which provides a substantially larger outbound port pool per subnet than Load Balancer outbound rules practically allocate.

Problem 2: A team configures Azure Front Door Standard in front of shipment-api's Application Gateway origin, keeping the Application Gateway's public IP reachable directly as a "just in case Front Door has an issue" fallback. A security audit later finds the WAF protections on Front Door are being bypassed by traffic hitting the Application Gateway's public IP directly. What's the design flaw, and what's the fix?

Answer: Keeping the origin's public IP directly reachable defeats the security purpose of putting Front Door and its WAF in front of it at all — an attacker (or any client) can simply target the Application Gateway's public IP directly, completely bypassing Front Door's WAF, rate limiting, and bot protection. The "just in case Front Door has an issue" reasoning also doesn't hold up: Front Door is a highly available, globally distributed service, and a direct-access fallback path introduces a permanent, unconditional security gap in exchange for hedging against a rare failure mode. The fix is upgrading to Front Door Premium and securing the origin via Private Link, removing its public IP entirely — the origin becomes reachable ONLY through Front Door, closing the bypass path structurally rather than relying on anyone remembering not to use the direct IP.

Problem 3: Meridian Freight enables Front Door caching on all routes, including /api/rates, which returns carrier-specific pricing data unique to each authenticated partner. Shortly after, a partner reports seeing pricing data that doesn't match their account. What happened, and what's the fix?

Answer: Caching was applied to a dynamic, per-partner API route without excluding it — Front Door's edge cache served a cached response (from a different partner's earlier request with a similarly-shaped URL) to this partner, since the cache didn't distinguish between partners for that route. This is a genuine data-leakage incident, not just a staleness bug. The fix is explicitly excluding /api/rates (and any other route returning caller-specific dynamic data) from caching, reserving caching for genuinely cacheable, non-personalized content like /static/* — caching rules need to be scoped deliberately per route pattern based on whether the response is actually safe to share across different callers, never applied as a blanket "cache everything" default.

Problem 4: An architect recommends Traffic Manager for globally routing Meridian Freight's shipment-api HTTP traffic, citing "it's simpler and cheaper than Front Door." A colleague objects that this loses caching and WAF capabilities. Evaluate both positions.

Answer: The colleague's objection is well-founded for this specific use case: Traffic Manager only returns a DNS response pointing a client at an endpoint — it never proxies or inspects traffic itself, so it provides no caching, no WAF, and no Layer 7 routing capability at all, unlike Front Door. For an HTTP API that benefits from edge caching (static assets) and needs WAF protection against a real bot/credential-stuffing threat model (as this chapter's earlier Front Door tier discussion established), Traffic Manager's simplicity comes at the cost of capabilities shipment-api genuinely needs. The architect's "simpler and cheaper" framing isn't wrong in isolation — Traffic Manager IS simpler and cheaper — but the right tool choice depends on the specific workload's requirements, not on defaulting to the cheaper option: Traffic Manager is the right recommendation for a NON-HTTP protocol needing global routing, but not for an HTTP API needing edge caching and WAF, which is exactly Front Door's purpose.

Problem 5: A platform team migrates an Application Gateway from v1 to v2 and is surprised to find the new deployment automatically spans three Availability Zones without any additional Traffic Manager or multi-instance configuration they previously needed for v1. What changed, and why does this matter for the migration's cost and complexity?

Answer: Application Gateway v2 SKUs span multiple Availability Zones by default, a genuine architectural improvement over v1, which required manually provisioning separate per-zone Application Gateway instances behind a Traffic Manager to achieve equivalent zone redundancy. This matters concretely for the migration's cost and complexity: the v1 design's Traffic Manager and multiple gateway instances can likely be decommissioned entirely, since v2's built-in zone redundancy provides the same protection natively — a genuine simplification, not just a version bump, reducing both the resource count and the operational surface area the platform team needs to maintain going forward.

Problem 6: Meridian Freight's driver-portal backend keeps session state in local instance memory rather than an external cache, so the platform team enables session affinity on its Application Gateway to keep each user's requests routed to the same instance. Six months later, as the fleet scales to handle growth, the team notices load is distributed unevenly across instances, with a few instances consistently running hotter than others. What's the underlying cause, and what's the more durable long-term fix?

Answer: Session affinity is the underlying cause — once a client's session is pinned to a specific backend instance, it stays pinned for the session's full lifetime regardless of how load shifts afterward, so instances that happened to receive many long-lived sessions early accumulate a disproportionate share of ongoing load compared to instances that started more lightly loaded. This is an inherent tradeoff of session affinity, not a misconfiguration to tune away. The more durable long-term fix is externalizing driver-portal's session state to a shared cache (Redis, covered in Part 11) so any instance can serve any request statelessly — at which point session affinity can be disabled entirely, restoring genuinely even load distribution across the fleet regardless of how long any individual session lives.

Problem 7: A team manually procures a TLS certificate for shipment-api's Front Door custom domain from a third-party certificate authority, uploads it, and considers the setup complete. Eleven months later, the certificate expires unexpectedly during a period when the engineer who originally set it up is on leave, causing a multi-hour outage. What process gap caused this, and what would Front Door's managed certificate option have done differently?

Answer: The process gap is relying on a manually procured certificate with no automated renewal tracking — nothing forced anyone to notice the approaching expiration date, and the one person most likely to remember was unavailable when it mattered. Front Door's managed certificate option would have prevented this entirely: Microsoft handles both issuance and automatic renewal for the domain, with no manual renewal step for any engineer to forget or be unavailable for. A custom certificate remains a legitimate choice when an organization has a specific certificate authority requirement (an internal PKI, a specific compliance mandate naming an approved CA), but absent that requirement, the managed certificate option removes an entire class of real, recurring operational risk.

Problem 8: A team planning a new AKS deployment for a future Meridian Freight service finds several existing tutorials and Helm charts online recommending the managed NGINX ingress add-on, and adopts it without further research since "it's the standard approach." What risk does this introduce given the current AKS ingress landscape, and what should the team target instead?

Answer: The managed NGINX ingress add-on is based on the legacy Kubernetes Ingress API, and Azure support for it ends after November 2026 — adopting it for a NEW deployment being built today means building on a path that loses support relatively soon after going live, likely forcing a disruptive ingress migration not long after launch. The team should target Application Gateway for Containers instead, which implements the Kubernetes Gateway API — the direction AKS is aligning toward long-term, consistent with upstream Kubernetes' own move away from the older Ingress API. The lesson here generalizes beyond this specific case: "recommended in existing tutorials" isn't the same as "current guidance," especially in a fast-moving area like Kubernetes ingress, and confirming a recommendation's currency against official, dated sources is worth the extra step before committing a new production design to it.

Problem 9: Meridian Freight switches a newly configured WAF policy on shipment-api's Application Gateway directly to Prevention mode immediately after creating it, reasoning that "we want protection active from day one." Within hours, several legitimate carrier partners report their document-upload requests are being rejected with 403 errors. What process step was skipped, and what should the team do now?

Answer: The team skipped the Detection-mode tuning period this chapter recommends — switching straight to Prevention mode enforces the full, untuned OWASP Core Rule Set against real traffic immediately, and a legitimate request pattern (likely the multipart form data in the document upload, a common false-positive trigger) is being blocked as if it were an attack. The immediate fix is switching back to Detection mode to stop blocking legitimate traffic while investigating, then reviewing the WAF logs to identify exactly which rule triggered on the upload requests, adding a scoped exclusion for that specific field (not disabling the whole rule group), and only then switching back to Prevention mode once confirmed the exclusion resolves the false positive without meaningfully weakening protection elsewhere.

Problem 10: Meridian Freight's global expansion plan calls for the lowest-latency routing available within North America and within Europe separately, but with a hard requirement that European traffic NEVER fails over to North American infrastructure (a data-residency requirement) even during a full North American outage, and vice versa. A single flat Performance-routing Traffic Manager profile is proposed. Why doesn't this satisfy the requirement, and what's the correct design?

Answer: A single flat Performance-routing profile routes purely by lowest measured latency across ALL configured endpoints — during a North American outage, if a European endpoint happened to offer acceptable (if not optimal) latency to some North American clients, Performance routing could route traffic there, violating the hard data-residency requirement that European and North American traffic must never cross that boundary regardless of health status. The correct design uses two SEPARATE Traffic Manager profiles, one per geographic tier, each internally using Performance routing among endpoints within that tier only — with no nesting or failover relationship between the two tiers at all, since the requirement explicitly forbids cross-tier failover. This is a case where nested profiles (this chapter's earlier pattern) would be the WRONG tool — nesting is for compound routing where failover BETWEEN tiers is desired; a hard data-residency boundary calls for genuinely separate, non-interacting profiles instead.


Summary and What's Next#

  • Azure Load Balancer (Layer 4), Application Gateway (Layer 7, regional), Front Door (Layer 7, global), and Traffic Manager (DNS-level, protocol-agnostic) solve genuinely distinct problems — choosing among them depends on layer awareness and scope needs, not a single "best" option.
  • Basic Load Balancer was retired in September 2025 — Standard is the only current, supported SKU for regular load balancing.
  • SNAT port exhaustion is a real failure mode for Load Balancer outbound rules under high connection volume — NAT Gateway (Part 4) is the current recommended fix.
  • Application Gateway v2 spans multiple zones by default and autoscales, removing v1's need for manual per-zone instances behind a Traffic Manager.
  • Azure Front Door (classic) is being retired (full retirement March 2027) — new designs use the current Standard/Premium tiers exclusively.
  • Front Door Premium's Private Link origin support closes a real security gap — an origin with no public IP cannot be attacked by bypassing Front Door's WAF directly.
  • Edge caching must be scoped deliberately per route — caching a dynamic, per-caller API response is a genuine data-leakage risk, not just a staleness inconvenience.
  • Session affinity is a legitimate stopgap, not a permanent architectural goal — it undermines even load distribution over time, and externalizing session state removes the need for it entirely.
  • Front Door's managed certificates remove an entire class of "the certificate expired and nobody noticed" outage by handling issuance and renewal automatically.
  • AKS ingress is moving to the Kubernetes Gateway API via Application Gateway for Containers — the legacy Ingress API/managed NGINX add-on path loses support after November 2026, worth knowing before Part 10's full AKS treatment.
  • Rate limiting and scoped WAF exclusions belong at the edge, tuned deliberately — Detection mode before Prevention, and a targeted exclusion rather than disabling a whole rule group.
  • Nested Traffic Manager profiles express compound routing policies a single profile cannot — but a hard data-residency boundary calls for genuinely separate, non-nested profiles instead.

Continue to Part 7 (07-networking-private-access-and-security.md) for the private connectivity and network security layer — Private Link, NSGs, Azure Firewall, and WAF policy in depth — that locks down everything this chapter's application delivery services expose.