Networking

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

graph TB subgraph Global["Global Edge"] FD[Azure Front Door and WAF] end subgraph Regional["Regional Hub"] APPGW[Application Gateway and WAF] AZFW[Azure Firewall Premium] end subgraph Connectivity["Hybrid Connectivity"] VPN[VPN Gateway] ER[ExpressRoute] end subgraph Spokes["Spoke VNets"] S1[Workload Subnet] S2[Data Subnet] S3[Private Endpoints] end FD --> APPGW APPGW --> S1 VPN --> AZFW ER --> AZFW AZFW --> S1 AZFW --> S2 AZFW --> S3 style Global stroke:#ec4899,stroke-width:2px style Regional stroke:#f59e0b,stroke-width:2px style Connectivity stroke:#a855f7,stroke-width:2px style Spokes stroke:#22d3ee,stroke-width:2px

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.

PropertyValue
Defender PlanN/A — built-in WAF analytics, integrate with Sentinel
Azure Policy Built-inYes — WAF enablement, TLS version, WAF mode
Recommended TierPremium (managed rulesets + bot protection + Private Link origins)
Key FeaturePrivate Link to origins (App Service, Storage, LB) with no public exposure

Baseline Controls

NS✓ Supported

WAF (Prevention Mode)

○ ManualCustomer
NS✓ Supported

DDoS Protection

● DefaultMicrosoft
NS✓ Supported

Private Link Origins

○ ManualCustomer
NS✓ Supported

TLS 1.2 Minimum

● DefaultMicrosoft
DP✓ Supported

End-to-End TLS

○ ManualCustomer
LT✓ Supported

WAF & Access Logs

○ ManualCustomer

Terraform: Hardened Front Door Premium

hcl
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
  1. Navigate to your Azure Front Door resource in the Azure Portal
  2. In the left menu under Settings, select Web Application Firewall
  3. Click Create to create a new WAF policy (or select an existing one)
  4. Under Managed rules, enable the Default Rule Set (DRS 2.1 or OWASP 3.2)
  5. Under Custom rules, add rate limiting and geo-filtering rules as needed
  6. Under Policy settings, set the WAF mode to Prevention
  7. Associate the WAF policy with the Front Door endpoint
  8. 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
  1. Navigate to your Azure Front Door resource in the Azure Portal
  2. In the left menu under Settings, select Domains
  3. Select a custom domain and verify the minimum TLS version is set to 1.2
  4. For certificates, select "AFD Managed" for automatic certificates or "Bring Your Own Certificate" from Key Vault
  5. Navigate to Origin groups > select your origin group > edit an origin
  6. Set the Origin protocol to HTTPS only
  7. Click Update and then Save
DP-7 — Certificate Management
  1. Navigate to your Azure Front Door resource in the Azure Portal
  2. In the left menu under Settings, select Domains
  3. For a custom domain, click on the domain name to edit
  4. Under Certificate management type, choose "AFD Managed" (auto-renewal) or "Bring Your Own Certificate"
  5. If BYOC, select your Key Vault, Secret, and Secret version
  6. Ensure Front Door has the necessary access policy on the Key Vault
  7. Click Update
LT-3 — Access & WAF Logs
  1. Navigate to your Azure Front Door resource in the Azure Portal
  2. In the left menu under Monitoring, select Diagnostic settings
  3. Click + Add diagnostic setting
  4. Give the setting a name
  5. Select the log categories: FrontDoorAccessLog, FrontDoorHealthProbeLog, FrontDoorWebApplicationFirewallLog
  6. Select the destination: Send to Log Analytics workspace
  7. Select your Log Analytics workspace
  8. Click Save

Origin Lockdown Pattern

When using Front Door with Private Link origins, configure access restrictions on the origin App Service to reject all traffic except Front Door's service tag. This prevents attackers from bypassing the WAF by hitting the origin directly.

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

NS✓ Supported

Network Rules (L3-L4)

● DefaultCustomer
NS✓ Supported

Application Rules (L7 FQDN)

● DefaultCustomer
NS✓ Supported

IDPS (Premium)

○ ManualCustomer
NS✓ Supported

TLS Inspection (Premium)

○ ManualCustomer
NS✓ Supported

Threat Intelligence Filtering

● DefaultMicrosoft
NS✓ Supported

DNS Proxy

○ ManualCustomer
LT✓ Supported

Structured Logs

○ ManualCustomer
LT✓ Supported

Firewall Workbooks

○ ManualCustomer

Azure Portal Instructions

Azure Firewall Baseline Configuration Workflow
  1. Navigate to your Azure Firewall or Firewall Policy resource in the Azure Portal.
  2. Configure Network rules and Application rules with explicit allowlists and a deny-by-default posture.
  3. If you use Premium, enable IDPS and TLS inspection in the policy settings.
  4. Set Threat intelligence mode to Deny and enable DNS proxy if the firewall will centralize name resolution.
  5. Under Monitoring, add diagnostic settings to send logs and metrics to Log Analytics.
  6. Review Azure Monitor workbooks to validate denied traffic, inspection coverage, and alert quality.

Terraform: Azure Firewall Premium with IDPS

hcl
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

NS✓ Supported

IPsec/IKEv2 Encryption

● DefaultShared
NS✓ Supported

Custom IPsec Policies

○ ManualCustomer
NS✓ Supported

BGP Support

○ ManualCustomer
DP✓ Supported

AES-256-GCM Encryption

○ ManualCustomer
LT✓ Supported

Tunnel Diagnostics

○ ManualCustomer
BR✓ Supported

Active-Active Mode

○ ManualCustomer

Azure Portal Instructions

VPN Gateway Baseline Configuration Workflow
  1. Navigate to your Virtual network gateway resource in the Azure Portal.
  2. Under Configuration, confirm the gateway is route-based and enable active-active mode if high availability is required.
  3. Enable BGP if you need dynamic route exchange with on-premises devices.
  4. Open the connection resource and configure a custom IPsec/IKE policy with strong ciphers such as AES-256 and SHA-256.
  5. Under Monitoring, enable diagnostic settings for tunnel status and gateway diagnostics.
  6. Validate tunnel state, route advertisement, and failover behavior after the policy is applied.

Terraform: Hardened VPN Gateway with Custom IPsec

hcl
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

Store VPN pre-shared keys in Key Vault — never in Terraform state, pipeline variables, or documentation. Rotate keys quarterly and use certificate-based authentication where possible.

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.

PropertyValue
Defender PlanN/A — built-in WAF analytics, integrate with Sentinel
Azure Policy Built-inYes — WAF enablement, TLS version, WAF mode
Recommended SKUWAF v2 (auto-scaling, zone-redundant, managed rulesets)
Key FeatureVNet-native deployment with private frontend IP for internal apps

Baseline Controls

NS✓ Supported

VNet Deployment

● DefaultCustomer
NS✓ Supported

WAF Integration

○ ManualCustomer
NS✓ Supported

Private Frontend

○ ManualCustomer
IM✓ Supported

Managed Identity

○ ManualCustomer
DP✓ Supported

End-to-End TLS

○ ManualCustomer
LT✓ Supported

Diagnostic Logging

○ ManualCustomer

Terraform: Hardened Application Gateway WAF v2

hcl
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.

PropertyValue
Defender PlanN/A — session logs integrate with Sentinel
Required SubnetAzureBastionSubnet (/26 minimum)
Recommended SKUStandard (session recording, shareable links, native client)
Key FeatureBrowser-based RDP/SSH over TLS — no public IPs on VMs

Baseline Controls

NS✓ Supported

VNet-Only Access

○ ManualCustomer
NS✓ Supported

NSG Lockdown

○ ManualCustomer
IM✓ Supported

Entra ID VM Login

○ ManualCustomer
DP✓ Supported

TLS Encryption

● DefaultMicrosoft
LT✓ Supported

Session Recording

○ ManualCustomer

Terraform: Standard Bastion with Logging

hcl
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.

PropertyValue
Defender PlanN/A — monitor via Network Watcher and Sentinel
Encryption OptionsMACsec (Direct), IPsec VPN overlay, or application-layer TLS
Recommended SKUPremium for Global Reach and cross-region connectivity
Key FeaturePrivate Layer 3 connectivity — no public internet traversal

Baseline Controls

NS✓ Supported

Private Peering

○ ManualCustomer
NS✓ Supported

Route Filtering

○ ManualCustomer
DP✓ Supported

MACsec Encryption

○ ManualCustomer
DP✓ Supported

IPsec over ER

○ ManualCustomer
LT✓ Supported

Circuit Diagnostics

○ ManualCustomer

Terraform: ExpressRoute Circuit with Gateway

hcl
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

While ExpressRoute provides private connectivity, the traffic is not encrypted at the network layer. For compliance requirements, enable MACsec on ExpressRoute Direct ports or deploy a VPN tunnel over ExpressRoute private peering for IPsec encryption.