Networking Security Baselines
Networking services form the security perimeter of every Azure architecture. Front Door provides global edge protection with WAF, Azure Firewall controls all east-west and north-south traffic flows, and VPN Gateway secures hybrid connectivity.
Network Security Layers
Azure Front Door
Global CDN and L7 load balancer with built-in WAF, DDoS protection, and TLS termination at the edge. Front Door should be the single entry point for all internet-facing HTTP workloads — it's your first line of defense against web attacks.
| Property | Value |
|---|---|
| Defender Plan | N/A — built-in WAF analytics, integrate with Sentinel |
| Azure Policy Built-in | Yes — WAF enablement, TLS version, WAF mode |
| Recommended Tier | Premium (managed rulesets + bot protection + Private Link origins) |
| Key Feature | Private Link to origins (App Service, Storage, LB) with no public exposure |
Baseline Controls
| Domain | Feature | Supported | Default | Responsibility |
|---|---|---|---|---|
| NS | WAF (Prevention Mode) | ✓ | Manual | Customer |
| NS | DDoS Protection | ✓ | Default | Microsoft |
| NS | Private Link Origins | ✓ | Manual | Customer |
| NS | TLS 1.2 Minimum | ✓ | Default | Microsoft |
| DP | End-to-End TLS | ✓ | Manual | Customer |
| LT | WAF & Access Logs | ✓ | Manual | Customer |
WAF (Prevention Mode)
DDoS Protection
Private Link Origins
TLS 1.2 Minimum
End-to-End TLS
WAF & Access Logs
Terraform: Hardened Front Door Premium
resource "azurerm_cdn_frontdoor_profile" "main" {
name = "fd-prod"
resource_group_name = azurerm_resource_group.network.name
sku_name = "Premium_AzureFrontDoor" # Private Link + managed rulesets
}
# NS-6: WAF Policy in Prevention Mode
resource "azurerm_cdn_frontdoor_firewall_policy" "main" {
name = "wafprod"
resource_group_name = azurerm_resource_group.network.name
sku_name = azurerm_cdn_frontdoor_profile.main.sku_name
mode = "Prevention"
managed_rule {
type = "Microsoft_DefaultRuleSet"
version = "2.1" # Latest OWASP ruleset
action = "Block"
}
managed_rule {
type = "Microsoft_BotManagerRuleSet"
version = "1.0"
action = "Block"
}
# Rate limiting rule
custom_rule {
name = "RateLimitRule"
type = "RateLimitRule"
action = "Block"
priority = 100
rate_limit_duration_in_minutes = 1
rate_limit_threshold = 100
match_condition {
match_variable = "RemoteAddr"
operator = "IPMatch"
match_values = ["0.0.0.0/0"]
negation_condition = true
}
}
}
resource "azurerm_cdn_frontdoor_endpoint" "main" {
name = "ep-prod"
cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id
}
# NS-2: Private Link origin (no public exposure on backend)
resource "azurerm_cdn_frontdoor_origin_group" "app" {
name = "og-app-prod"
cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id
health_probe {
interval_in_seconds = 30
path = "/health"
protocol = "Https"
}
load_balancing {
sample_size = 4
successful_samples_required = 3
}
}
resource "azurerm_cdn_frontdoor_origin" "app" {
name = "origin-app-prod"
cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.app.id
host_name = azurerm_linux_web_app.main.default_hostname
origin_host_header = azurerm_linux_web_app.main.default_hostname
certificate_name_check_enabled = true
enabled = true
private_link {
location = azurerm_resource_group.app.location
private_link_target_id = azurerm_linux_web_app.main.id
target_type = "sites"
request_message = "Front Door Private Link"
}
}
# LT-3: Diagnostic Settings
resource "azurerm_monitor_diagnostic_setting" "fd" {
name = "diag-fd-prod"
target_resource_id = azurerm_cdn_frontdoor_profile.main.id
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
enabled_log { category = "FrontDoorAccessLog" }
enabled_log { category = "FrontDoorWebApplicationFirewallLog" }
enabled_log { category = "FrontDoorHealthProbeLog" }
metric { category = "AllMetrics" }
}resource "azurerm_cdn_frontdoor_profile" "main" {
name = "fd-prod"
resource_group_name = azurerm_resource_group.network.name
sku_name = "Premium_AzureFrontDoor" # Private Link + managed rulesets
}
# NS-6: WAF Policy in Prevention Mode
resource "azurerm_cdn_frontdoor_firewall_policy" "main" {
name = "wafprod"
resource_group_name = azurerm_resource_group.network.name
sku_name = azurerm_cdn_frontdoor_profile.main.sku_name
mode = "Prevention"
managed_rule {
type = "Microsoft_DefaultRuleSet"
version = "2.1" # Latest OWASP ruleset
action = "Block"
}
managed_rule {
type = "Microsoft_BotManagerRuleSet"
version = "1.0"
action = "Block"
}
# Rate limiting rule
custom_rule {
name = "RateLimitRule"
type = "RateLimitRule"
action = "Block"
priority = 100
rate_limit_duration_in_minutes = 1
rate_limit_threshold = 100
match_condition {
match_variable = "RemoteAddr"
operator = "IPMatch"
match_values = ["0.0.0.0/0"]
negation_condition = true
}
}
}
resource "azurerm_cdn_frontdoor_endpoint" "main" {
name = "ep-prod"
cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id
}
# NS-2: Private Link origin (no public exposure on backend)
resource "azurerm_cdn_frontdoor_origin_group" "app" {
name = "og-app-prod"
cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id
health_probe {
interval_in_seconds = 30
path = "/health"
protocol = "Https"
}
load_balancing {
sample_size = 4
successful_samples_required = 3
}
}
resource "azurerm_cdn_frontdoor_origin" "app" {
name = "origin-app-prod"
cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.app.id
host_name = azurerm_linux_web_app.main.default_hostname
origin_host_header = azurerm_linux_web_app.main.default_hostname
certificate_name_check_enabled = true
enabled = true
private_link {
location = azurerm_resource_group.app.location
private_link_target_id = azurerm_linux_web_app.main.id
target_type = "sites"
request_message = "Front Door Private Link"
}
}
# LT-3: Diagnostic Settings
resource "azurerm_monitor_diagnostic_setting" "fd" {
name = "diag-fd-prod"
target_resource_id = azurerm_cdn_frontdoor_profile.main.id
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
enabled_log { category = "FrontDoorAccessLog" }
enabled_log { category = "FrontDoorWebApplicationFirewallLog" }
enabled_log { category = "FrontDoorHealthProbeLog" }
metric { category = "AllMetrics" }
}Azure Portal Instructions
Step-by-step instructions for configuring each Front Door control through the Azure Portal.
NS-6 — Web Application Firewall
- Navigate to your Azure Front Door resource in the Azure Portal
- In the left menu under Settings, select Web Application Firewall
- Click Create to create a new WAF policy (or select an existing one)
- Under Managed rules, enable the Default Rule Set (DRS 2.1 or OWASP 3.2)
- Under Custom rules, add rate limiting and geo-filtering rules as needed
- Under Policy settings, set the WAF mode to Prevention
- Associate the WAF policy with the Front Door endpoint
- Click Save
NS-5 — DDoS Protection
This feature is managed by Microsoft and enabled by default. DDoS infrastructure protection is built into Azure Front Door at no additional cost. For additional Layer 7 protection, configure WAF rate limiting rules.
DP-3 — End-to-End TLS
- Navigate to your Azure Front Door resource in the Azure Portal
- In the left menu under Settings, select Domains
- Select a custom domain and verify the minimum TLS version is set to 1.2
- For certificates, select "AFD Managed" for automatic certificates or "Bring Your Own Certificate" from Key Vault
- Navigate to Origin groups > select your origin group > edit an origin
- Set the Origin protocol to HTTPS only
- Click Update and then Save
DP-7 — Certificate Management
- Navigate to your Azure Front Door resource in the Azure Portal
- In the left menu under Settings, select Domains
- For a custom domain, click on the domain name to edit
- Under Certificate management type, choose "AFD Managed" (auto-renewal) or "Bring Your Own Certificate"
- If BYOC, select your Key Vault, Secret, and Secret version
- Ensure Front Door has the necessary access policy on the Key Vault
- Click Update
LT-3 — Access & WAF Logs
- Navigate to your Azure Front Door resource in the Azure Portal
- In the left menu under Monitoring, select Diagnostic settings
- Click + Add diagnostic setting
- Give the setting a name
- Select the log categories: FrontDoorAccessLog, FrontDoorHealthProbeLog, FrontDoorWebApplicationFirewallLog
- Select the destination: Send to Log Analytics workspace
- Select your Log Analytics workspace
- Click Save
Origin Lockdown Pattern
Azure Firewall
Cloud-native L3-L7 firewall that sits at the hub of your network topology. Premium tier adds TLS inspection and IDPS for full packet-level visibility. This is your primary north-south and east-west traffic control point.
Baseline Controls
| Domain | Feature | Supported | Default | Responsibility |
|---|---|---|---|---|
| NS | Network Rules (L3-L4) | ✓ | Default | Customer |
| NS | Application Rules (L7 FQDN) | ✓ | Default | Customer |
| NS | IDPS (Premium) | ✓ | Manual | Customer |
| NS | TLS Inspection (Premium) | ✓ | Manual | Customer |
| NS | Threat Intelligence Filtering | ✓ | Default | Microsoft |
| NS | DNS Proxy | ✓ | Manual | Customer |
| LT | Structured Logs | ✓ | Manual | Customer |
| LT | Firewall Workbooks | ✓ | Manual | Customer |
Network Rules (L3-L4)
Application Rules (L7 FQDN)
IDPS (Premium)
TLS Inspection (Premium)
Threat Intelligence Filtering
DNS Proxy
Structured Logs
Firewall Workbooks
Azure Portal Instructions
Azure Firewall Baseline Configuration Workflow
- Navigate to your Azure Firewall or Firewall Policy resource in the Azure Portal.
- Configure Network rules and Application rules with explicit allowlists and a deny-by-default posture.
- If you use Premium, enable IDPS and TLS inspection in the policy settings.
- Set Threat intelligence mode to Deny and enable DNS proxy if the firewall will centralize name resolution.
- Under Monitoring, add diagnostic settings to send logs and metrics to Log Analytics.
- Review Azure Monitor workbooks to validate denied traffic, inspection coverage, and alert quality.
Terraform: Azure Firewall Premium with IDPS
resource "azurerm_firewall_policy" "main" {
name = "fwpol-hub-prod"
resource_group_name = azurerm_resource_group.hub.name
location = azurerm_resource_group.hub.location
sku = "Premium"
threat_intelligence_mode = "Deny"
intrusion_detection {
mode = "Deny" # Alert+Deny for IDPS
traffic_bypass {
name = "AllowHealthProbes"
protocol = "TCP"
source_addresses = ["168.63.129.16/32"]
destination_addresses = ["*"]
destination_ports = ["*"]
}
}
dns {
proxy_enabled = true # DNS proxy for private DNS resolution
}
}
# Application rule collection — FQDN filtering
resource "azurerm_firewall_policy_rule_collection_group" "app_rules" {
name = "rcg-app-prod"
firewall_policy_id = azurerm_firewall_policy.main.id
priority = 200
application_rule_collection {
name = "AllowAzurePaaS"
priority = 210
action = "Allow"
rule {
name = "AllowAzureMonitor"
source_addresses = ["10.0.0.0/8"]
protocols { type = "Https"; port = 443 }
destination_fqdn_tags = ["AzureMonitor"]
}
}
network_rule_collection {
name = "DenyAll"
priority = 300
action = "Deny"
rule {
name = "DenyInternetOutbound"
source_addresses = ["10.0.0.0/8"]
destination_addresses = ["*"]
destination_ports = ["*"]
protocols = ["Any"]
}
}
}resource "azurerm_firewall_policy" "main" {
name = "fwpol-hub-prod"
resource_group_name = azurerm_resource_group.hub.name
location = azurerm_resource_group.hub.location
sku = "Premium"
threat_intelligence_mode = "Deny"
intrusion_detection {
mode = "Deny" # Alert+Deny for IDPS
traffic_bypass {
name = "AllowHealthProbes"
protocol = "TCP"
source_addresses = ["168.63.129.16/32"]
destination_addresses = ["*"]
destination_ports = ["*"]
}
}
dns {
proxy_enabled = true # DNS proxy for private DNS resolution
}
}
# Application rule collection — FQDN filtering
resource "azurerm_firewall_policy_rule_collection_group" "app_rules" {
name = "rcg-app-prod"
firewall_policy_id = azurerm_firewall_policy.main.id
priority = 200
application_rule_collection {
name = "AllowAzurePaaS"
priority = 210
action = "Allow"
rule {
name = "AllowAzureMonitor"
source_addresses = ["10.0.0.0/8"]
protocols { type = "Https"; port = 443 }
destination_fqdn_tags = ["AzureMonitor"]
}
}
network_rule_collection {
name = "DenyAll"
priority = 300
action = "Deny"
rule {
name = "DenyInternetOutbound"
source_addresses = ["10.0.0.0/8"]
destination_addresses = ["*"]
destination_ports = ["*"]
protocols = ["Any"]
}
}
}VPN Gateway
Encrypted IPsec/IKEv2 tunnels connecting on-premises networks to Azure. Use for hybrid connectivity when ExpressRoute is not available, or as a backup for ExpressRoute with mutual failover.
Baseline Controls
| Domain | Feature | Supported | Default | Responsibility |
|---|---|---|---|---|
| NS | IPsec/IKEv2 Encryption | ✓ | Default | Shared |
| NS | Custom IPsec Policies | ✓ | Manual | Customer |
| NS | BGP Support | ✓ | Manual | Customer |
| DP | AES-256-GCM Encryption | ✓ | Manual | Customer |
| LT | Tunnel Diagnostics | ✓ | Manual | Customer |
| BR | Active-Active Mode | ✓ | Manual | Customer |
IPsec/IKEv2 Encryption
Custom IPsec Policies
BGP Support
AES-256-GCM Encryption
Tunnel Diagnostics
Active-Active Mode
Azure Portal Instructions
VPN Gateway Baseline Configuration Workflow
- Navigate to your Virtual network gateway resource in the Azure Portal.
- Under Configuration, confirm the gateway is route-based and enable active-active mode if high availability is required.
- Enable BGP if you need dynamic route exchange with on-premises devices.
- Open the connection resource and configure a custom IPsec/IKE policy with strong ciphers such as AES-256 and SHA-256.
- Under Monitoring, enable diagnostic settings for tunnel status and gateway diagnostics.
- Validate tunnel state, route advertisement, and failover behavior after the policy is applied.
Terraform: Hardened VPN Gateway with Custom IPsec
resource "azurerm_virtual_network_gateway" "vpn" {
name = "vpng-hub-prod"
location = azurerm_resource_group.hub.location
resource_group_name = azurerm_resource_group.hub.name
type = "Vpn"
vpn_type = "RouteBased"
sku = "VpnGw2AZ" # Zone-redundant
active_active = true # BR: High availability
enable_bgp = true
ip_configuration {
name = "vpn-ipconfig-1"
public_ip_address_id = azurerm_public_ip.vpn_1.id
subnet_id = azurerm_subnet.gateway.id
}
ip_configuration {
name = "vpn-ipconfig-2"
public_ip_address_id = azurerm_public_ip.vpn_2.id
subnet_id = azurerm_subnet.gateway.id
}
}
# NS-9: Custom IPsec policy (strong crypto only)
resource "azurerm_virtual_network_gateway_connection" "onprem" {
name = "conn-onprem-prod"
location = azurerm_resource_group.hub.location
resource_group_name = azurerm_resource_group.hub.name
type = "IPsec"
virtual_network_gateway_id = azurerm_virtual_network_gateway.vpn.id
local_network_gateway_id = azurerm_local_network_gateway.onprem.id
shared_key = data.azurerm_key_vault_secret.vpn_psk.value
ipsec_policy {
sa_lifetime = 3600 # 1 hour
sa_datasize = 102400000 # 100 GB
ipsec_encryption = "GCMAES256" # AES-256-GCM
ipsec_integrity = "GCMAES256"
ike_encryption = "AES256"
ike_integrity = "SHA256"
dh_group = "DHGroup14" # 2048-bit
pfs_group = "PFS2048"
}
}resource "azurerm_virtual_network_gateway" "vpn" {
name = "vpng-hub-prod"
location = azurerm_resource_group.hub.location
resource_group_name = azurerm_resource_group.hub.name
type = "Vpn"
vpn_type = "RouteBased"
sku = "VpnGw2AZ" # Zone-redundant
active_active = true # BR: High availability
enable_bgp = true
ip_configuration {
name = "vpn-ipconfig-1"
public_ip_address_id = azurerm_public_ip.vpn_1.id
subnet_id = azurerm_subnet.gateway.id
}
ip_configuration {
name = "vpn-ipconfig-2"
public_ip_address_id = azurerm_public_ip.vpn_2.id
subnet_id = azurerm_subnet.gateway.id
}
}
# NS-9: Custom IPsec policy (strong crypto only)
resource "azurerm_virtual_network_gateway_connection" "onprem" {
name = "conn-onprem-prod"
location = azurerm_resource_group.hub.location
resource_group_name = azurerm_resource_group.hub.name
type = "IPsec"
virtual_network_gateway_id = azurerm_virtual_network_gateway.vpn.id
local_network_gateway_id = azurerm_local_network_gateway.onprem.id
shared_key = data.azurerm_key_vault_secret.vpn_psk.value
ipsec_policy {
sa_lifetime = 3600 # 1 hour
sa_datasize = 102400000 # 100 GB
ipsec_encryption = "GCMAES256" # AES-256-GCM
ipsec_integrity = "GCMAES256"
ike_encryption = "AES256"
ike_integrity = "SHA256"
dh_group = "DHGroup14" # 2048-bit
pfs_group = "PFS2048"
}
}Pre-Shared Key Management
Application Gateway
Regional L7 load balancer with optional WAF v2, URL-based routing, and end-to-end TLS. Application Gateway is the best choice for intra-region HTTP workload protection — it deploys into a VNet subnet and supports private-only frontends for internal apps.
| Property | Value |
|---|---|
| Defender Plan | N/A — built-in WAF analytics, integrate with Sentinel |
| Azure Policy Built-in | Yes — WAF enablement, TLS version, WAF mode |
| Recommended SKU | WAF v2 (auto-scaling, zone-redundant, managed rulesets) |
| Key Feature | VNet-native deployment with private frontend IP for internal apps |
Baseline Controls
| Domain | Feature | Supported | Default | Responsibility |
|---|---|---|---|---|
| NS | VNet Deployment | ✓ | Default | Customer |
| NS | WAF Integration | ✓ | Manual | Customer |
| NS | Private Frontend | ✓ | Manual | Customer |
| IM | Managed Identity | ✓ | Manual | Customer |
| DP | End-to-End TLS | ✓ | Manual | Customer |
| LT | Diagnostic Logging | ✓ | Manual | Customer |
VNet Deployment
WAF Integration
Private Frontend
Managed Identity
End-to-End TLS
Diagnostic Logging
Terraform: Hardened Application Gateway WAF v2
resource "azurerm_application_gateway" "main" {
name = "appgw-prod"
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
sku {
name = "WAF_v2"
tier = "WAF_v2"
}
autoscale_configuration {
min_capacity = 2
max_capacity = 10
}
# NS-1: VNet deployment
gateway_ip_configuration {
name = "appgw-ipconfig"
subnet_id = azurerm_subnet.appgw.id
}
# DP-3: HTTPS listener with TLS 1.2+
ssl_policy {
policy_type = "Predefined"
policy_name = "AppGwSslPolicy20220101S" # TLS 1.2+ only
}
# IM-3: Key Vault certificate via managed identity
identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.appgw.id]
}
ssl_certificate {
name = "frontend-cert"
key_vault_secret_id = azurerm_key_vault_certificate.frontend.versionless_secret_id
}
# NS-6: WAF configuration
waf_configuration {
enabled = true
firewall_mode = "Prevention"
rule_set_type = "OWASP"
rule_set_version = "3.2"
}
}resource "azurerm_application_gateway" "main" {
name = "appgw-prod"
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
sku {
name = "WAF_v2"
tier = "WAF_v2"
}
autoscale_configuration {
min_capacity = 2
max_capacity = 10
}
# NS-1: VNet deployment
gateway_ip_configuration {
name = "appgw-ipconfig"
subnet_id = azurerm_subnet.appgw.id
}
# DP-3: HTTPS listener with TLS 1.2+
ssl_policy {
policy_type = "Predefined"
policy_name = "AppGwSslPolicy20220101S" # TLS 1.2+ only
}
# IM-3: Key Vault certificate via managed identity
identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.appgw.id]
}
ssl_certificate {
name = "frontend-cert"
key_vault_secret_id = azurerm_key_vault_certificate.frontend.versionless_secret_id
}
# NS-6: WAF configuration
waf_configuration {
enabled = true
firewall_mode = "Prevention"
rule_set_type = "OWASP"
rule_set_version = "3.2"
}
}Azure Bastion
Fully managed PaaS service for secure RDP/SSH access to VMs without exposing public IPs. Bastion is the recommended replacement for jump boxes and public RDP/SSH — it eliminates the attack surface of exposed management ports.
| Property | Value |
|---|---|
| Defender Plan | N/A — session logs integrate with Sentinel |
| Required Subnet | AzureBastionSubnet (/26 minimum) |
| Recommended SKU | Standard (session recording, shareable links, native client) |
| Key Feature | Browser-based RDP/SSH over TLS — no public IPs on VMs |
Baseline Controls
| Domain | Feature | Supported | Default | Responsibility |
|---|---|---|---|---|
| NS | VNet-Only Access | ✓ | Manual | Customer |
| NS | NSG Lockdown | ✓ | Manual | Customer |
| IM | Entra ID VM Login | ✓ | Manual | Customer |
| DP | TLS Encryption | ✓ | Default | Microsoft |
| LT | Session Recording | ✓ | Manual | Customer |
VNet-Only Access
NSG Lockdown
Entra ID VM Login
TLS Encryption
Session Recording
Terraform: Standard Bastion with Logging
resource "azurerm_subnet" "bastion" {
name = "AzureBastionSubnet" # Must be this exact name
resource_group_name = azurerm_resource_group.hub.name
virtual_network_name = azurerm_virtual_network.hub.name
address_prefixes = ["10.0.1.0/26"] # /26 minimum
}
resource "azurerm_bastion_host" "main" {
name = "bastion-hub-prod"
location = azurerm_resource_group.hub.location
resource_group_name = azurerm_resource_group.hub.name
sku = "Standard" # Session recording + native client
ip_configuration {
name = "bastion-ipconfig"
subnet_id = azurerm_subnet.bastion.id
public_ip_address_id = azurerm_public_ip.bastion.id
}
}
# LT-3: Session audit logs
resource "azurerm_monitor_diagnostic_setting" "bastion" {
name = "diag-bastion-prod"
target_resource_id = azurerm_bastion_host.main.id
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
enabled_log { category = "BastionAuditLogs" }
metric { category = "AllMetrics" }
}resource "azurerm_subnet" "bastion" {
name = "AzureBastionSubnet" # Must be this exact name
resource_group_name = azurerm_resource_group.hub.name
virtual_network_name = azurerm_virtual_network.hub.name
address_prefixes = ["10.0.1.0/26"] # /26 minimum
}
resource "azurerm_bastion_host" "main" {
name = "bastion-hub-prod"
location = azurerm_resource_group.hub.location
resource_group_name = azurerm_resource_group.hub.name
sku = "Standard" # Session recording + native client
ip_configuration {
name = "bastion-ipconfig"
subnet_id = azurerm_subnet.bastion.id
public_ip_address_id = azurerm_public_ip.bastion.id
}
}
# LT-3: Session audit logs
resource "azurerm_monitor_diagnostic_setting" "bastion" {
name = "diag-bastion-prod"
target_resource_id = azurerm_bastion_host.main.id
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
enabled_log { category = "BastionAuditLogs" }
metric { category = "AllMetrics" }
}ExpressRoute
Dedicated private connectivity from on-premises datacenters to Azure over provider networks. ExpressRoute does not traverse the public internet, but it is not encrypted by default — use MACsec on Direct ports or IPsec VPN overlay for encryption requirements.
| Property | Value |
|---|---|
| Defender Plan | N/A — monitor via Network Watcher and Sentinel |
| Encryption Options | MACsec (Direct), IPsec VPN overlay, or application-layer TLS |
| Recommended SKU | Premium for Global Reach and cross-region connectivity |
| Key Feature | Private Layer 3 connectivity — no public internet traversal |
Baseline Controls
| Domain | Feature | Supported | Default | Responsibility |
|---|---|---|---|---|
| NS | Private Peering | ✓ | Manual | Customer |
| NS | Route Filtering | ✓ | Manual | Customer |
| DP | MACsec Encryption | ✓ | Manual | Customer |
| DP | IPsec over ER | ✓ | Manual | Customer |
| LT | Circuit Diagnostics | ✓ | Manual | Customer |
Private Peering
Route Filtering
MACsec Encryption
IPsec over ER
Circuit Diagnostics
Terraform: ExpressRoute Circuit with Gateway
resource "azurerm_express_route_circuit" "main" {
name = "erc-prod"
resource_group_name = azurerm_resource_group.hub.name
location = azurerm_resource_group.hub.location
service_provider_name = "Equinix"
peering_location = "Silicon Valley"
bandwidth_in_mbps = 1000
sku {
tier = "Premium" # Cross-region + Global Reach
family = "UnlimitedData"
}
}
resource "azurerm_virtual_network_gateway" "er" {
name = "ergw-hub-prod"
location = azurerm_resource_group.hub.location
resource_group_name = azurerm_resource_group.hub.name
type = "ExpressRoute"
sku = "ErGw2AZ" # Zone-redundant
ip_configuration {
name = "ergw-ipconfig"
public_ip_address_id = azurerm_public_ip.ergw.id
subnet_id = azurerm_subnet.gateway.id
}
}
resource "azurerm_virtual_network_gateway_connection" "er" {
name = "conn-er-prod"
location = azurerm_resource_group.hub.location
resource_group_name = azurerm_resource_group.hub.name
type = "ExpressRoute"
virtual_network_gateway_id = azurerm_virtual_network_gateway.er.id
express_route_circuit_id = azurerm_express_route_circuit.main.id
}resource "azurerm_express_route_circuit" "main" {
name = "erc-prod"
resource_group_name = azurerm_resource_group.hub.name
location = azurerm_resource_group.hub.location
service_provider_name = "Equinix"
peering_location = "Silicon Valley"
bandwidth_in_mbps = 1000
sku {
tier = "Premium" # Cross-region + Global Reach
family = "UnlimitedData"
}
}
resource "azurerm_virtual_network_gateway" "er" {
name = "ergw-hub-prod"
location = azurerm_resource_group.hub.location
resource_group_name = azurerm_resource_group.hub.name
type = "ExpressRoute"
sku = "ErGw2AZ" # Zone-redundant
ip_configuration {
name = "ergw-ipconfig"
public_ip_address_id = azurerm_public_ip.ergw.id
subnet_id = azurerm_subnet.gateway.id
}
}
resource "azurerm_virtual_network_gateway_connection" "er" {
name = "conn-er-prod"
location = azurerm_resource_group.hub.location
resource_group_name = azurerm_resource_group.hub.name
type = "ExpressRoute"
virtual_network_gateway_id = azurerm_virtual_network_gateway.er.id
express_route_circuit_id = azurerm_express_route_circuit.main.id
}ExpressRoute Is Not Encrypted by Default