Skip to content

Connectivity Landing Zone

Related: README | Management Landing Zone | Application Landing Zone

Overview

The Connectivity Landing Zone provides the network foundation for the entire Azure environment. For a cloud-only SaaS startup, this is a simplified hub-spoke topology optimized for cost and operational efficiency.

Architecture

graph TB
    subgraph "Internet"
        USR[Users]
        AFD[Azure Front Door<br/>Global Load Balancer + WAF]
    end

    subgraph "Region 1 - Primary"
        subgraph "Hub VNet - Region 1"
            HUB1[Hub VNet<br/>10.0.0.0/16]
            FW1[Azure Firewall<br/>Day 2]
            BAS1[Azure Bastion<br/>Secure Access]
            DNS1[Private DNS Resolver<br/>Conditional Forwarding]
        end

        subgraph "Spokes - Region 1"
            SPOKE1A[Production Spoke<br/>10.1.0.0/16]
            SPOKE1B[Non-Prod Spoke<br/>10.2.0.0/16]
        end

        HUB1 --- SPOKE1A
        HUB1 --- SPOKE1B
    end

    subgraph "Region 2 - Secondary"
        subgraph "Hub VNet - Region 2"
            HUB2[Hub VNet<br/>10.100.0.0/16]
            FW2[Azure Firewall<br/>Day 2]
            BAS2[Azure Bastion<br/>Secure Access]
        end

        subgraph "Spokes - Region 2"
            SPOKE2A[Production Spoke<br/>10.101.0.0/16]
            SPOKE2B[Non-Prod Spoke<br/>10.102.0.0/16]
        end

        HUB2 --- SPOKE2A
        HUB2 --- SPOKE2B
    end

    USR --> AFD
    AFD --> SPOKE1A
    AFD --> SPOKE2A
    HUB1 -.->|Global Peering| HUB2

    style AFD fill:#e8f5e9
    style HUB1 fill:#e3f2fd
    style HUB2 fill:#e3f2fd

Network Topology Decision

Cloud-Only Considerations

Since this is a cloud-only SaaS deployment with no on-premises connectivity:

Requirement Traditional Enterprise Cloud-Only SaaS (Our Choice)
ExpressRoute/VPN Required Not needed
Virtual Network Gateway Required Not needed
Hub complexity High Simplified
Firewall requirement Day 1 Day 2 (cost optimization)
DNS resolution Hybrid DNS Azure DNS Private Zones

Topology Options Comparison

Option Day 1 Cost Complexity Scalability Recommendation
Hub-Spoke with Azure Firewall High (~$900/mo/region) Medium High Day 2
Hub-Spoke with NSGs only Low Low Medium Day 1
Azure Virtual WAN Very High (~$1,500/mo) Low Very High Not for startup
Flat network (no hub) Lowest Very Low Low Not recommended

IP Address Planning

Address Space Allocation

ip_address_plan:
  # Region 1 (Primary)
  region_1:
    name: "eastus2"
    hub:
      address_space: "10.0.0.0/16"
      subnets:
        AzureFirewallSubnet: "10.0.0.0/26"      # Required name
        AzureBastionSubnet: "10.0.1.0/26"       # Required name
        GatewaySubnet: "10.0.2.0/26"            # Reserved for future
        PrivateEndpoints: "10.0.10.0/24"
        DnsResolverInbound: "10.0.20.0/28"
        DnsResolverOutbound: "10.0.20.16/28"
        Management: "10.0.100.0/24"
    spokes:
      production:
        address_space: "10.1.0.0/16"
        subnets:
          AppService: "10.1.0.0/24"
          ContainerApps: "10.1.1.0/23"
          PrivateEndpoints: "10.1.10.0/24"
          AKS: "10.1.16.0/20"
      non_production:
        address_space: "10.2.0.0/16"
        subnets:
          AppService: "10.2.0.0/24"
          ContainerApps: "10.2.1.0/23"
          PrivateEndpoints: "10.2.10.0/24"

  # Region 2 (Secondary)
  region_2:
    name: "westus2"
    hub:
      address_space: "10.100.0.0/16"
      subnets:
        AzureFirewallSubnet: "10.100.0.0/26"
        AzureBastionSubnet: "10.100.1.0/26"
        GatewaySubnet: "10.100.2.0/26"
        PrivateEndpoints: "10.100.10.0/24"
        Management: "10.100.100.0/24"
    spokes:
      production:
        address_space: "10.101.0.0/16"
      non_production:
        address_space: "10.102.0.0/16"

Subnet Sizing Guidelines

Workload Type Minimum Size Recommended Notes
Azure Firewall /26 /26 Fixed requirement
Azure Bastion /26 /26 Fixed requirement
AKS Node Pool /24 /20 ~250 nodes per /20
Container Apps /23 /23 Environment requirement
App Service VNet Integration /28 /24 Per plan, allow growth
Private Endpoints /26 /24 ~250 endpoints per /24
DNS Resolver /28 /28 Per direction

Hub Virtual Network

Hub Network Configuration

hub_virtual_network:
  name: "vnet-hub-{region}-001"
  resource_group: "rg-connectivity-hub-{region}"
  location: "{region}"
  address_space:
    - "10.0.0.0/16"  # Region 1
    # - "10.100.0.0/16"  # Region 2

  dns_servers: []  # Use Azure-provided DNS

  subnets:
    - name: "AzureFirewallSubnet"
      address_prefix: "10.0.0.0/26"
      # No NSG - managed by Azure Firewall

    - name: "AzureBastionSubnet"
      address_prefix: "10.0.1.0/26"
      # No NSG - managed by Azure Bastion

    - name: "snet-privateendpoints"
      address_prefix: "10.0.10.0/24"
      network_security_group: "nsg-hub-privateendpoints-{region}"
      private_endpoint_network_policies: "Disabled"

    - name: "snet-management"
      address_prefix: "10.0.100.0/24"
      network_security_group: "nsg-hub-management-{region}"

Hub Peering Strategy

graph LR
    subgraph "Region 1"
        H1[Hub VNet<br/>10.0.0.0/16]
        S1A[Prod Spoke<br/>10.1.0.0/16]
        S1B[NonProd Spoke<br/>10.2.0.0/16]

        H1 <-->|"Peering<br/>Allow Gateway Transit"| S1A
        H1 <-->|"Peering<br/>Allow Gateway Transit"| S1B
    end

    subgraph "Region 2"
        H2[Hub VNet<br/>10.100.0.0/16]
        S2A[Prod Spoke<br/>10.101.0.0/16]
        S2B[NonProd Spoke<br/>10.102.0.0/16]

        H2 <-->|Peering| S2A
        H2 <-->|Peering| S2B
    end

    H1 <-.->|"Global Peering<br/>Cross-Region Traffic"| H2

    style H1 fill:#e3f2fd
    style H2 fill:#e3f2fd

VNet Peering Configuration

vnet_peering:
  # Hub to Spoke peering (same region)
  hub_to_spoke:
    name: "peer-hub-to-{spoke-name}"
    allow_virtual_network_access: true
    allow_forwarded_traffic: true
    allow_gateway_transit: true  # For future gateway
    use_remote_gateways: false

  # Spoke to Hub peering
  spoke_to_hub:
    name: "peer-{spoke-name}-to-hub"
    allow_virtual_network_access: true
    allow_forwarded_traffic: true
    allow_gateway_transit: false
    use_remote_gateways: false  # True when gateway deployed

  # Hub to Hub (cross-region)
  hub_to_hub:
    name: "peer-hub-{region1}-to-hub-{region2}"
    allow_virtual_network_access: true
    allow_forwarded_traffic: true
    allow_gateway_transit: false

Network Security

Day 1: NSG-Based Security

Network Security Groups provide the primary network security layer for Day 1.

flowchart TB
    subgraph "Inbound Traffic Flow"
        INT[Internet]
        AFD[Azure Front Door]
        WAF[WAF Policy]
        NSG1[NSG - App Subnet]
        APP[Application]

        INT --> AFD
        AFD --> WAF
        WAF --> NSG1
        NSG1 --> APP
    end

    subgraph "Internal Traffic Flow"
        APP2[Application]
        NSG2[NSG - DB Subnet]
        PE[Private Endpoint]
        DB[(Database)]

        APP2 --> NSG2
        NSG2 --> PE
        PE --> DB
    end

NSG Rule Templates

Application Subnet NSG

nsg_rules:
  application_subnet:
    inbound_rules:
      - name: "Allow-FrontDoor"
        priority: 100
        direction: "Inbound"
        access: "Allow"
        protocol: "Tcp"
        source_address_prefix: "AzureFrontDoor.Backend"
        source_port_range: "*"
        destination_address_prefix: "VirtualNetwork"
        destination_port_ranges:
          - "443"
          - "80"

      - name: "Allow-LoadBalancer"
        priority: 110
        direction: "Inbound"
        access: "Allow"
        protocol: "*"
        source_address_prefix: "AzureLoadBalancer"
        source_port_range: "*"
        destination_address_prefix: "*"
        destination_port_range: "*"

      - name: "Deny-All-Inbound"
        priority: 4096
        direction: "Inbound"
        access: "Deny"
        protocol: "*"
        source_address_prefix: "*"
        source_port_range: "*"
        destination_address_prefix: "*"
        destination_port_range: "*"

    outbound_rules:
      - name: "Allow-HTTPS-Outbound"
        priority: 100
        direction: "Outbound"
        access: "Allow"
        protocol: "Tcp"
        source_address_prefix: "VirtualNetwork"
        source_port_range: "*"
        destination_address_prefix: "Internet"
        destination_port_range: "443"

      - name: "Allow-AzureServices"
        priority: 200
        direction: "Outbound"
        access: "Allow"
        protocol: "Tcp"
        source_address_prefix: "VirtualNetwork"
        source_port_range: "*"
        destination_address_prefix: "AzureCloud"
        destination_port_ranges:
          - "443"
          - "1433"  # SQL
          - "5432"  # PostgreSQL

Private Endpoint Subnet NSG

nsg_rules:
  private_endpoint_subnet:
    inbound_rules:
      - name: "Allow-VNet-Inbound"
        priority: 100
        direction: "Inbound"
        access: "Allow"
        protocol: "Tcp"
        source_address_prefix: "VirtualNetwork"
        source_port_range: "*"
        destination_address_prefix: "VirtualNetwork"
        destination_port_ranges:
          - "443"
          - "1433"
          - "5432"
          - "6379"  # Redis

      - name: "Deny-All-Inbound"
        priority: 4096
        direction: "Inbound"
        access: "Deny"
        protocol: "*"
        source_address_prefix: "*"
        source_port_range: "*"
        destination_address_prefix: "*"
        destination_port_range: "*"

Day 2: Azure Firewall

Azure Firewall provides centralized network security with FQDN filtering, threat intelligence, and TLS inspection.

azure_firewall:
  name: "afw-hub-{region}-001"
  resource_group: "rg-connectivity-hub-{region}"
  location: "{region}"
  sku:
    name: "AZFW_VNet"
    tier: "Standard"  # or "Premium" for TLS inspection

  threat_intel_mode: "Alert"  # Start with Alert, move to Deny

  ip_configuration:
    name: "ipconfig-fw"
    subnet_id: "/subscriptions/.../AzureFirewallSubnet"
    public_ip_address_id: "/subscriptions/.../pip-afw-{region}"

  # Firewall Policy (managed separately)
  firewall_policy_id: "/subscriptions/.../afwp-platform-{region}"

Firewall Policy

firewall_policy:
  name: "afwp-platform-{region}"
  sku: "Standard"

  rule_collection_groups:
    - name: "rcg-platform-rules"
      priority: 100

      application_rule_collections:
        - name: "arc-allow-microsoft"
          priority: 100
          action: "Allow"
          rules:
            - name: "Allow-WindowsUpdate"
              source_addresses: ["10.0.0.0/8"]
              protocols:
                - type: "Https"
                  port: 443
              target_fqdns:
                - "*.windowsupdate.com"
                - "*.microsoft.com"
                - "*.azure.com"

            - name: "Allow-AzureMonitor"
              source_addresses: ["10.0.0.0/8"]
              protocols:
                - type: "Https"
                  port: 443
              target_fqdns:
                - "*.ods.opinsights.azure.com"
                - "*.oms.opinsights.azure.com"
                - "*.monitoring.azure.com"

      network_rule_collections:
        - name: "nrc-allow-dns"
          priority: 200
          action: "Allow"
          rules:
            - name: "Allow-DNS"
              source_addresses: ["10.0.0.0/8"]
              destination_addresses: ["168.63.129.16"]
              destination_ports: ["53"]
              protocols: ["UDP", "TCP"]

DDoS Protection

DDoS Protection Plan

ddos_protection_plan:
  name: "ddos-platform-001"
  resource_group: "rg-connectivity-shared"
  location: "eastus2"  # Region doesn't matter for DDoS

  # Associate with all production VNets
  associated_vnets:
    - "vnet-hub-eastus2-001"
    - "vnet-hub-westus2-001"
    - "vnet-spoke-prod-eastus2-001"
    - "vnet-spoke-prod-westus2-001"

DDoS Cost Consideration

Note: Azure DDoS Protection Standard costs ~$2,944/month per plan (covers up to 100 public IPs). For a startup, consider:

  1. Day 1: Use Azure Front Door's built-in DDoS protection (free)
  2. Day 2: Add DDoS Protection Standard when handling sensitive/high-value traffic

Private Endpoints

Private Endpoint Strategy

graph TB
    subgraph "Application VNet"
        APP[Application<br/>Container Apps / AKS]
    end

    subgraph "Private Endpoints Subnet"
        PE1[PE - Storage]
        PE2[PE - SQL Database]
        PE3[PE - Key Vault]
        PE4[PE - Cosmos DB]
        PE5[PE - Redis Cache]
    end

    subgraph "Azure PaaS Services"
        ST[(Storage Account)]
        SQL[(Azure SQL)]
        KV[(Key Vault)]
        CDB[(Cosmos DB)]
        RED[(Redis)]
    end

    APP --> PE1 --> ST
    APP --> PE2 --> SQL
    APP --> PE3 --> KV
    APP --> PE4 --> CDB
    APP --> PE5 --> RED

Private Endpoint Configuration

private_endpoint:
  naming_convention: "pe-{service-name}-{resource-type}-{region}"

  examples:
    - name: "pe-saasdb-sql-eastus2"
      resource_group: "rg-app-saas-prod-eastus2"
      location: "eastus2"
      subnet_id: "/subscriptions/.../snet-privateendpoints"

      private_service_connection:
        name: "psc-saasdb-sql"
        private_connection_resource_id: "/subscriptions/.../servers/sql-saas-prod"
        subresource_names: ["sqlServer"]
        is_manual_connection: false

      private_dns_zone_group:
        name: "pdzg-sql"
        private_dns_zone_ids:
          - "/subscriptions/.../privateDnsZones/privatelink.database.windows.net"

Private DNS Zones

Service Private DNS Zone Linked VNets
Storage Blob privatelink.blob.core.windows.net All hubs and spokes
Storage File privatelink.file.core.windows.net All hubs and spokes
Azure SQL privatelink.database.windows.net All hubs and spokes
Cosmos DB privatelink.documents.azure.com All hubs and spokes
Key Vault privatelink.vaultcore.azure.net All hubs and spokes
ACR privatelink.azurecr.io All hubs and spokes
Redis privatelink.redis.cache.windows.net All hubs and spokes
Event Hubs privatelink.servicebus.windows.net All hubs and spokes
Service Bus privatelink.servicebus.windows.net All hubs and spokes

Private DNS Zone Configuration

private_dns_zone:
  name: "privatelink.database.windows.net"
  resource_group: "rg-connectivity-dns-{region}"

  virtual_network_links:
    - name: "link-hub-eastus2"
      virtual_network_id: "/subscriptions/.../vnet-hub-eastus2-001"
      registration_enabled: false

    - name: "link-spoke-prod-eastus2"
      virtual_network_id: "/subscriptions/.../vnet-spoke-prod-eastus2-001"
      registration_enabled: false

Azure DNS Private Resolver

Deep dive: See Private DNS Zones and Azure DNS Private Resolver for a detailed explanation of centralized vs. distributed DNS patterns, name collision resolution, and the role of each component in enterprise hub-spoke topologies.

For complex DNS scenarios and conditional forwarding:

dns_private_resolver:
  name: "dnspr-hub-{region}-001"
  resource_group: "rg-connectivity-dns-{region}"
  location: "{region}"
  virtual_network_id: "/subscriptions/.../vnet-hub-{region}-001"

  inbound_endpoints:
    - name: "inbound-endpoint"
      subnet_id: "/subscriptions/.../snet-dnsresolver-inbound"
      private_ip_allocation: "Dynamic"

  outbound_endpoints:
    - name: "outbound-endpoint"
      subnet_id: "/subscriptions/.../snet-dnsresolver-outbound"

  # Forwarding rules (if needed for external DNS)
  forwarding_rulesets:
    - name: "ruleset-external"
      rules:
        - name: "forward-partner-domain"
          domain_name: "partner.example.com."
          target_dns_servers:
            - ip_address: "203.0.113.53"
              port: 53

Azure Front Door

Global Load Balancing Architecture

graph TB
    subgraph "Internet"
        USR[Users Worldwide]
    end

    subgraph "Azure Front Door"
        AFD[Front Door Profile<br/>Premium Tier]
        WAF[WAF Policy<br/>OWASP 3.2]
        EP1[Endpoint: api.saas.com]
        EP2[Endpoint: app.saas.com]
    end

    subgraph "Origin Groups"
        OG1[API Origins]
        OG2[App Origins]
    end

    subgraph "Region 1 - East US 2"
        API1[API - Container Apps]
        APP1[App - Static Web Apps]
    end

    subgraph "Region 2 - West US 2"
        API2[API - Container Apps]
        APP2[App - Static Web Apps]
    end

    USR --> AFD
    AFD --> WAF
    WAF --> EP1
    WAF --> EP2

    EP1 --> OG1
    EP2 --> OG2

    OG1 --> API1
    OG1 --> API2
    OG2 --> APP1
    OG2 --> APP2

Front Door Configuration

azure_front_door:
  name: "afd-saas-global-001"
  resource_group: "rg-connectivity-frontdoor"
  sku: "Premium_AzureFrontDoor"  # Required for Private Link origins

  endpoints:
    - name: "ep-api"
      enabled: true
      custom_domains:
        - host_name: "api.saas.com"
          certificate_type: "ManagedCertificate"
      routes:
        - name: "route-api"
          patterns_to_match: ["/*"]
          origin_group: "og-api"
          forwarding_protocol: "HttpsOnly"
          https_redirect: true

    - name: "ep-app"
      enabled: true
      custom_domains:
        - host_name: "app.saas.com"
          certificate_type: "ManagedCertificate"

  origin_groups:
    - name: "og-api"
      load_balancing:
        sample_size: 4
        successful_samples_required: 3
        additional_latency_in_milliseconds: 50
      health_probe:
        path: "/health"
        protocol: "Https"
        interval_in_seconds: 30
      origins:
        - name: "api-eastus2"
          host_name: "api-eastus2.internal.saas.com"
          http_port: 80
          https_port: 443
          priority: 1
          weight: 1000
          enabled: true
          private_link:
            private_link_target_id: "/subscriptions/.../containerApps/..."
            location: "eastus2"

        - name: "api-westus2"
          host_name: "api-westus2.internal.saas.com"
          priority: 2  # Failover
          weight: 1000
          enabled: true

WAF Policy

waf_policy:
  name: "wafp-saas-global"
  resource_group: "rg-connectivity-frontdoor"
  sku: "Premium_AzureFrontDoor"

  policy_settings:
    enabled: true
    mode: "Prevention"  # Start with "Detection" and move to "Prevention"

  managed_rules:
    - type: "Microsoft_DefaultRuleSet"
      version: "2.1"

    - type: "Microsoft_BotManagerRuleSet"
      version: "1.0"

  custom_rules:
    - name: "RateLimitByIP"
      priority: 1
      rate_limit_duration_in_minutes: 1
      rate_limit_threshold: 1000
      action: "Block"
      match_conditions:
        - match_variable: "RemoteAddr"
          operator: "IPMatch"
          negation: false
          match_values: ["0.0.0.0/0"]

Azure Bastion

Secure administrative access without exposing VMs to the internet:

azure_bastion:
  name: "bas-hub-{region}-001"
  resource_group: "rg-connectivity-hub-{region}"
  location: "{region}"
  sku: "Standard"  # For native client support, IP-based connections

  ip_configuration:
    name: "ipconfig-bastion"
    subnet_id: "/subscriptions/.../AzureBastionSubnet"
    public_ip_address_id: "/subscriptions/.../pip-bastion-{region}"

  features:
    copy_paste: true
    file_copy: true
    ip_connect: true  # Connect by IP address
    shareable_link: false  # Disable for security
    tunneling: true  # Native client support

Cross-Region Connectivity

Multi-Region Traffic Flow

sequenceDiagram
    participant User
    participant AFD as Azure Front Door
    participant R1 as Region 1 (Primary)
    participant R2 as Region 2 (Secondary)
    participant DB as Cosmos DB (Multi-Region)

    User->>AFD: HTTPS Request
    AFD->>AFD: WAF Inspection

    alt Primary Region Healthy
        AFD->>R1: Route to Primary
        R1->>DB: Read/Write (Strong Consistency)
        DB->>R1: Response
        R1->>AFD: Response
    else Primary Region Unhealthy
        AFD->>R2: Failover to Secondary
        R2->>DB: Read/Write
        DB->>R2: Response
        R2->>AFD: Response
    end

    AFD->>User: HTTPS Response

Cross-Region Database Replication

For multi-region SaaS, use Azure services with built-in replication:

Service Replication Method Consistency RTO RPO
Cosmos DB Active-Active Configurable <1 min 0-5 min
Azure SQL Failover Groups Strong <30 sec <5 sec
Redis Cache Geo-Replication Eventual Minutes Seconds
Storage GRS/GZRS Eventual Hours <15 min

Network Monitoring

Network Watcher Configuration

network_watcher:
  # Auto-created per region when needed

  flow_logs:
    - name: "flowlog-nsg-hub-{region}"
      network_security_group_id: "/subscriptions/.../nsg-hub-{region}"
      storage_account_id: "/subscriptions/.../stflowlogs{region}"
      enabled: true
      retention_days: 90

      traffic_analytics:
        enabled: true
        workspace_id: "/subscriptions/.../law-platform-{region}"
        interval_in_minutes: 10

  connection_monitors:
    - name: "cm-crossregion-connectivity"
      location: "eastus2"
      endpoints:
        - name: "hub-eastus2"
          type: "AzureVM"
          resource_id: "/subscriptions/.../vm-jumpbox-eastus2"
        - name: "hub-westus2"
          type: "AzureVM"
          resource_id: "/subscriptions/.../vm-jumpbox-westus2"
      test_configurations:
        - name: "tcp-443"
          protocol: "TCP"
          tcp_configuration:
            port: 443

Implementation Checklist

Day 1 - MVP Connectivity

  • [ ] Create hub VNets in each region
  • [ ] Configure VNet peering (hub-spoke, hub-hub)
  • [ ] Deploy NSGs with baseline rules
  • [ ] Set up Private DNS Zones
  • [ ] Deploy Azure Bastion (one region minimum)
  • [ ] Configure Azure Front Door with WAF
  • [ ] Enable DDoS at Front Door level

Day 2 - Enhanced Security

  • [ ] Deploy Azure Firewall
  • [ ] Migrate from NSG-only to Firewall-based security
  • [ ] Enable DDoS Protection Standard
  • [ ] Configure DNS Private Resolver
  • [ ] Enable Network Watcher flow logs
  • [ ] Set up Traffic Analytics

References


Previous: 02 - Management Landing Zone | Next: 04 - EA & Subscription Architecture