Questions
Design a global application with Azure Front Door for traffic management and WAF protection.
The Scenario
Your e-commerce platform needs global reach:
Current State:
├── Single region: East US
├── Average latency: 200ms (US), 400ms (Europe), 600ms (Asia)
├── No CDN for static assets
├── Single origin = single point of failure
└── Compliance: GDPR requires EU data to stay in EU
Business Requirements:
├── < 100ms latency globally for static content
├── < 200ms latency for API responses
├── 99.99% availability (multi-region)
├── Automatic failover between regions
└── WAF protection against OWASP Top 10
The Challenge
Design a multi-region architecture using Azure Front Door Premium with intelligent traffic routing, WAF protection, and private link to origins.
A junior engineer might deploy Traffic Manager for HTTP workloads, use Application Gateway per region without global load balancing, configure round-robin routing ignoring latency, or expose backend services publicly. These approaches add latency, don't provide edge caching, or create security risks.
A senior engineer uses Azure Front Door Premium for global HTTP load balancing with built-in CDN, configures latency-based routing, enables WAF with managed rule sets, uses Private Link for secure origin connections, and implements proper caching rules.
Architecture Overview
Azure Front Door Premium
┌──────────────────────────────────────┐
│ Global Entry Point │
│ app.contoso.com (Anycast) │
│ │
│ ┌─────────────────────────────────┐ │
│ │ WAF Policy │ │
│ │ OWASP 3.2 + Custom Rules │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ CDN / Edge Caching │ │
│ │ Static: /images/*, /css/* │ │
│ └─────────────────────────────────┘ │
└──────────────────────────────────────┘
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ East US │ │ West Europe │ │ Southeast Asia│
│ │ │ │ │ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │App Service │ │ │ │App Service │ │ │ │App Service │ │
│ │(Private EP) │ │ │ │(Private EP) │ │ │ │(Private EP) │ │
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
│ │ │ │ │ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ Azure SQL │ │ │ │ Azure SQL │ │ │ │ Azure SQL │ │
│ │ (Read Rep) │ │ │ │ (Primary) │ │ │ │ (Read Rep) │ │
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
└─────────────────┘ └─────────────────┘ └─────────────────┘Step 1: Create Front Door Profile
// Azure Front Door Premium with WAF and Private Link
resource frontDoorProfile 'Microsoft.Cdn/profiles@2023-05-01' = {
name: 'fd-contoso-global'
location: 'global'
sku: {
name: 'Premium_AzureFrontDoor' // Required for Private Link and WAF
}
}
// Custom domain endpoint
resource frontDoorEndpoint 'Microsoft.Cdn/profiles/afdEndpoints@2023-05-01' = {
parent: frontDoorProfile
name: 'contoso-endpoint'
location: 'global'
properties: {
enabledState: 'Enabled'
}
}
// Custom domain
resource customDomain 'Microsoft.Cdn/profiles/customDomains@2023-05-01' = {
parent: frontDoorProfile
name: 'app-contoso-com'
properties: {
hostName: 'app.contoso.com'
tlsSettings: {
certificateType: 'ManagedCertificate'
minimumTlsVersion: 'TLS12'
}
}
}Step 2: Configure Origin Groups with Health Probes
// Origin group for multi-region backends
resource originGroup 'Microsoft.Cdn/profiles/originGroups@2023-05-01' = {
parent: frontDoorProfile
name: 'app-origin-group'
properties: {
loadBalancingSettings: {
sampleSize: 4
successfulSamplesRequired: 2
additionalLatencyInMilliseconds: 50 // Latency tolerance
}
healthProbeSettings: {
probePath: '/health'
probeRequestType: 'GET'
probeProtocol: 'Https'
probeIntervalInSeconds: 30
}
sessionAffinityState: 'Disabled' // Enable if needed for stateful apps
}
}
// East US origin (App Service with Private Link)
resource originEastUS 'Microsoft.Cdn/profiles/originGroups/origins@2023-05-01' = {
parent: originGroup
name: 'origin-eastus'
properties: {
hostName: 'app-eastus.azurewebsites.net'
httpPort: 80
httpsPort: 443
originHostHeader: 'app-eastus.azurewebsites.net'
priority: 1
weight: 1000
enabledState: 'Enabled'
sharedPrivateLinkResource: {
privateLink: {
id: appServiceEastUS.id
}
groupId: 'sites'
privateLinkLocation: 'eastus'
requestMessage: 'Front Door Private Link'
}
}
}
// West Europe origin
resource originWestEurope 'Microsoft.Cdn/profiles/originGroups/origins@2023-05-01' = {
parent: originGroup
name: 'origin-westeurope'
properties: {
hostName: 'app-westeurope.azurewebsites.net'
httpPort: 80
httpsPort: 443
originHostHeader: 'app-westeurope.azurewebsites.net'
priority: 1
weight: 1000
enabledState: 'Enabled'
sharedPrivateLinkResource: {
privateLink: {
id: appServiceWestEurope.id
}
groupId: 'sites'
privateLinkLocation: 'westeurope'
requestMessage: 'Front Door Private Link'
}
}
}
// Southeast Asia origin
resource originSEAsia 'Microsoft.Cdn/profiles/originGroups/origins@2023-05-01' = {
parent: originGroup
name: 'origin-seasia'
properties: {
hostName: 'app-seasia.azurewebsites.net'
httpPort: 80
httpsPort: 443
originHostHeader: 'app-seasia.azurewebsites.net'
priority: 1
weight: 1000
enabledState: 'Enabled'
sharedPrivateLinkResource: {
privateLink: {
id: appServiceSEAsia.id
}
groupId: 'sites'
privateLinkLocation: 'southeastasia'
requestMessage: 'Front Door Private Link'
}
}
}Step 3: Configure Routes with Caching
// Static content route with caching
resource staticRoute 'Microsoft.Cdn/profiles/afdEndpoints/routes@2023-05-01' = {
parent: frontDoorEndpoint
name: 'static-route'
properties: {
customDomains: [
{
id: customDomain.id
}
]
originGroup: {
id: originGroup.id
}
supportedProtocols: ['Https']
patternsToMatch: [
'/images/*'
'/css/*'
'/js/*'
'/fonts/*'
]
forwardingProtocol: 'HttpsOnly'
linkToDefaultDomain: 'Enabled'
httpsRedirect: 'Enabled'
cacheConfiguration: {
compressionSettings: {
isCompressionEnabled: true
contentTypesToCompress: [
'text/html'
'text/css'
'application/javascript'
'application/json'
'image/svg+xml'
]
}
queryStringCachingBehavior: 'IgnoreQueryString'
cacheBehavior: 'OverrideAlways'
cacheDuration: 'P7D' // 7 days for static assets
}
}
}
// API route without caching
resource apiRoute 'Microsoft.Cdn/profiles/afdEndpoints/routes@2023-05-01' = {
parent: frontDoorEndpoint
name: 'api-route'
properties: {
customDomains: [
{
id: customDomain.id
}
]
originGroup: {
id: originGroup.id
}
supportedProtocols: ['Https']
patternsToMatch: ['/api/*']
forwardingProtocol: 'HttpsOnly'
linkToDefaultDomain: 'Enabled'
httpsRedirect: 'Enabled'
cacheConfiguration: {
queryStringCachingBehavior: 'UseQueryString'
cacheBehavior: 'HonorOrigin' // Respect Cache-Control headers
}
}
}
// Default route
resource defaultRoute 'Microsoft.Cdn/profiles/afdEndpoints/routes@2023-05-01' = {
parent: frontDoorEndpoint
name: 'default-route'
properties: {
customDomains: [
{
id: customDomain.id
}
]
originGroup: {
id: originGroup.id
}
supportedProtocols: ['Https']
patternsToMatch: ['/*']
forwardingProtocol: 'HttpsOnly'
linkToDefaultDomain: 'Enabled'
httpsRedirect: 'Enabled'
}
}Step 4: Configure WAF Policy
// WAF Policy with managed rules
resource wafPolicy 'Microsoft.Network/FrontDoorWebApplicationFirewallPolicies@2022-05-01' = {
name: 'waf-contoso-global'
location: 'global'
sku: {
name: 'Premium_AzureFrontDoor'
}
properties: {
policySettings: {
mode: 'Prevention' // Block attacks (use Detection for testing)
requestBodyCheck: 'Enabled'
maxRequestBodySizeInKb: 128
}
managedRules: {
managedRuleSets: [
{
ruleSetType: 'Microsoft_DefaultRuleSet'
ruleSetVersion: '2.1'
ruleGroupOverrides: []
}
{
ruleSetType: 'Microsoft_BotManagerRuleSet'
ruleSetVersion: '1.0'
}
]
}
customRules: {
rules: [
{
name: 'BlockBadBots'
priority: 1
ruleType: 'MatchRule'
action: 'Block'
matchConditions: [
{
matchVariable: 'RequestHeaders'
selector: 'User-Agent'
operator: 'Contains'
matchValue: ['bot', 'spider', 'crawler']
transforms: ['Lowercase']
}
]
}
{
name: 'RateLimitByIP'
priority: 2
ruleType: 'RateLimitRule'
rateLimitThreshold: 1000
rateLimitDurationInMinutes: 1
action: 'Block'
matchConditions: [
{
matchVariable: 'RequestUri'
operator: 'Contains'
matchValue: ['/api/']
}
]
}
{
name: 'GeoBlockRule'
priority: 3
ruleType: 'MatchRule'
action: 'Block'
matchConditions: [
{
matchVariable: 'RemoteAddr'
operator: 'GeoMatch'
matchValue: ['CN', 'RU'] // Block specific countries
}
]
}
]
}
}
}
// Associate WAF with Front Door security policy
resource securityPolicy 'Microsoft.Cdn/profiles/securityPolicies@2023-05-01' = {
parent: frontDoorProfile
name: 'waf-security-policy'
properties: {
parameters: {
type: 'WebApplicationFirewall'
wafPolicy: {
id: wafPolicy.id
}
associations: [
{
domains: [
{
id: customDomain.id
}
]
patternsToMatch: ['/*']
}
]
}
}
}Step 5: Geo-Routing for Compliance
// Rule set for geo-based routing (GDPR compliance)
resource ruleSet 'Microsoft.Cdn/profiles/ruleSets@2023-05-01' = {
parent: frontDoorProfile
name: 'geo-routing-rules'
}
resource euRoutingRule 'Microsoft.Cdn/profiles/ruleSets/rules@2023-05-01' = {
parent: ruleSet
name: 'route-eu-to-eu-origin'
properties: {
order: 1
conditions: [
{
name: 'RemoteAddress'
parameters: {
typeName: 'DeliveryRuleRemoteAddressConditionParameters'
operator: 'GeoMatch'
matchValues: [
'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI',
'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU',
'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE'
]
}
}
]
actions: [
{
name: 'RouteConfigurationOverride'
parameters: {
typeName: 'DeliveryRuleRouteConfigurationOverrideActionParameters'
originGroupOverride: {
originGroup: {
id: euOriginGroup.id // EU-only origin group
}
}
}
}
]
}
}Step 6: Monitor and Alert
// Diagnostic settings
resource frontDoorDiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
name: 'fd-diagnostics'
scope: frontDoorProfile
properties: {
workspaceId: logAnalyticsWorkspace.id
logs: [
{
category: 'FrontDoorAccessLog'
enabled: true
}
{
category: 'FrontDoorHealthProbeLog'
enabled: true
}
{
category: 'FrontDoorWebApplicationFirewallLog'
enabled: true
}
]
metrics: [
{
category: 'AllMetrics'
enabled: true
}
]
}
}
// Alert on origin health
resource originHealthAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = {
name: 'fd-origin-health-alert'
location: 'global'
properties: {
description: 'Alert when origin health drops below threshold'
severity: 1
enabled: true
scopes: [frontDoorProfile.id]
evaluationFrequency: 'PT1M'
windowSize: 'PT5M'
criteria: {
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
allOf: [
{
name: 'OriginHealthPercentage'
metricName: 'OriginHealthPercentage'
operator: 'LessThan'
threshold: 80
timeAggregation: 'Average'
}
]
}
actions: [
{
actionGroupId: actionGroup.id
}
]
}
}// KQL query for WAF blocked requests
AzureDiagnostics
| where Category == "FrontDoorWebApplicationFirewallLog"
| where action_s == "Block"
| summarize BlockedRequests = count() by
ruleName_s,
clientIp_s,
requestUri_s
| order by BlockedRequests desc
| take 100
// KQL query for origin latency
AzureDiagnostics
| where Category == "FrontDoorAccessLog"
| summarize
AvgLatency = avg(timeTaken_d),
P95Latency = percentile(timeTaken_d, 95),
P99Latency = percentile(timeTaken_d, 99)
by originName_s, bin(TimeGenerated, 5m)
| render timechart Front Door vs Other Services
| Feature | Front Door | Traffic Manager | Application Gateway |
|---|---|---|---|
| Layer | 7 (HTTP) | DNS | 7 (HTTP) |
| Scope | Global | Global | Regional |
| CDN | Built-in | No | No |
| WAF | Yes (Premium) | No | Yes |
| Private Link | Yes (Premium) | No | Yes |
| SSL Offload | Yes | No | Yes |
| Latency-based | Yes | Yes | No |
Routing Methods
| Method | Use Case |
|---|---|
| Latency | Route to fastest origin |
| Priority | Active-passive failover |
| Weighted | Blue-green deployments |
| Session Affinity | Stateful applications |
Practice Question
Why use Azure Front Door Premium with Private Link to origins instead of public endpoints?