| name | azure-front-door |
| description | Generate Front Door configs with CDN, WAF policies, and global routing. Use when the user wants to set up Azure Front Door for global load balancing, CDN, and web application firewall. |
| argument-hint | [profile-type] [origin] [description] |
| disable-model-invocation | true |
| allowed-tools | Read, Write, Edit, Glob, Grep, Bash(az *) |
| user-invocable | true |
Instructions
You are an Azure Front Door expert. Generate production-ready Front Door configurations with global load balancing, CDN, WAF, and advanced routing.
Step 1: Gather requirements
Determine from user input or $ARGUMENTS:
- Profile type: Standard (CDN + basic routing) or Premium (CDN + WAF + Private Link)
- Origin type: App Service, Container Apps, Storage (static site), Application Gateway, custom origin
- Use case: global web app, API acceleration, static site CDN, multi-region failover
- Domains: custom domains and SSL certificates
- Security: WAF policies, rate limiting, geo-filtering, bot protection
Step 2: Generate Front Door profile
Bicep template:
param location string = 'global'
param frontDoorName string
param profileType string = 'Premium_AzureFrontDoor'
resource frontDoorProfile 'Microsoft.Cdn/profiles@2023-05-01' = {
name: frontDoorName
location: location
sku: {
name: profileType // 'Standard_AzureFrontDoor' or 'Premium_AzureFrontDoor'
}
properties: {
originResponseTimeoutSeconds: 60
}
}
Step 3: Configure origins and origin groups
Origin group with health probes:
resource originGroup 'Microsoft.Cdn/profiles/originGroups@2023-05-01' = {
parent: frontDoorProfile
name: 'api-origin-group'
properties: {
loadBalancingSettings: {
sampleSize: 4
successfulSamplesRequired: 3
additionalLatencyInMilliseconds: 50
}
healthProbeSettings: {
probePath: '/health'
probeRequestType: 'HEAD'
probeProtocol: 'Https'
probeIntervalInSeconds: 30
}
sessionAffinityState: 'Disabled'
}
}
Multi-region origins:
resource primaryOrigin 'Microsoft.Cdn/profiles/originGroups/origins@2023-05-01' = {
parent: originGroup
name: 'primary-eastus'
properties: {
hostName: 'myapp-eastus.azurewebsites.net'
httpPort: 80
httpsPort: 443
originHostHeader: 'myapp-eastus.azurewebsites.net'
priority: 1
weight: 1000
enabledState: 'Enabled'
enforceCertificateNameCheck: true
}
}
resource secondaryOrigin 'Microsoft.Cdn/profiles/originGroups/origins@2023-05-01' = {
parent: originGroup
name: 'secondary-westus'
properties: {
hostName: 'myapp-westus.azurewebsites.net'
httpPort: 80
httpsPort: 443
originHostHeader: 'myapp-westus.azurewebsites.net'
priority: 2
weight: 1000
enabledState: 'Enabled'
enforceCertificateNameCheck: true
}
}
Private Link origin (Premium tier only):
resource privateLinkOrigin 'Microsoft.Cdn/profiles/originGroups/origins@2023-05-01' = {
parent: originGroup
name: 'private-origin'
properties: {
hostName: 'myapp-internal.azurewebsites.net'
httpPort: 80
httpsPort: 443
originHostHeader: 'myapp-internal.azurewebsites.net'
priority: 1
weight: 1000
enabledState: 'Enabled'
enforceCertificateNameCheck: true
sharedPrivateLinkResource: {
privateLink: {
id: appService.id
}
privateLinkLocation: 'eastus'
groupId: 'sites'
requestMessage: 'Front Door Private Link request'
}
}
}
Step 4: Configure endpoints and routes
Endpoint:
resource endpoint 'Microsoft.Cdn/profiles/afdEndpoints@2023-05-01' = {
parent: frontDoorProfile
name: 'myapp-endpoint'
location: location
properties: {
enabledState: 'Enabled'
autoGeneratedDomainNameLabelScope: 'TenantReuse'
}
}
Routes with pattern matching:
resource apiRoute 'Microsoft.Cdn/profiles/afdEndpoints/routes@2023-05-01' = {
parent: endpoint
name: 'api-route'
properties: {
originGroup: { id: apiOriginGroup.id }
originPath: '/'
patternsToMatch: ['/api/*']
supportedProtocols: ['Https']
httpsRedirect: 'Enabled'
forwardingProtocol: 'HttpsOnly'
cacheConfiguration: null // No caching for APIs
linkToDefaultDomain: 'Enabled'
customDomains: [
{ id: customDomain.id }
]
}
dependsOn: [primaryOrigin, secondaryOrigin]
}
resource staticRoute 'Microsoft.Cdn/profiles/afdEndpoints/routes@2023-05-01' = {
parent: endpoint
name: 'static-route'
properties: {
originGroup: { id: staticOriginGroup.id }
originPath: '/'
patternsToMatch: ['/*']
supportedProtocols: ['Http', 'Https']
httpsRedirect: 'Enabled'
forwardingProtocol: 'HttpsOnly'
cacheConfiguration: {
queryStringCachingBehavior: 'IgnoreQueryString'
compressionSettings: {
isCompressionEnabled: true
contentTypesToCompress: [
'text/html'
'text/css'
'text/javascript'
'application/javascript'
'application/json'
'application/xml'
'image/svg+xml'
'font/woff2'
]
}
}
linkToDefaultDomain: 'Enabled'
customDomains: [
{ id: customDomain.id }
]
}
dependsOn: [staticOrigin]
}
Step 5: Configure custom domains with managed certificates
resource customDomain 'Microsoft.Cdn/profiles/customDomains@2023-05-01' = {
parent: frontDoorProfile
name: 'myapp-com'
properties: {
hostName: 'www.myapp.com'
tlsSettings: {
certificateType: 'ManagedCertificate'
minimumTlsVersion: 'TLS12'
}
azureDnsZone: {
id: dnsZone.id
}
}
}
// DNS validation CNAME record
resource cnameRecord 'Microsoft.Network/dnsZones/CNAME@2023-07-01-preview' = {
parent: dnsZone
name: 'www'
properties: {
TTL: 3600
CNAMERecord: {
cname: endpoint.properties.hostName
}
}
}
// DNS validation TXT record for domain ownership
resource txtRecord 'Microsoft.Network/dnsZones/TXT@2023-07-01-preview' = {
parent: dnsZone
name: '_dnsauth.www'
properties: {
TTL: 3600
TXTRecords: [
{ value: [customDomain.properties.validationProperties.validationToken] }
]
}
}
Step 6: Configure WAF policy
WAF policy for Front Door:
resource wafPolicy 'Microsoft.Network/FrontDoorWebApplicationFirewallPolicies@2022-05-01' = {
name: '${frontDoorName}WafPolicy'
location: 'global'
sku: {
name: profileType
}
properties: {
policySettings: {
enabledState: 'Enabled'
mode: 'Prevention'
requestBodyCheck: 'Enabled'
customBlockResponseStatusCode: 403
customBlockResponseBody: base64('{"error":"Blocked by WAF"}')
}
managedRules: {
managedRuleSets: [
{
ruleSetType: 'Microsoft_DefaultRuleSet'
ruleSetVersion: '2.1'
ruleSetAction: 'Block'
ruleGroupOverrides: [
{
ruleGroupName: 'SQLI'
rules: [
{
ruleId: '942440'
enabledState: 'Enabled'
action: 'Log' // Override to log-only for specific rule
}
]
}
]
exclusions: [
{
matchVariable: 'RequestHeaderNames'
selectorMatchOperator: 'Equals'
selector: 'Authorization'
}
{
matchVariable: 'RequestCookieNames'
selectorMatchOperator: 'StartsWith'
selector: '__Host-'
}
]
}
{
ruleSetType: 'Microsoft_BotManagerRuleSet'
ruleSetVersion: '1.1'
ruleGroupOverrides: [
{
ruleGroupName: 'GoodBots'
rules: [
{
ruleId: 'Bot100100'
enabledState: 'Enabled'
action: 'Allow'
}
]
}
]
}
]
}
customRules: {
rules: [
{
name: 'RateLimitPerIP'
priority: 1
enabledState: 'Enabled'
ruleType: 'RateLimitRule'
rateLimitDurationInMinutes: 1
rateLimitThreshold: 1000
matchConditions: [
{
matchVariable: 'SocketAddr'
operator: 'IPMatch'
negateCondition: true
matchValue: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
}
]
action: 'Block'
}
{
name: 'GeoBlock'
priority: 2
enabledState: 'Enabled'
ruleType: 'MatchRule'
matchConditions: [
{
matchVariable: 'SocketAddr'
operator: 'GeoMatch'
matchValue: ['CN', 'RU', 'KP']
}
]
action: 'Block'
}
{
name: 'BlockBadUserAgents'
priority: 3
enabledState: 'Enabled'
ruleType: 'MatchRule'
matchConditions: [
{
matchVariable: 'RequestHeader'
selector: 'User-Agent'
operator: 'Contains'
matchValue: ['curl', 'wget', 'python-requests']
transforms: ['Lowercase']
}
]
action: 'Block'
}
{
name: 'AllowAdminFromCorporate'
priority: 10
enabledState: 'Enabled'
ruleType: 'MatchRule'
matchConditions: [
{
matchVariable: 'RequestUri'
operator: 'BeginsWith'
matchValue: ['/admin']
}
{
matchVariable: 'SocketAddr'
operator: 'IPMatch'
matchValue: ['203.0.113.0/24']
}
]
action: 'Allow'
}
]
}
}
}
// Associate WAF policy with endpoint
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: endpoint.id }
{ id: customDomain.id }
]
patternsToMatch: ['/*']
}
]
}
}
}
Step 7: Configure Rules Engine
Request/response manipulation:
resource ruleSet 'Microsoft.Cdn/profiles/ruleSets@2023-05-01' = {
parent: frontDoorProfile
name: 'SecurityHeaders'
}
resource securityHeaderRule 'Microsoft.Cdn/profiles/ruleSets/rules@2023-05-01' = {
parent: ruleSet
name: 'AddSecurityHeaders'
properties: {
order: 1
conditions: [] // Apply to all requests
actions: [
{
name: 'ModifyResponseHeader'
parameters: {
typeName: 'DeliveryRuleHeaderActionParameters'
headerAction: 'Overwrite'
headerName: 'Strict-Transport-Security'
value: 'max-age=31536000; includeSubDomains; preload'
}
}
{
name: 'ModifyResponseHeader'
parameters: {
typeName: 'DeliveryRuleHeaderActionParameters'
headerAction: 'Overwrite'
headerName: 'X-Content-Type-Options'
value: 'nosniff'
}
}
{
name: 'ModifyResponseHeader'
parameters: {
typeName: 'DeliveryRuleHeaderActionParameters'
headerAction: 'Overwrite'
headerName: 'X-Frame-Options'
value: 'DENY'
}
}
{
name: 'ModifyResponseHeader'
parameters: {
typeName: 'DeliveryRuleHeaderActionParameters'
headerAction: 'Overwrite'
headerName: 'Content-Security-Policy'
value: 'default-src \'self\'; script-src \'self\'; style-src \'self\' \'unsafe-inline\''
}
}
]
}
}
resource cacheControlRule 'Microsoft.Cdn/profiles/ruleSets/rules@2023-05-01' = {
parent: ruleSet
name: 'CacheStaticAssets'
properties: {
order: 2
conditions: [
{
name: 'UrlFileExtension'
parameters: {
typeName: 'DeliveryRuleUrlFileExtensionMatchConditionParameters'
operator: 'Equal'
matchValues: ['js', 'css', 'png', 'jpg', 'jpeg', 'gif', 'svg', 'woff2', 'woff', 'ttf']
transforms: ['Lowercase']
}
}
]
actions: [
{
name: 'RouteConfigurationOverride'
parameters: {
typeName: 'DeliveryRuleRouteConfigurationOverrideActionParameters'
cacheConfiguration: {
cacheBehavior: 'OverrideAlways'
cacheDuration: '30.00:00:00' // 30 days
isCompressionEnabled: 'Enabled'
queryStringCachingBehavior: 'IgnoreQueryString'
}
}
}
{
name: 'ModifyResponseHeader'
parameters: {
typeName: 'DeliveryRuleHeaderActionParameters'
headerAction: 'Overwrite'
headerName: 'Cache-Control'
value: 'public, max-age=2592000, immutable'
}
}
]
}
}
resource redirectRule 'Microsoft.Cdn/profiles/ruleSets/rules@2023-05-01' = {
parent: ruleSet
name: 'RedirectApexToWww'
properties: {
order: 3
conditions: [
{
name: 'HostName'
parameters: {
typeName: 'DeliveryRuleHostNameConditionParameters'
operator: 'Equal'
matchValues: ['myapp.com']
}
}
]
actions: [
{
name: 'UrlRedirect'
parameters: {
typeName: 'DeliveryRuleUrlRedirectActionParameters'
redirectType: 'PermanentRedirect'
destinationProtocol: 'Https'
customHostname: 'www.myapp.com'
}
}
]
}
}
Step 8: Configure caching strategies
Static site caching:
cacheConfiguration: {
queryStringCachingBehavior: 'IgnoreQueryString'
cacheDuration: '1.00:00:00' // 1 day for HTML
compressionSettings: {
isCompressionEnabled: true
contentTypesToCompress: [
'text/html'
'text/css'
'text/javascript'
'application/javascript'
'application/json'
'image/svg+xml'
'font/woff2'
]
}
}
API caching (selective):
cacheConfiguration: {
queryStringCachingBehavior: 'UseQueryString'
cacheDuration: '00:05:00' // 5 minutes
}
No caching (dynamic content):
cacheConfiguration: null // or omit entirely
Step 9: Terraform alternative
resource "azurerm_cdn_frontdoor_profile" "main" {
name = var.front_door_name
resource_group_name = azurerm_resource_group.main.name
sku_name = "Premium_AzureFrontDoor"
response_timeout_seconds = 60
}
resource "azurerm_cdn_frontdoor_origin_group" "api" {
name = "api-origin-group"
cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id
session_affinity_enabled = false
load_balancing {
sample_size = 4
successful_samples_required = 3
additional_latency_in_milliseconds = 50
}
health_probe {
path = "/health"
request_type = "HEAD"
protocol = "Https"
interval_in_seconds = 30
}
}
resource "azurerm_cdn_frontdoor_origin" "primary" {
name = "primary-origin"
cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.api.id
enabled = true
certificate_name_check_enabled = true
host_name = "myapp-eastus.azurewebsites.net"
origin_host_header = "myapp-eastus.azurewebsites.net"
http_port = 80
https_port = 443
priority = 1
weight = 1000
}
resource "azurerm_cdn_frontdoor_origin" "secondary" {
name = "secondary-origin"
cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.api.id
enabled = true
certificate_name_check_enabled = true
host_name = "myapp-westus.azurewebsites.net"
origin_host_header = "myapp-westus.azurewebsites.net"
http_port = 80
https_port = 443
priority = 2
weight = 1000
}
resource "azurerm_cdn_frontdoor_endpoint" "main" {
name = "myapp-endpoint"
cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id
enabled = true
}
resource "azurerm_cdn_frontdoor_route" "api" {
name = "api-route"
cdn_frontdoor_endpoint_id = azurerm_cdn_frontdoor_endpoint.main.id
cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.api.id
cdn_frontdoor_origin_ids = [azurerm_cdn_frontdoor_origin.primary.id, azurerm_cdn_frontdoor_origin.secondary.id]
cdn_frontdoor_rule_set_ids = [azurerm_cdn_frontdoor_rule_set.security.id]
enabled = true
forwarding_protocol = "HttpsOnly"
https_redirect_enabled = true
patterns_to_match = ["/api/*"]
supported_protocols = ["Http", "Https"]
cdn_frontdoor_custom_domain_ids = [azurerm_cdn_frontdoor_custom_domain.main.id]
}
resource "azurerm_cdn_frontdoor_custom_domain" "main" {
name = "myapp-com"
cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id
host_name = "www.myapp.com"
tls {
certificate_type = "ManagedCertificate"
minimum_tls_version = "TLS12"
}
}
resource "azurerm_cdn_frontdoor_firewall_policy" "main" {
name = "${var.front_door_name}waf"
resource_group_name = azurerm_resource_group.main.name
sku_name = "Premium_AzureFrontDoor"
enabled = true
mode = "Prevention"
custom_block_response_status_code = 403
managed_rule {
type = "Microsoft_DefaultRuleSet"
version = "2.1"
action = "Block"
}
managed_rule {
type = "Microsoft_BotManagerRuleSet"
version = "1.1"
}
custom_rule {
name = "RateLimitPerIP"
enabled = true
priority = 1
type = "RateLimitRule"
action = "Block"
rate_limit_duration_in_minutes = 1
rate_limit_threshold = 1000
match_condition {
match_variable = "SocketAddr"
operator = "IPMatch"
negation_condition = true
match_values = ["10.0.0.0/8"]
}
}
custom_rule {
name = "GeoBlock"
enabled = true
priority = 2
type = "MatchRule"
action = "Block"
match_condition {
match_variable = "SocketAddr"
operator = "GeoMatch"
match_values = ["CN", "RU"]
}
}
}
resource "azurerm_cdn_frontdoor_security_policy" "main" {
name = "waf-policy"
cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id
security_policies {
firewall {
cdn_frontdoor_firewall_policy_id = azurerm_cdn_frontdoor_firewall_policy.main.id
association {
domain {
cdn_frontdoor_domain_id = azurerm_cdn_frontdoor_endpoint.main.id
}
domain {
cdn_frontdoor_domain_id = azurerm_cdn_frontdoor_custom_domain.main.id
}
patterns_to_match = ["/*"]
}
}
}
}
resource "azurerm_cdn_frontdoor_rule_set" "security" {
name = "SecurityHeaders"
cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id
}
resource "azurerm_cdn_frontdoor_rule" "security_headers" {
name = "AddSecurityHeaders"
cdn_frontdoor_rule_set_id = azurerm_cdn_frontdoor_rule_set.security.id
order = 1
actions {
response_header_action {
header_action = "Overwrite"
header_name = "Strict-Transport-Security"
value = "max-age=31536000; includeSubDomains; preload"
}
response_header_action {
header_action = "Overwrite"
header_name = "X-Content-Type-Options"
value = "nosniff"
}
response_header_action {
header_action = "Overwrite"
header_name = "X-Frame-Options"
value = "DENY"
}
}
}
Step 10: Front Door vs Azure CDN vs Application Gateway
| Feature | Front Door | Azure CDN | Application Gateway |
|---|
| Scope | Global (anycast) | Global (POP) | Regional |
| Layer | Layer 7 | Layer 7 (CDN) | Layer 7 |
| WAF | Yes (Premium) | No | Yes (WAF_v2) |
| Private Link | Yes (Premium) | No | No (but private frontend) |
| SSL Offload | Yes | Yes | Yes |
| Caching | Yes | Yes | No |
| Path routing | Yes | Limited | Yes |
| Use case | Global apps, multi-region | Static content CDN | Regional load balancing |
Best practices:
- Use Premium tier when you need WAF or Private Link to origins
- Configure health probes with appropriate intervals (30s for production)
- Use priority-based routing for active-passive failover
- Use weighted routing for gradual traffic shifts (canary deployments)
- Enable compression for text-based content types
- Use Rules Engine for security headers and URL manipulation
- Configure managed certificates for custom domains (auto-renewal)
- Use separate origin groups for different backend types (API vs static)
- Enable HTTPS redirect on all routes
- Set appropriate cache durations per content type
Anti-patterns to avoid:
- Do NOT use Standard tier if you need WAF (WAF requires Premium)
- Do NOT cache API responses without understanding the implications
- Do NOT skip health probes; they are essential for failover
- Do NOT use overly aggressive caching for dynamic content
- Do NOT forget to validate custom domains with DNS records
- Do NOT apply WAF in Prevention mode without first testing in Detection mode
- Do NOT use Front Door for region-local traffic (use Application Gateway instead)
- Do NOT ignore the origin response timeout setting; increase for slow backends
Security considerations:
- Enable WAF with Microsoft_DefaultRuleSet 2.1 and BotManagerRuleSet
- Implement rate limiting per IP to prevent DDoS and brute force
- Use geo-filtering to block traffic from high-risk regions
- Configure custom WAF rules for application-specific threats
- Use Private Link origins (Premium) to eliminate public internet exposure to backends
- Add security headers via Rules Engine (HSTS, CSP, X-Frame-Options)
- Use TLS 1.2 minimum on custom domains
- Block known bad user agents and suspicious request patterns
- Enable WAF logging for security monitoring and incident response
- Restrict backend access to only Front Door IPs using the AzureFrontDoor.Backend service tag
Cost optimization tips:
- Standard tier: Lower cost, suitable when WAF is handled at origin or not needed
- Premium tier: Higher cost, but includes WAF and Private Link (may eliminate need for separate WAF)
- Front Door is billed per routing rule, data transfer, and WAF requests
- Use caching aggressively for static content to reduce origin load and egress
- Enable compression to reduce data transfer costs
- Use Rules Engine to cache static assets with long TTLs
- Consolidate multiple apps behind a single Front Door profile
- Monitor data transfer with Azure Cost Management; high egress may warrant reserved capacity
- Consider Standard tier + origin-level WAF (App Gateway WAF_v2) as a cost-effective alternative to Premium
- Use query string caching behavior wisely; unnecessary variations increase cache misses