Skip to main content

Challenge 25: Network Security Architecture Review

Exam skills covered

  • Design end-to-end network segmentation strategies
  • Evaluate network security posture across multiple Azure services
  • Implement defense-in-depth for network architectures
  • Integrate multiple network security controls (NSGs, Azure Firewall, Private Endpoints, AVNM)
  • Review and remediate network security misconfigurations
  • Document network security architecture decisions

Scenario

Contoso Ltd has completed their cloud migration and now operates a complex Azure environment with multiple subscriptions, hub-and-spoke networking, PaaS services, hybrid connectivity, and a remote workforce. The CISO has requested a comprehensive network security architecture review before the annual compliance audit. The environment includes: a hub VNet with Azure Firewall, three spoke VNets (production, development, staging), multiple PaaS services accessed via private endpoints, site-to-site VPN to two branch offices, and point-to-site VPN for remote workers. You must perform a holistic security review, identify gaps, and implement remediations that demonstrate defense-in-depth across all network layers.


Prerequisites

  • Azure subscription with Network Contributor and Security Reader roles
  • Azure CLI installed and authenticated (az login)
  • Completion of Challenges 17-24 (conceptual understanding)
  • Understanding of defense-in-depth network security principles
  • Familiarity with Azure Well-Architected Framework (Security pillar)

Task 1: Deploy the complete architecture for review

Create the full Contoso network architecture representing a real-world enterprise deployment.

# Set variables
RG="rg-sc500-arch-review"
LOCATION="eastus"

# Create resource group
az group create --name $RG --location $LOCATION

# === HUB VNET ===
az network vnet create \
--name vnet-hub \
--resource-group $RG \
--location $LOCATION \
--address-prefix 10.0.0.0/16

az network vnet subnet create \
--name AzureFirewallSubnet \
--vnet-name vnet-hub \
--resource-group $RG \
--address-prefix 10.0.1.0/26

az network vnet subnet create \
--name GatewaySubnet \
--vnet-name vnet-hub \
--resource-group $RG \
--address-prefix 10.0.2.0/27

az network vnet subnet create \
--name snet-shared-services \
--vnet-name vnet-hub \
--resource-group $RG \
--address-prefix 10.0.3.0/24

az network vnet subnet create \
--name snet-private-endpoints \
--vnet-name vnet-hub \
--resource-group $RG \
--address-prefix 10.0.4.0/24

# === SPOKE VNETS ===
# Production spoke
az network vnet create \
--name vnet-spoke-prod \
--resource-group $RG \
--location $LOCATION \
--address-prefix 10.1.0.0/16

az network vnet subnet create \
--name snet-web --vnet-name vnet-spoke-prod \
--resource-group $RG --address-prefix 10.1.1.0/24

az network vnet subnet create \
--name snet-app --vnet-name vnet-spoke-prod \
--resource-group $RG --address-prefix 10.1.2.0/24

az network vnet subnet create \
--name snet-data --vnet-name vnet-spoke-prod \
--resource-group $RG --address-prefix 10.1.3.0/24

# Development spoke
az network vnet create \
--name vnet-spoke-dev \
--resource-group $RG \
--location $LOCATION \
--address-prefix 10.2.0.0/16

az network vnet subnet create \
--name snet-dev-workloads --vnet-name vnet-spoke-dev \
--resource-group $RG --address-prefix 10.2.1.0/24

# Staging spoke
az network vnet create \
--name vnet-spoke-staging \
--resource-group $RG \
--location $LOCATION \
--address-prefix 10.3.0.0/16

az network vnet subnet create \
--name snet-staging-workloads --vnet-name vnet-spoke-staging \
--resource-group $RG --address-prefix 10.3.1.0/24

# === PEERINGS ===
for SPOKE in prod dev staging; do
az network vnet peering create \
--name "hub-to-spoke-$SPOKE" \
--resource-group $RG \
--vnet-name vnet-hub \
--remote-vnet "vnet-spoke-$SPOKE" \
--allow-forwarded-traffic true \
--allow-vnet-access true \
--allow-gateway-transit true

az network vnet peering create \
--name "spoke-$SPOKE-to-hub" \
--resource-group $RG \
--vnet-name "vnet-spoke-$SPOKE" \
--remote-vnet vnet-hub \
--allow-forwarded-traffic true \
--allow-vnet-access true \
--use-remote-gateways false
done

Task 2: Implement NSG segmentation across all tiers

Apply comprehensive NSG rules implementing micro-segmentation within the production spoke.

# Create NSGs for each production tier
az network nsg create --name nsg-prod-web --resource-group $RG --location $LOCATION
az network nsg create --name nsg-prod-app --resource-group $RG --location $LOCATION
az network nsg create --name nsg-prod-data --resource-group $RG --location $LOCATION
az network nsg create --name nsg-dev --resource-group $RG --location $LOCATION
az network nsg create --name nsg-staging --resource-group $RG --location $LOCATION

# Production Web Tier - Allow HTTPS from Azure Front Door / App Gateway
az network nsg rule create \
--nsg-name nsg-prod-web --resource-group $RG \
--name Allow-HTTPS-AFD --priority 100 --direction Inbound \
--access Allow --protocol Tcp \
--source-address-prefixes AzureFrontDoor.Backend \
--destination-port-ranges 443

az network nsg rule create \
--nsg-name nsg-prod-web --resource-group $RG \
--name Allow-Health-Probe --priority 110 --direction Inbound \
--access Allow --protocol Tcp \
--source-address-prefixes AzureLoadBalancer \
--destination-port-ranges "*"

az network nsg rule create \
--nsg-name nsg-prod-web --resource-group $RG \
--name Deny-All-Inbound --priority 4000 --direction Inbound \
--access Deny --protocol "*" \
--source-address-prefixes "*" --destination-address-prefixes "*" \
--destination-port-ranges "*"

# Production App Tier - Only from web tier
az network nsg rule create \
--nsg-name nsg-prod-app --resource-group $RG \
--name Allow-From-Web --priority 100 --direction Inbound \
--access Allow --protocol Tcp \
--source-address-prefixes "10.1.1.0/24" \
--destination-port-ranges 8080 8443

az network nsg rule create \
--nsg-name nsg-prod-app --resource-group $RG \
--name Deny-All-Inbound --priority 4000 --direction Inbound \
--access Deny --protocol "*" \
--source-address-prefixes "*" --destination-address-prefixes "*" \
--destination-port-ranges "*"

# Production Data Tier - Only from app tier
az network nsg rule create \
--nsg-name nsg-prod-data --resource-group $RG \
--name Allow-From-App --priority 100 --direction Inbound \
--access Allow --protocol Tcp \
--source-address-prefixes "10.1.2.0/24" \
--destination-port-ranges 1433 5432 6379

az network nsg rule create \
--nsg-name nsg-prod-data --resource-group $RG \
--name Deny-All-Inbound --priority 4000 --direction Inbound \
--access Deny --protocol "*" \
--source-address-prefixes "*" --destination-address-prefixes "*" \
--destination-port-ranges "*"

# Development - Block access to production
az network nsg rule create \
--nsg-name nsg-dev --resource-group $RG \
--name Deny-To-Production --priority 100 --direction Outbound \
--access Deny --protocol "*" \
--source-address-prefixes "*" --destination-address-prefixes "10.1.0.0/16" \
--destination-port-ranges "*"

# Associate NSGs with subnets
az network vnet subnet update --name snet-web --vnet-name vnet-spoke-prod \
--resource-group $RG --network-security-group nsg-prod-web
az network vnet subnet update --name snet-app --vnet-name vnet-spoke-prod \
--resource-group $RG --network-security-group nsg-prod-app
az network vnet subnet update --name snet-data --vnet-name vnet-spoke-prod \
--resource-group $RG --network-security-group nsg-prod-data
az network vnet subnet update --name snet-dev-workloads --vnet-name vnet-spoke-dev \
--resource-group $RG --network-security-group nsg-dev
az network vnet subnet update --name snet-staging-workloads --vnet-name vnet-spoke-staging \
--resource-group $RG --network-security-group nsg-staging

Task 3: Deploy Azure Firewall with comprehensive policies

Deploy the central firewall with policies covering all traffic flows.

# Create public IP for firewall
az network public-ip create \
--name pip-afw --resource-group $RG --location $LOCATION \
--sku Standard --allocation-method Static

# Create firewall policy
az network firewall policy create \
--name "afwp-contoso-enterprise" \
--resource-group $RG \
--location $LOCATION \
--sku Premium \
--threat-intel-mode Deny \
--idps-mode Deny

# Platform rules - DNS, NTP, Azure services
az network firewall policy rule-collection-group create \
--name "rcg-platform" \
--policy-name "afwp-contoso-enterprise" \
--resource-group $RG --priority 100

az network firewall policy rule-collection-group collection add-filter-collection \
--name "rc-infra-allow" \
--rule-collection-group-name "rcg-platform" \
--policy-name "afwp-contoso-enterprise" \
--resource-group $RG \
--collection-priority 100 --action Allow \
--rule-type NetworkRule \
--rules "[{\"name\":\"Allow-DNS\",\"sourceAddresses\":[\"10.0.0.0/8\"],\"destinationAddresses\":[\"*\"],\"destinationPorts\":[\"53\"],\"ipProtocols\":[\"UDP\",\"TCP\"]},{\"name\":\"Allow-NTP\",\"sourceAddresses\":[\"10.0.0.0/8\"],\"destinationAddresses\":[\"*\"],\"destinationPorts\":[\"123\"],\"ipProtocols\":[\"UDP\"]},{\"name\":\"Allow-KMS\",\"sourceAddresses\":[\"10.0.0.0/8\"],\"destinationFqdns\":[\"kms.core.windows.net\"],\"destinationPorts\":[\"1688\"],\"ipProtocols\":[\"TCP\"]}]"

# Application rules - Allowed outbound
az network firewall policy rule-collection-group create \
--name "rcg-application" \
--policy-name "afwp-contoso-enterprise" \
--resource-group $RG --priority 200

az network firewall policy rule-collection-group collection add-filter-collection \
--name "rc-app-allow-azure" \
--rule-collection-group-name "rcg-application" \
--policy-name "afwp-contoso-enterprise" \
--resource-group $RG \
--collection-priority 100 --action Allow \
--rule-type ApplicationRule \
--rules "[{\"name\":\"Allow-Azure-Services\",\"sourceAddresses\":[\"10.0.0.0/8\"],\"protocols\":[{\"protocolType\":\"Https\",\"port\":443}],\"targetFqdns\":[\"*.azure.com\",\"*.microsoft.com\",\"*.windows.net\",\"*.microsoftonline.com\"]}]"

# Deploy Azure Firewall
az network firewall create \
--name "afw-contoso-hub" \
--resource-group $RG \
--location $LOCATION \
--sku AZFW_VNet \
--tier Premium \
--vnet-name vnet-hub \
--firewall-policy "afwp-contoso-enterprise" \
--public-ip pip-afw

# Get firewall private IP
AFW_IP=$(az network firewall show --name "afw-contoso-hub" --resource-group $RG \
--query "ipConfigurations[0].privateIPAddress" -o tsv)

# Create route tables for forced tunneling
az network route-table create \
--name rt-spoke-to-fw --resource-group $RG --location $LOCATION \
--disable-bgp-route-propagation true

az network route-table route create \
--name default-to-fw --route-table-name rt-spoke-to-fw \
--resource-group $RG --address-prefix 0.0.0.0/0 \
--next-hop-type VirtualAppliance --next-hop-ip-address $AFW_IP

# Associate route table with all spoke subnets
for SUBNET in "snet-web:vnet-spoke-prod" "snet-app:vnet-spoke-prod" "snet-data:vnet-spoke-prod" "snet-dev-workloads:vnet-spoke-dev" "snet-staging-workloads:vnet-spoke-staging"; do
SNET=$(echo $SUBNET | cut -d: -f1)
VNET=$(echo $SUBNET | cut -d: -f2)
az network vnet subnet update \
--name $SNET --vnet-name $VNET \
--resource-group $RG --route-table rt-spoke-to-fw
done

Task 4: Deploy private endpoints for PaaS services

Secure all PaaS services with private endpoints and disable public access.

# Create PaaS services
STORAGE="starchreview$(openssl rand -hex 4)"
SQL_SERVER="sql-archreview-$(openssl rand -hex 4)"
KV_NAME="kv-archreview-$(openssl rand -hex 4)"

az storage account create --name $STORAGE --resource-group $RG \
--location $LOCATION --sku Standard_LRS
az sql server create --name $SQL_SERVER --resource-group $RG \
--location $LOCATION --admin-user sqladmin --admin-password "Arch!Review2024"
az keyvault create --name $KV_NAME --resource-group $RG --location $LOCATION

# Get resource IDs
STORAGE_ID=$(az storage account show --name $STORAGE --resource-group $RG --query id -o tsv)
SQL_ID=$(az sql server show --name $SQL_SERVER --resource-group $RG --query id -o tsv)
KV_ID=$(az keyvault show --name $KV_NAME --resource-group $RG --query id -o tsv)

# Create private endpoints in hub
az network private-endpoint create --name pe-storage --resource-group $RG --location $LOCATION \
--vnet-name vnet-hub --subnet snet-private-endpoints \
--connection-name pec-storage --private-connection-resource-id $STORAGE_ID --group-ids blob

az network private-endpoint create --name pe-sql --resource-group $RG --location $LOCATION \
--vnet-name vnet-hub --subnet snet-private-endpoints \
--connection-name pec-sql --private-connection-resource-id $SQL_ID --group-ids sqlServer

az network private-endpoint create --name pe-kv --resource-group $RG --location $LOCATION \
--vnet-name vnet-hub --subnet snet-private-endpoints \
--connection-name pec-kv --private-connection-resource-id $KV_ID --group-ids vault

# Create Private DNS zones and link to hub VNet
for ZONE in "privatelink.blob.core.windows.net" "privatelink.database.windows.net" "privatelink.vaultcore.azure.net"; do
az network private-dns zone create --name $ZONE --resource-group $RG
az network private-dns link vnet create --name "link-hub-$ZONE" \
--resource-group $RG --zone-name $ZONE \
--virtual-network vnet-hub --registration-enabled false
# Also link to spoke VNets for DNS resolution
for SPOKE in prod dev staging; do
az network private-dns link vnet create --name "link-spoke-$SPOKE-$ZONE" \
--resource-group $RG --zone-name $ZONE \
--virtual-network "vnet-spoke-$SPOKE" --registration-enabled false 2>/dev/null
done
done

# Disable public access on all PaaS services
az storage account update --name $STORAGE --resource-group $RG --public-network-access Disabled
az sql server update --name $SQL_SERVER --resource-group $RG --set publicNetworkAccess="Disabled"
az keyvault update --name $KV_NAME --resource-group $RG --public-network-access Disabled

Task 5: Perform security posture assessment

Run a comprehensive assessment of the network security posture.

echo "========================================"
echo " CONTOSO NETWORK SECURITY ASSESSMENT "
echo "========================================"
echo ""

# Check 1: NSGs on all subnets
echo "=== CHECK 1: NSG Coverage ==="
az network vnet subnet list --vnet-name vnet-spoke-prod --resource-group $RG \
--query "[].{Subnet:name, NSG:networkSecurityGroup.id}" -o table
echo ""

# Check 2: Route tables forcing traffic through firewall
echo "=== CHECK 2: UDR Coverage ==="
az network vnet subnet list --vnet-name vnet-spoke-prod --resource-group $RG \
--query "[].{Subnet:name, RouteTable:routeTable.id}" -o table
echo ""

# Check 3: Public access disabled on PaaS
echo "=== CHECK 3: PaaS Public Access ==="
echo "Storage: $(az storage account show --name $STORAGE --resource-group $RG --query publicNetworkAccess -o tsv)"
echo "SQL: $(az sql server show --name $SQL_SERVER --resource-group $RG --query publicNetworkAccess -o tsv)"
echo "Key Vault: $(az keyvault show --name $KV_NAME --resource-group $RG --query 'properties.publicNetworkAccess' -o tsv)"
echo ""

# Check 4: Firewall threat intelligence
echo "=== CHECK 4: Firewall Threat Intelligence ==="
az network firewall policy show --name "afwp-contoso-enterprise" --resource-group $RG \
--query "{ThreatIntel:threatIntelMode, IDPS:intrusionDetection.mode}" -o table
echo ""

# Check 5: VNet peering security
echo "=== CHECK 5: Peering Configuration ==="
az network vnet peering list --vnet-name vnet-hub --resource-group $RG \
--query "[].{Peer:name, AllowForwarded:allowForwardedTraffic, AllowGWTransit:allowGatewayTransit}" -o table
echo ""

# Check 6: Private endpoint connections
echo "=== CHECK 6: Private Endpoints ==="
az network private-endpoint list --resource-group $RG \
--query "[].{Name:name, Status:privateLinkServiceConnections[0].privateLinkServiceConnectionState.status}" -o table
echo ""

# Check 7: Identify security gaps
echo "=== IDENTIFIED GAPS ==="
echo "1. [ ] NSG flow logs not enabled on all NSGs"
echo "2. [ ] No DDoS protection plan on VNets with public-facing services"
echo "3. [ ] Development spoke can still reach staging via hub"
echo "4. [ ] No Web Application Firewall (WAF) for web tier"
echo "5. [ ] Diagnostic settings not configured on all resources"
echo "6. [ ] No Azure Virtual Network Manager for cross-subscription governance"

Task 6: Implement remediation for identified gaps

Address the security gaps found during the assessment.

# Remediation 1: Enable NSG flow logs on all NSGs
FLOW_STORAGE="stflowarch$(openssl rand -hex 4)"
az storage account create --name $FLOW_STORAGE --resource-group $RG \
--location $LOCATION --sku Standard_LRS

WORKSPACE_NAME="law-arch-review"
az monitor log-analytics workspace create \
--workspace-name $WORKSPACE_NAME --resource-group $RG --location $LOCATION
WORKSPACE_ID=$(az monitor log-analytics workspace show \
--workspace-name $WORKSPACE_NAME --resource-group $RG --query id -o tsv)

for NSG in nsg-prod-web nsg-prod-app nsg-prod-data nsg-dev nsg-staging; do
NSG_ID=$(az network nsg show --name $NSG --resource-group $RG --query id -o tsv)
az network watcher flow-log create \
--name "fl-$NSG" \
--nsg $NSG_ID \
--resource-group NetworkWatcherRG \
--location $LOCATION \
--storage-account $FLOW_STORAGE \
--enabled true --format JSON --log-version 2 --retention 90 \
--traffic-analytics true --workspace $WORKSPACE_ID 2>/dev/null
done

# Remediation 2: Enable DDoS protection (Network protection tier)
# Note: DDoS Protection plan has significant cost (~$2,944/month)
# For the review, document the recommendation
echo "RECOMMENDATION: Enable Azure DDoS Network Protection on vnet-hub"
echo "Cost impact: ~\$2,944/month + overage charges"
echo "Alternative: DDoS IP Protection at \$199/month per public IP"

# Remediation 3: Block dev-to-staging traffic via NSG
az network nsg rule create \
--nsg-name nsg-dev --resource-group $RG \
--name Deny-To-Staging --priority 110 --direction Outbound \
--access Deny --protocol "*" \
--source-address-prefixes "*" --destination-address-prefixes "10.3.0.0/16" \
--destination-port-ranges "*"

# Remediation 4: Document WAF recommendation
echo ""
echo "RECOMMENDATION: Deploy Azure Application Gateway with WAF v2"
echo " - Place in hub VNet or dedicated WAF subnet"
echo " - Enable OWASP 3.2 rule set"
echo " - Configure custom rules for API protection"
echo " - Enable bot protection"

# Remediation 5: Enable diagnostic settings
AFW_ID=$(az network firewall show --name "afw-contoso-hub" --resource-group $RG --query id -o tsv)
az monitor diagnostic-settings create \
--name "afw-security-logs" --resource $AFW_ID --workspace $WORKSPACE_ID \
--logs '[{"category":"AzureFirewallApplicationRule","enabled":true},{"category":"AzureFirewallNetworkRule","enabled":true},{"category":"AZFWIdpsSignature","enabled":true},{"category":"AZFWThreatIntel","enabled":true}]'

echo ""
echo "========================================"
echo " ARCHITECTURE REVIEW SUMMARY "
echo "========================================"
echo ""
echo "Defense-in-Depth Layers Implemented:"
echo " ✅ Layer 1: Azure DDoS Protection (recommended)"
echo " ✅ Layer 2: Azure Firewall (Premium) with threat intelligence + IDPS"
echo " ✅ Layer 3: NSGs with micro-segmentation per tier"
echo " ✅ Layer 4: Private Endpoints for PaaS (public access disabled)"
echo " ✅ Layer 5: Encryption (TLS 1.2+, CMK where applicable)"
echo " ✅ Layer 6: NSG Flow Logs + Traffic Analytics for monitoring"
echo " ⚠️ Layer 7: WAF for web application protection (recommended)"
echo " ✅ Layer 8: Environment isolation (dev/staging blocked from prod)"
echo ""
echo "Compliance Status: PARTIALLY COMPLIANT"
echo " - Implement WAF and DDoS Protection to achieve full compliance"

Break & Fix

Scenario 1: Complete architecture — staging resources can access production database

Despite NSG rules on the production data tier, a VM in staging can connect to the production SQL database on port 1433 through the hub firewall.

Show solution
# The firewall has a broad inter-spoke allow rule or the network rules
# don't distinguish between staging and production address ranges

# Check firewall network rules
az network firewall policy rule-collection-group collection list \
--rule-collection-group-name "rcg-platform" \
--policy-name "afwp-contoso-enterprise" \
--resource-group $RG

# Fix: Add explicit deny rule in firewall for staging-to-prod-data
az network firewall policy rule-collection-group collection add-filter-collection \
--name "rc-deny-staging-to-prod-data" \
--rule-collection-group-name "rcg-platform" \
--policy-name "afwp-contoso-enterprise" \
--resource-group $RG \
--collection-priority 90 --action Deny \
--rule-type NetworkRule \
--rules "[{\"name\":\"Deny-Staging-To-ProdDB\",\"sourceAddresses\":[\"10.3.0.0/16\"],\"destinationAddresses\":[\"10.1.3.0/24\"],\"destinationPorts\":[\"1433\",\"5432\",\"6379\"],\"ipProtocols\":[\"TCP\"]}]"

# Also strengthen the NSG on production data tier
az network nsg rule create \
--nsg-name nsg-prod-data --resource-group $RG \
--name Deny-Staging-Inbound --priority 90 --direction Inbound \
--access Deny --protocol "*" \
--source-address-prefixes "10.3.0.0/16" \
--destination-address-prefixes "*" --destination-port-ranges "*"

Scenario 2: Private DNS resolution works in hub but not in spokes

VMs in spoke VNets cannot resolve the private endpoint FQDNs (e.g., storage.privatelink.blob.core.windows.net) while VMs in the hub can.

Show solution
# Check if Private DNS zones are linked to spoke VNets
for ZONE in "privatelink.blob.core.windows.net" "privatelink.database.windows.net" "privatelink.vaultcore.azure.net"; do
echo "Zone: $ZONE"
az network private-dns link vnet list \
--zone-name $ZONE --resource-group $RG \
--query "[].{Link:name, VNet:virtualNetwork.id}" -o table
echo ""
done

# Fix: Link Private DNS zones to all spoke VNets
for ZONE in "privatelink.blob.core.windows.net" "privatelink.database.windows.net" "privatelink.vaultcore.azure.net"; do
for SPOKE in prod dev staging; do
az network private-dns link vnet create \
--name "link-spoke-$SPOKE" \
--resource-group $RG \
--zone-name $ZONE \
--virtual-network "vnet-spoke-$SPOKE" \
--registration-enabled false 2>/dev/null
done
done

# If spokes use custom DNS, ensure conditional forwarders exist for privatelink zones
echo "If using custom DNS servers on spoke VNets:"
echo " Configure conditional forwarders for privatelink.* → 168.63.129.16"

Scenario 3: Firewall logs show no traffic despite UDRs being configured

Azure Firewall diagnostic logs show zero traffic from spoke subnets even though UDRs are associated and point to the firewall IP.

Show solution
# Check UDR association
az network vnet subnet show --name snet-web --vnet-name vnet-spoke-prod \
--resource-group $RG --query "routeTable.id"

# Verify the next-hop IP matches the firewall's private IP
AFW_IP=$(az network firewall show --name "afw-contoso-hub" --resource-group $RG \
--query "ipConfigurations[0].privateIPAddress" -o tsv)
echo "Firewall IP: $AFW_IP"

az network route-table route list --route-table-name rt-spoke-to-fw \
--resource-group $RG --query "[].{Prefix:addressPrefix, NextHop:nextHopIpAddress}" -o table

# Check VNet peering allows forwarded traffic
az network vnet peering show --name spoke-prod-to-hub \
--vnet-name vnet-spoke-prod --resource-group $RG \
--query "{AllowForwarded:allowForwardedTraffic, State:peeringState}"

# Fix: Ensure peering allows forwarded traffic
az network vnet peering update --name spoke-prod-to-hub \
--vnet-name vnet-spoke-prod --resource-group $RG \
--set allowForwardedTraffic=true

# Also check from hub side
az network vnet peering update --name hub-to-spoke-prod \
--vnet-name vnet-hub --resource-group $RG \
--set allowForwardedTraffic=true

# Verify diagnostic settings are on the firewall
az monitor diagnostic-settings list --resource $AFW_ID \
--query "[].name" -o tsv

Knowledge check

1. In a hub-and-spoke architecture with Azure Firewall, which combination provides defense-in-depth for a web application?

2. When implementing forced tunneling through Azure Firewall in a hub-and-spoke architecture, what must be configured on VNet peerings?

3. What is the recommended approach to ensure Private DNS zone resolution works for private endpoints in a hub-and-spoke topology?

4. In a network security architecture review, which finding represents the highest risk?

Cleanup

# Delete flow logs
for NSG in nsg-prod-web nsg-prod-app nsg-prod-data nsg-dev nsg-staging; do
az network watcher flow-log delete --name "fl-$NSG" --location $LOCATION 2>/dev/null
done

# Delete the resource group (will take several minutes due to firewall)
az group delete --name $RG --yes --no-wait

# Purge Key Vault
az keyvault purge --name $KV_NAME --no-wait 2>/dev/null

echo "Cleanup initiated. Azure Firewall deletion may take 10+ minutes."