Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

🏗️ Azure Landing Zone Lab

Terraform Azure AzAPI License

Azure Landing Zone Lab banner

🎯 Overview

Azure Landing Zone Lab is a Terraform-first sandbox for building and understanding an enterprise Azure foundation. It brings together hub-spoke networking, identity, governance, security, monitoring, and optional workload services so you can learn the moving parts of a landing zone in a real deployable environment.

  • Terraform-first: every resource is defined as code, with reusable modules and environment-driven configuration.
  • CAF-aligned architecture: the lab follows Microsoft Cloud Adoption Framework landing zone patterns for platform, governance, and operations.
  • Real network and security flows: hub-spoke VNets, Azure Firewall, NSGs, private endpoints, routing, diagnostics, and optional VPN are wired together for hands-on testing.
  • Flexible lab profiles: start small, then enable PaaS, Application Gateway, AKS, hybrid connectivity, and observability features as needed.
What you can practiceHow this lab helps
Azure learningExplore networking, security, identity, monitoring, and infrastructure as code in one environment.
Architecture testingValidate hub-spoke, private endpoint, load balancing, firewall, and routing decisions before production.
Certification prepBuild hands-on evidence for AZ-104, AZ-305, AZ-700, and AZ-400 study paths.
Proof of concept workToggle features in terraform.tfvars, run a plan, and compare deployment profiles quickly.

🏛️ Architecture Diagram

Actual Azure Architecture Diagram

Architecture at a Glance

  • Topology: A hub VNet connects identity, management, shared services, workload, dev, and optional on-premises spokes.
  • Traffic control: Azure Firewall, route tables, NSGs, NAT Gateway, Load Balancer, and Application Gateway model realistic ingress and egress paths.
  • Private access: Private DNS and Private Link can keep Key Vault, Storage, and SQL access on the Azure backbone.
  • Operations: Log Analytics, diagnostics, flow logs, alerts, and budget controls show how the landing zone is monitored after deployment.

🎛️ Master Control Panel (feature toggles)

At the top of terraform.tfvars there is a MASTER CONTROL PANEL section that contains all the main deploy_* / enable_* switches in one place. Flip those values to quickly change what gets deployed, then run terraform plan and terraform apply.


📋 Table of Contents


🔌 Network Add-Ons & Observability

Enable these when you need tighter control or visibility. Check prerequisites to avoid failed plans:

FlagWhat it addsPrerequisitesCost
deploy_nat_gatewayFixed outbound IP for workload web subnetdeploy_workload_prod = true~$4-5/mo + data
deploy_private_dns_zonesCentral Private DNS for blob, Key Vault, SQLHub RG presentMinimal
deploy_private_endpointsPrivate Link for Key Vault, Storage, SQLdeploy_private_dns_zones = trueNone
deploy_application_security_groupsASGs for web/app/data tiersdeploy_workload_prod = trueNone
enable_vnet_flow_logsVNet flow logs to storage (replaces NSG flow logs)deploy_storage = true, Network WatcherStorage ingest
enable_traffic_analyticsTraffic flow visualizationenable_vnet_flow_logs, deploy_log_analytics, deploy_storageLog Analytics ingest
create_network_watcherCreates NetworkWatcherRG if missingSet true only for new subscriptionsNone

Note: VNet Flow Logs are the modern replacement for NSG Flow Logs (retired June 2025). Uses AzAPI provider.

☁️ PaaS Services (Cloud-Native Workloads)

All PaaS services are optional and controlled via deployment flags:

ServiceFlagTierMonthly Cost
🔷 Azure Functionsdeploy_functionsY1 ConsumptionFREE
🌐 Static Web Appsdeploy_static_web_appFreeFREE
Logic Appsdeploy_logic_appsConsumption~$0 (pay per run)
📬 Event Griddeploy_event_gridStandardFREE (100k ops)
🚌 Service Busdeploy_service_busBasic~$0.05/month
🌍 App Servicedeploy_app_serviceF1 FreeFREE
🗃️ Cosmos DBdeploy_cosmos_dbServerless~$0-5/month

Note: deploy_container_apps exists as a placeholder flag but is not currently wired to a module.

🎯 Use Cases

Use CaseDescription
🎓 LearningPractice Azure networking, security, load balancing, and IaC
🧪 TestingValidate architectures before production deployment
📚 TrainingTeach teams about Azure Landing Zones and CAF
🔬 PoCQuickly spin up proof-of-concept environments
🏆 Certification PrepHands-on practice for AZ-104, AZ-305, AZ-700 exams

⚡ Deployment Profiles

ProfileComponentsDeploy TimeEst. Monthly Cost
MinimalCore VNets + Identity + Management (no Firewall)~8 min~$100-150
StandardMinimal + Firewall + Load Balancer + IIS~15 min~$450-500
Standard + PaaSStandard + All PaaS Services~25 min~$500-550
Full HybridStandard + VPN + On-Prem Simulation~45 min~$650-700
EnterpriseFull Hybrid + AKS + App Gateway~55 min~$850-950

Current Default Config (terraform.tfvars): Standard + PaaS + Network Add-ons + Dev VNet (VPN/AKS/Flow Logs/Backup off; ~$500-600/month)


🧪 Lab Scenarios

Lab scenarios overview

This landing zone supports multiple hands-on lab scenarios for learning and testing. Each scenario builds on the core infrastructure.

Scenario 1: Hub-Spoke Network Fundamentals

Objective: Understand Azure networking concepts and hub-spoke topology

TaskSkills Practiced
Explore VNet peering connectionsVirtual network peering, traffic flow
Test connectivity between spokes via hubRoute tables, UDRs
Analyze Azure Firewall logsNetwork security, logging
Configure custom NSG rulesNetwork security groups
# Deploy minimal infrastructure
deploy_firewall         = true
deploy_vpn_gateway      = false
deploy_onprem_simulation = false

Scenario 2: Load Balancing & High Availability

Objective: Learn Azure Load Balancer concepts and IIS web server deployment

TaskSkills Practiced
Test load balancer distribution5-tuple hash, health probes
Simulate VM failure (stop a VM)Backend pool health, failover
Configure custom health probesHTTP probes, intervals
Access VMs via NAT rulesInbound NAT, port mapping
# Test load balancing
curl http://<lb_frontend_ip>  # Observe round-robin between web01-prd and web02-prd

# RDP to individual servers
mstsc /v:<lb_frontend_ip>:3389  # web01
mstsc /v:<lb_frontend_ip>:3390  # web02

Scenario 3: Hybrid Connectivity (VPN)

Objective: Configure site-to-site VPN and hybrid networking

TaskSkills Practiced
Establish VPN tunnelIPsec, IKE configuration
Test on-prem to Azure connectivityVPN troubleshooting
Configure BGP routing (optional)Dynamic routing, ASN
Access Azure resources from "on-prem"Hybrid network design
# Enable hybrid scenario
deploy_vpn_gateway       = true
deploy_onprem_simulation = true
enable_bgp               = true  # Optional: Enable BGP routing

Scenario 4: Azure Firewall & Security

Objective: Implement network security controls

TaskSkills Practiced
Create application rulesFQDN filtering, web categories
Configure network rulesIP-based filtering, protocols
Set up DNAT rulesInbound traffic, port forwarding
Analyze threat intelligence logsSecurity monitoring
# From jumpbox, test firewall rules
Test-NetConnection -ComputerName google.com -Port 443
Invoke-WebRequest -Uri https://ifconfig.me  # Check SNAT IP

Scenario 5: Containers with AKS

Objective: Deploy and manage Kubernetes workloads

TaskSkills Practiced
Connect to AKS clusterkubectl, Azure CLI
Deploy sample applicationKubernetes deployments
Configure ingressService exposure, networking
Integrate with Log AnalyticsContainer monitoring
# Enable AKS
deploy_aks = true

# Connect to cluster
az aks get-credentials --resource-group rg-workload-prod-lab-<location_short> --name aks-prod-lab-<location_short>
kubectl get nodes

Scenario 6: PaaS Services & Private Endpoints

Objective: Work with Azure PaaS services securely

TaskSkills Practiced
Access Key Vault secretsSecret management, RBAC
Connect to SQL via private endpointPrivate Link, DNS
Upload files to Storage AccountBlob storage, access tiers
Test Azure FunctionsServerless compute
# From jumpbox, access Key Vault
$secret = Get-AzKeyVaultSecret -VaultName "kv-azlab-xxxx" -Name "admin-password"

# Test SQL connectivity
Test-NetConnection -ComputerName "sql-xxxx.database.windows.net" -Port 1433

Scenario 7: Monitoring & Alerting

Objective: Implement Azure Monitor for infrastructure

TaskSkills Practiced
Review Log Analytics queriesKQL, log analysis
Create custom alertsMetric alerts, action groups
Configure diagnostic settingsResource logging
Build monitoring dashboardsAzure Dashboards, workbooks
// Sample KQL query for VM performance
Perf
| where ObjectName == "Processor" and CounterName == "% Processor Time"
| summarize avg(CounterValue) by Computer, bin(TimeGenerated, 5m)
| render timechart

Scenario 8: Application Gateway & WAF

Objective: Implement Layer 7 load balancing with WAF

TaskSkills Practiced
Configure backend poolsHealth probes, routing
Test WAF rulesOWASP protection, custom rules
Set up URL path-based routingMulti-site hosting
Analyze WAF logsSecurity investigation
# Enable Application Gateway
deploy_application_gateway = true
appgw_waf_mode             = "Detection"  # or "Prevention"

📊 Lab Progress Tracker

Use this checklist to track your learning progress:

  • Core Infrastructure

    • Deployed hub-spoke topology
    • Verified VNet peering connectivity
    • Tested Azure Firewall egress
    • Accessed jumpbox via RDP/Bastion
  • Load Balancing

    • Tested HTTP load balancing
    • Verified health probe behavior
    • Used NAT rules for RDP access
    • Stopped a VM and verified failover
  • Security

    • Reviewed NSG rules
    • Created custom firewall rules
    • Accessed Key Vault secrets
    • Analyzed security logs
  • Hybrid Networking (Optional)

    • Established VPN tunnel
    • Tested cross-premises connectivity
    • Configured BGP (if enabled)
  • Containers (Optional)

    • Deployed AKS cluster
    • Connected with kubectl
    • Deployed sample workload
  • Monitoring

    • Queried Log Analytics
    • Created custom alert
    • Built monitoring dashboard

🔄 Traffic Flow

Traffic flow diagram

Network Traffic Patterns

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                    TRAFFIC FLOWS                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘

  INTERNET                   INTERNET                    INTERNET
      │                          │                           │
      ▼                          ▼                           ▼
┌───────────┐            ┌───────────────┐            ┌───────────────┐
│   Azure   │            │    Public     │            │  Application  │
│ Firewall  │            │ Load Balancer │            │   Gateway     │
│  (SNAT)   │            │               │            │   (WAF)       │
└─────┬─────┘            └───────┬───────┘            └───────┬───────┘
      │                          │                           │
      ▼                          ▼                           ▼
┌───────────────┐        ┌───────────────┐            ┌───────────────┐
│ Spoke VNets   │        │  Web Servers  │            │  Web Servers  │
│ (Egress Only) │        │  (Direct LB)  │            │  (via AppGW)  │
└───────────────┘        └───────────────┘            └───────────────┘

Flow 1: Outbound         Flow 2: Inbound              Flow 3: WAF
(All Spokes)             (Load Balancer)              (App Gateway)

Flow Details

FlowPathUse Case
Outbound (SNAT)VM → Azure Firewall → InternetAll spoke VMs accessing internet
Load BalancerInternet → Public LB → Web VMsDirect HTTP/HTTPS to web tier
App GatewayInternet → App GW (WAF) → Web VMsWAF-protected web traffic
Spoke-to-SpokeSpoke A → Hub Firewall → Spoke BCross-spoke communication
VPN TunnelOn-Prem → VPN GW → Hub → SpokesHybrid connectivity

Why Web Subnet Bypasses Firewall

When using a Public Load Balancer, the web subnet must have direct internet routing to avoid asymmetric routing:

ScenarioInbound PathOutbound PathResult
❌ Web subnet with FW routeLB → VMVM → Firewall → InternetBroken (TCP fails)
✅ Web subnet directLB → VMVM → InternetWorks (symmetric)

The configuration automatically excludes the web subnet from firewall routing when the public load balancer is enabled.


📦 What Gets Deployed

Resource Groups

Resource GroupPurposeKey Resources
rg-hub-{env}-{location}Hub networkingVNet, Azure Firewall*, VPN Gateway*, App Gateway*
rg-identity-{env}-{location}Identity servicesVNet, Domain Controller (DC01), DC02*
rg-management-{env}-{location}OperationsVNet, Jumpbox VM, Log Analytics
rg-shared-{env}-{location}Shared servicesVNet, Key Vault, Storage Account, SQL Database*
rg-workload-prod-{env}-{location}Production workloadVNet, Load Balancer*, Web Servers*, AKS*, PaaS*
rg-workload-dev-{env}-{location}Development workloadVNet, similar to prod*
rg-onprem-{env}-{location}Simulated on-premVNet, VPN Gateway, File Server*

* = Optional, controlled by deployment flags

Resource Count by Configuration

ConfigurationApproximate Resources
Minimal (No firewall, no LB)~60-80
Standard (Firewall + LB + IIS)~130-150
Standard + PaaS~170-190
Full Hybrid (+ VPN + On-prem)~190-210
Enterprise (+ AKS + AppGW)~220-250

Core Infrastructure Breakdown

CategoryResourcesCount
NetworkingVNets, Subnets, NSGs, Route Tables, Peerings~30-40
SecurityAzure Firewall, Firewall Policy, Rule Collections~8-10
ComputeVMs (DC, Jumpbox, Web Servers), NICs, Disks~15-25
Load BalancingLoad Balancer, Backend Pool, Health Probes, Rules~8-10
StorageStorage Account, Key Vault, File Shares~5-8
DatabaseAzure SQL Server, Database, Private Endpoint~3-5
MonitoringLog Analytics, Diagnostic Settings, Alerts~15-25
IdentityManaged Identities, RBAC Assignments~5

Load Balancer Configuration

ResourceConfigurationPurpose
Public Load BalancerStandard SKUDistributes HTTP traffic
Frontend IPStatic public IPInternet entry point
Backend Pool2 Web ServersTarget VMs
Health ProbeHTTP/80, 5s intervalVM health monitoring
LB Rule (HTTP)TCP/80 → 80Web traffic distribution
LB Rule (HTTPS)TCP/443 → 443Secure web traffic
NAT Rule (RDP web01)TCP/3389 → 3389Direct RDP to web01
NAT Rule (RDP web02)TCP/3390 → 3389Direct RDP to web02
Outbound RuleSNAT via LB PIPOutbound internet access

Web Servers (IIS)

ResourceConfigurationPurpose
web01-prdWindows Server 2022 Core, Standard_B1msIIS Web Server
web02-prdWindows Server 2022 Core, Standard_B1msIIS Web Server
IIS ExtensionCustomScriptExtensionAuto-install IIS + custom page

Monitoring & Alerting

ResourceConfiguration
Log Analytics Workspace30-day retention, 1GB daily quota
Diagnostic SettingsAzure Firewall, VPN Gateway, App Gateway
CPU AlertVM CPU > 80% for 5 minutes
Disk Read AlertDisk read ops > 100/s
Firewall AlertThroughput anomaly detection
Action GroupEmail notifications

🌐 Network Topology

Network topology overview

Address Space Allocation

NetworkCIDRPurpose
Hub10.0.0.0/16Central connectivity hub
├─ GatewaySubnet10.0.0.0/24VPN Gateway
├─ AzureFirewallSubnet10.0.1.0/24Azure Firewall
├─ ManagementSubnet10.0.2.0/24Hub management
└─ AppGatewaySubnet10.0.3.0/24Application Gateway
Identity10.1.0.0/16Domain Controllers
└─ DCSubnet10.1.1.0/24DC01 (10.1.1.4), DC02 (10.1.1.5)
Management10.2.0.0/16Operations
└─ JumpboxSubnet10.2.1.0/24Jumpbox (10.2.1.4)
Shared10.3.0.0/16Shared services
├─ AppSubnet10.3.1.0/24Application tier
└─ PrivateEndpointSubnet10.3.2.0/24Private endpoints
Workload Prod10.10.0.0/16Production apps
├─ WebSubnet10.10.1.0/24Load Balanced Web Tier
├─ AppSubnet10.10.2.0/24App tier VMs
├─ DataSubnet10.10.3.0/24Database VMs
├─ ContainerAppsSubnet10.10.8.0/23Reserved for future Container Apps support
└─ AKSSubnet10.10.16.0/20AKS node pool (4094 IPs)
Workload Dev10.11.0.0/16Development apps
├─ WebSubnet10.11.1.0/24Dev web tier
├─ AppSubnet10.11.2.0/24Dev app tier
└─ DataSubnet10.11.3.0/24Dev data tier
On-Premises10.100.0.0/16Simulated on-prem
├─ GatewaySubnet10.100.0.0/24On-prem VPN Gateway
└─ ServersSubnet10.100.1.0/24File Server (10.100.1.4)
VPN Clients172.16.0.0/24Point-to-site VPN pool

🚀 Quick Start

Prerequisites

  • Terraform >= 1.9.0
  • Azure CLI >= 2.50.0
  • Azure subscription with Owner or Contributor access

Note: This project uses both AzureRM (> 4.0) and AzAPI (> 2.0) providers. AzAPI is required for VNet Flow Logs (the modern replacement for deprecated NSG Flow Logs).

Step 1: Clone and Configure

git clone https://github.com/Jamonygr/azure-landing-zone-lab.git
cd azure-landing-zone-lab

# Copy example config
cp terraform.tfvars.example terraform.tfvars

# Edit configuration (set at minimum subscription_id and admin credentials)
code terraform.tfvars

Step 2: Deploy

# Login to Azure and set the target subscription
az login
az account set --subscription "<your-subscription-id>"

# Initialize Terraform
terraform init

# Plan deployment
terraform plan -out=tfplan

# Apply
terraform apply tfplan

Step 3: Verify Load Balancer

# Get the Load Balancer IP
terraform output lb_frontend_ip

# Test with curl (should alternate between web01-prd and web02-prd)
curl http://$(terraform output -raw lb_frontend_ip)

# Clean up when done
terraform destroy

🔄 CI/CD Pipeline

CI/CD pipeline diagram

The GitHub Actions workflow (.github/workflows/terraform.yml) now has 16 visible jobs that cover formatting, validation, security, linting, docs, workflow validation, analysis, cost estimation, plan/apply/destroy, and metrics. It orchestrates composite actions in .github/actions/ for Terraform operations, state backup, inventories, changelog generation, graphs, and reporting.

Pipeline Stages

  • 1️⃣ Format Check2️⃣ Validate
  • 3️⃣ Security - tfsec, 3️⃣ Security - Checkov, 3️⃣ Security - Secrets (Gitleaks)
  • 4️⃣ Lint - TFLint, 4️⃣ Lint - Policy (Conftest), 4️⃣ Lint - Docs (terraform-docs), 4️⃣ Lint - Actions (actionlint)
  • 5️⃣ Analysis - Graph, 5️⃣ Analysis - Versions
  • 6️⃣ Analysis - Cost (Infracost, soft-fail)
  • 7️⃣ Plan (change detection + plan artifact)
  • 8️⃣ Apply (manual action=apply; includes state backup, resource inventory, changelog)
  • 9️⃣ Destroy (manual action=destroy + DESTROY confirm)
  • 🔟 Metrics (after successful Apply)

Artifacts include the saved plan, terraform-docs output, dependency graph SVG, module/provider versions, cost report, changelog, resource inventory, and metrics JSON.

Triggers

  • Push to main (repo health paths): runs format/validate → security/linters → docs/graph/version → cost → plan. Apply/Destroy never auto-run.
  • Pull Request to main: same checks plus plan for review; no PR comment is posted.
  • Manual dispatch: pick action (plan|apply|destroy) and environment (lab|dev|prod), plus destroy_confirm=DESTROY for destroys. Apply/Destroy only run via workflow_dispatch.

Concurrency: one run per branch + environment (terraform-${ref}-${environment}); newer runs wait rather than cancel.

Remote State Storage

Terraform state is stored in Azure Blob Storage for team collaboration and state locking:

Azure Storage Account
└── Container: tfstate
    ├── lab.terraform.tfstate
    ├── dev.terraform.tfstate
    └── prod.terraform.tfstate

Required GitHub Secrets

SecretDescription
AZURE_CLIENT_IDApp registration/client ID used by GitHub OIDC
AZURE_TENANT_IDAzure tenant ID used by GitHub OIDC
AZURE_SUBSCRIPTION_IDTarget Azure subscription
TF_STATE_RGResource group for state storage
TF_STATE_SAStorage account for state storage
INFRACOST_API_KEY(Optional) enables the cost estimation stage

The pipeline uses GitHub Actions OIDC federation. Do not store a long-lived Azure client secret JSON in repository secrets.

Quick Commands

# Create an app registration and add a federated credential for this repo.
# Then grant least-privilege Azure roles required by your selected profile.
az ad app create --display-name "terraform-alz-pipeline"

# Deploy infrastructure (manual apply)
gh workflow run "Terraform Pipeline" -f action=apply -f environment=lab

# Destroy infrastructure (requires confirmation)
gh workflow run "Terraform Pipeline" \
  -f action=destroy \
  -f environment=lab \
  -f destroy_confirm=DESTROY

# Watch pipeline progress
gh run watch

📖 Full documentation: See wiki/reference/pipeline.md and wiki/reference/pipeline-templates.md


⚙️ Configuration Options

terraform.tfvars

# =============================================================================
# CORE SETTINGS
# =============================================================================
subscription_id = "00000000-0000-0000-0000-000000000000"  # REQUIRED
project         = "azlab"
environment     = "lab"
location        = "westus2"  # Example: current lab profile region
owner           = "Lab-User"

# =============================================================================
# SECURITY (CHANGE THESE!)
# =============================================================================
admin_username     = "azureadmin"
admin_password     = null                       # Prefer TF_VAR_admin_password or a private tfvars file
sql_admin_login    = "sqladmin"
sql_admin_password = null                       # Prefer TF_VAR_sql_admin_password or a private tfvars file
vpn_shared_key     = null                       # Required only if VPN is enabled

# =============================================================================
# INFRASTRUCTURE FLAGS
# =============================================================================
# Core Networking
deploy_firewall              = true   # Azure Firewall (~$350/mo)
firewall_sku_tier            = "Standard"
deploy_vpn_gateway           = false  # VPN Gateway (~$140/mo, 30 min deploy)
deploy_onprem_simulation     = false  # Requires VPN Gateway
deploy_application_gateway   = true   # App Gateway with WAF (~$36/mo)

# Identity & Management
deploy_secondary_dc          = false  # Second Domain Controller (~$30/mo)
enable_jumpbox_public_ip     = false  # Prefer VPN/Bastion/private management
allowed_jumpbox_source_ips   = []     # Add trusted CIDRs only when public RDP is required
allow_public_rdp_from_internet = false

# Shared Services
deploy_keyvault              = true
deploy_storage               = true
deploy_sql                   = true   # Azure SQL (~$5/mo)

# Monitoring
deploy_log_analytics         = true
log_retention_days           = 30     # Free tier
log_daily_quota_gb           = 2      # Limit ingestion

# =============================================================================
# WORKLOADS
# =============================================================================
deploy_workload_prod         = true
deploy_workload_dev          = true   # Dev environment (similar to prod)

# Load Balancer
deploy_load_balancer         = true
enable_lb_rdp_nat_rules      = false  # Keep web VM RDP NAT closed by default
lb_type                      = "public"
lb_web_server_count          = 2
lb_web_server_size           = "Standard_B1ms"

# AKS (Disabled by default - long deploy time)
deploy_aks                   = false
aks_node_count               = 1
aks_vm_size                  = "Standard_B2s"

# =============================================================================
# PAAS SERVICES (All optional)
# =============================================================================
# Tier 1: FREE
deploy_functions             = false  # Azure Functions Y1
deploy_static_web_app        = true   # Static Web Apps Free
deploy_logic_apps            = true   # Logic Apps Consumption
deploy_event_grid            = true   # Event Grid (100k free)

# Tier 2: Low Cost (~$15-20/mo total)
deploy_service_bus           = true   # Service Bus Basic (~$0.05/mo)
deploy_app_service           = true   # App Service F1 (FREE)

# Tier 3: Data
deploy_cosmos_db             = true   # Cosmos DB Serverless (~$0-5/mo)
paas_alternative_location    = "canadacentral"  # For quota issues
cosmos_location              = "northeurope"    # Example alternate region

# =============================================================================
# NETWORK ADD-ONS
# =============================================================================
deploy_nat_gateway                   = true   # Fixed outbound IP
deploy_private_dns_zones             = true   # Private DNS for PaaS
deploy_private_endpoints             = true   # Private Link
deploy_application_security_groups   = true   # ASGs for segmentation

# Observability
create_network_watcher               = false  # Use existing NW (create only for new subs)
enable_vnet_flow_logs                = false  # VNet flow logs
enable_traffic_analytics             = false  # Traffic visualization

# =============================================================================
# COST OPTIMIZATION
# =============================================================================
enable_auto_shutdown         = true   # Shutdown VMs at 7 PM
vm_size                      = "Standard_B2s"

Replace <location_short> in CLI examples with the short code derived from your region (e.g., weu for West Europe, eus for East US, wus2 for West US 2). See locals.tf for the mapping logic.


🧪 Testing the Environment

Live Provisioning Validation

Live validation flow from static checks through Azure apply, smoke tests, destroy, and push readiness

For a disposable end-to-end validation in West Europe, use the live runner after signing in with Azure CLI:

.\scripts\invoke-live-validation.ps1

The runner creates a temporary tfvars file and backend-free Terraform workdir outside the repo, provisions the safe extended profile, runs static checks, Terratest, Azure CLI smoke checks, destroys the deployment in a finally block, verifies the resource groups are gone, and removes local Terraform state/plan files. See the live provisioning validation guide for the exact profile, expected resources, failure handling, and teardown checks.

Access Points Summary

ServiceAccess MethodNotes
Web Servers (LB)http://<lb_frontend_ip>Load balanced HTTP
Web01 RDP<lb_frontend_ip>:3389NAT rule
Web02 RDP<lb_frontend_ip>:3390NAT rule
Jumpbox<jumpbox_public_ip>:3389If public IP enabled
Jumpbox (via Firewall)DNAT through firewallIf no public IP
On-Prem Mgmt VM<onprem_mgmt_vm_public_ip>:3389If deployed

Test Load Balancing

# From your local machine - run multiple times
# You should see responses from both web01-prd and web02-prd
curl http://$(terraform output -raw lb_frontend_ip)

# Or use PowerShell
1..10 | ForEach-Object { 
    (Invoke-WebRequest -Uri "http://$(terraform output -raw lb_frontend_ip)" -UseBasicParsing).Content 
}

Expected Response

<h1>web01-prd</h1>
<p>Azure Landing Zone - prod Workload</p>
<p>Load Balanced Web Server</p>

Test Connectivity from Jumpbox

# RDP to jumpbox first, then test internal connectivity

# Test Domain Controller
Test-NetConnection -ComputerName 10.1.1.4 -Port 389  # LDAP

# Test Web Servers
Test-NetConnection -ComputerName 10.10.1.4 -Port 80
Test-NetConnection -ComputerName 10.10.1.5 -Port 80

# Test Key Vault
Resolve-DnsName kv-azlab-xxxx.vault.azure.net

# Test SQL via Private Endpoint
Test-NetConnection -ComputerName sql-azlab-xxxx.database.windows.net -Port 1433

# Test outbound through firewall
Invoke-WebRequest -Uri https://ifconfig.me -UseBasicParsing  # Shows firewall's public IP

Test VPN Connectivity (If Deployed)

# From on-prem management VM
Test-NetConnection -ComputerName 10.1.1.4 -Port 389   # DC in Azure
Test-NetConnection -ComputerName 10.2.1.4 -Port 3389  # Jumpbox in Azure

# From Azure jumpbox
Test-NetConnection -ComputerName 10.100.1.4 -Port 445  # File server on-prem

Health Check Commands

# Check backend pool health
az network lb show \
  --resource-group rg-workload-prod-lab-<location_short> \
  --name lb-prod-lab-<location_short> \
  --query "loadBalancingRules[].backendAddressPool.id" -o table

# Check probe status
az network lb probe list \
  --resource-group rg-workload-prod-lab-<location_short> \
  --lb-name lb-prod-lab-<location_short> -o table

# Check VPN tunnel status
az network vpn-connection show \
  --resource-group rg-hub-lab-<location_short> \
  --name vpn-conn-hub-to-onprem \
  --query "connectionStatus" -o tsv

🔒 Security Features

Network Security

FeatureImplementation
Azure FirewallCentralized egress control with DNAT/SNAT
NSG RulesSubnet-level traffic filtering
Route TablesForced tunneling through firewall (except web subnet for LB)
Private EndpointsPrivate access to PaaS services

Load Balancer Security

FeatureConfiguration
Standard SKUSecure by default (no public access without rules)
Health ProbesOnly healthy VMs receive traffic
NSG IntegrationNSG rules required to allow traffic
Outbound RulesControlled SNAT for internet access

Web Subnet NSG Rules

PriorityNameDirectionAccessPortSource
100AllowHTTPInboundAllow80*
110AllowHTTPSInboundAllow443*
200AllowRDPFromHubInboundAllow3389Hub VNet

💰 Cost Estimation

Monthly Cost by Deployment Profile

ProfileEst. Monthly CostKey Cost Drivers
Minimal~$150-200VMs only, no firewall
Standard~$450-550Firewall ($350), VMs ($150), LB ($25)
Standard + PaaS~$500-600Standard + PaaS services (~$60)
Full Hybrid~$650-750Standard + VPN Gateway (~$140)
Enterprise~$900-1000Full + AKS ($150) + App Gateway ($36)

Detailed Cost Breakdown

ResourceSKUMonthly Cost
Azure FirewallStandard~$350
VPN GatewayVpnGw1~$140
Application GatewayWAF_v2~$36
Public Load BalancerStandard~$25
Web Servers (2x)Standard_B1ms~$30
Domain ControllerStandard_B2s~$30
JumpboxStandard_B2s~$30
On-Prem VMs (2x)Standard_B2s~$60
AKS Cluster (1 node)Standard_B2s~$30
Storage AccountStandard_LRS~$5
Key VaultStandard~$3
SQL DatabaseBasic DTU~$5
Log AnalyticsPerGB2018~$10

PaaS Services (Low Cost/Free Tier)

ServiceTierMonthly Cost
Azure FunctionsConsumption (Y1)FREE
Static Web AppsFreeFREE
Logic AppsConsumption~$0 (pay per run)
Event GridStandardFREE (100k ops)
Service BusBasic~$0.05
App ServiceF1 FreeFREE
Cosmos DBServerless~$0-5
Total PaaS~$0-10/month

Note: deploy_container_apps exists as a placeholder flag but is not currently wired to a module.

💡 Cost Optimization Tips

TipSavings
✅ Enable auto_shutdown for VMs~50% on VM costs
✅ Use Standard_B1ms for web serversSufficient for IIS
✅ Disable VPN Gateway when not testing~$140/month
✅ Disable AKS when not using containers~$30-150/month
✅ Use Azure Firewall Basic SKU~$100/month (vs Standard)
✅ Set log_daily_quota_gb = 1Prevents log overage

Auto-Shutdown Schedule

VMs are configured to auto-shutdown at 7:00 PM local time when enable_auto_shutdown = true (default). This saves ~50% on compute costs for lab environments.


🔧 Troubleshooting

Common Issues & Solutions

Load Balancer Not Responding

# Check if VMs are in backend pool
az network nic show \
  --resource-group rg-workload-prod-lab-<location_short> \
  --name nic-web01-prd \
  --query "ipConfigurations[0].loadBalancerBackendAddressPools" -o table

# Check health probe status
az network lb probe list \
  --resource-group rg-workload-prod-lab-<location_short> \
  --lb-name lb-prod-lab-<location_short> -o table

# Verify NSG allows HTTP
az network nsg rule list \
  --resource-group rg-workload-prod-lab-<location_short> \
  --nsg-name nsg-web-prod-lab-<location_short> -o table

Asymmetric Routing Issues

If the load balancer is timing out:

  1. Check route table - Web subnet should NOT route through firewall when using public LB
  2. Verify lb_type = "public" in terraform.tfvars
  3. Confirm web subnet has direct internet routing (no UDR to firewall)

IIS Not Installed on Web Servers

# RDP to the VM via NAT rule
mstsc /v:<lb_frontend_ip>:3389  # web01
mstsc /v:<lb_frontend_ip>:3390  # web02

# Check IIS status
Get-WindowsFeature -Name Web-Server

# Manually install if needed
Install-WindowsFeature -Name Web-Server -IncludeManagementTools

# Recreate the default page
$content = "<h1>$env:COMPUTERNAME</h1><p>Azure Landing Zone Web Server</p>"
Set-Content -Path "C:\inetpub\wwwroot\index.html" -Value $content

VPN Tunnel Not Connecting

# Check VPN connection status
az network vpn-connection show \
  --resource-group rg-hub-lab-<location_short> \
  --name vpn-conn-hub-to-onprem \
  --query "{Status:connectionStatus,IngressBytes:ingressBytesTransferred,EgressBytes:egressBytesTransferred}" -o table

# Check VPN Gateway status
az network vnet-gateway show \
  --resource-group rg-hub-lab-<location_short> \
  --name vpngw-hub-lab-<location_short> \
  --query "provisioningState" -o tsv

# Reset VPN connection if stuck
az network vpn-connection update \
  --resource-group rg-hub-lab-<location_short> \
  --name vpn-conn-hub-to-onprem \
  --set connectionProtocol=IKEv2

Cannot Access Key Vault

# Check network access (from jumpbox)
Test-NetConnection -ComputerName kv-azlab-xxxx.vault.azure.net -Port 443

# Verify RBAC access
az role assignment list --scope /subscriptions/<sub-id>/resourceGroups/rg-shared-lab-<location_short>

# Check Key Vault firewall
az keyvault network-rule list --name kv-azlab-xxxx

SQL Connection Failing

# Verify private endpoint DNS resolution
Resolve-DnsName sql-azlab-xxxx.database.windows.net

# Should return private IP (10.3.x.x), not public IP

# Test connectivity
Test-NetConnection -ComputerName sql-azlab-xxxx.database.windows.net -Port 1433

Terraform Apply Errors

# State lock error
terraform force-unlock <lock-id>

# Resource already exists
terraform import <resource_address> <resource_id>

# Quota exceeded
# Request quota increase in Azure Portal or change region

# Provider version mismatch
terraform init -upgrade

Azure Firewall Blocking Traffic

// Check firewall logs in Log Analytics
AzureDiagnostics
| where Category == "AzureFirewallNetworkRule" or Category == "AzureFirewallApplicationRule"
| where msg_s contains "Deny"
| project TimeGenerated, msg_s
| order by TimeGenerated desc
| take 50

Deployment Timing Reference

ResourceTypical Deploy Time
VNets, Subnets, NSGs~2-3 minutes
Azure Firewall~5-8 minutes
VPN Gateway~25-35 minutes
VMs (with extensions)~8-12 minutes
IIS Extension~3-5 minutes
Firewall Rule Collections~3 minutes each
AKS Cluster~10-15 minutes
Application Gateway~8-12 minutes
Cosmos DB~3-5 minutes
Key Vault~8-12 minutes (with soft-delete recovery)
Private Endpoints~2-3 minutes
VNet Flow Logs~1-2 minutes

📁 Project Structure

azure-landing-zone-lab/
├── main.tf                    # Root orchestration (5-pillar architecture)
├── variables.tf               # 80+ configurable input variables
├── outputs.tf                 # Key resource outputs (IPs, URLs, etc.)
├── locals.tf                  # Computed local values
├── terraform.tfvars           # Your configuration (gitignored)
├── terraform.tfvars.example   # Example configuration template
├── LICENSE                    # MIT License
├── README.md                  # This documentation
│
├── environments/              # Environment-specific configurations
│   ├── lab.tfvars             # Lab settings (default)
│   ├── dev.tfvars             # Development settings
│   └── prod.tfvars            # Production settings
│
├── landing-zones/             # 5-Pillar Landing Zone Architecture
│   │
│   ├── networking/            # PILLAR 1: Networking
│   │   ├── main.tf            # Hub VNet, Firewall, VPN, App Gateway
│   │   ├── core/              # Hub networking core components
│   │   ├── connectivity/      # VNet peering, flow logs, NAT, ASGs
│   │   ├── onprem-simulated/  # Simulated on-premises for hybrid testing
│   │   └── secondary-region/  # Multi-region support (placeholder)
│   │
│   ├── identity-management/   # PILLAR 2: Identity Management
│   │   ├── main.tf            # Domain Controllers, DNS
│   │   └── core/              # DC VMs, Identity VNet
│   │
│   ├── governance/            # PILLAR 3: Governance
│   │   └── main.tf            # Management Groups, Azure Policy, Cost Mgmt, RBAC
│   │
│   ├── security/              # PILLAR 4: Security
│   │   ├── main.tf            # Shared services VNet, Key Vault, Storage, SQL
│   │   └── shared-services/   # Private DNS, Private Endpoints
│   │
│   └── management/            # PILLAR 5: Management
│       ├── main.tf            # Jumpbox, Log Analytics, Monitoring, Backup
│       ├── core/              # Management VNet, Jumpbox VM
│       └── workload/          # Workload zones (prod/dev), LB, AKS, PaaS
│
├── modules/                   # Reusable infrastructure modules
    ├── aks/                   # Azure Kubernetes Service
    ├── compute/
    │   └── windows-vm/        # Windows Server 2022 VMs
    ├── firewall/              # Azure Firewall
    ├── firewall-rules/        # Firewall policy & rule collections
    ├── keyvault/              # Azure Key Vault
    ├── monitoring/
    │   ├── action-group/      # Alert action groups
    │   ├── alerts/            # Metric alerts
    │   ├── diagnostic-settings/  # Resource diagnostics
    │   └── log-analytics/     # Log Analytics workspace
    ├── naming/                # Resource naming conventions
    ├── networking/
    │   ├── local-network-gateway/  # On-prem gateway definition
    │   ├── nsg/               # Network Security Groups
    │   ├── peering/           # VNet peering
    │   ├── route-table/       # UDR route tables
    │   ├── subnet/            # Subnet with service endpoints
    │   ├── vnet/              # Virtual Networks
    │   ├── vpn-connection/    # Site-to-site VPN
    │   └── vpn-gateway/       # VPN Gateway
    ├── private-endpoint/      # Private Link endpoints
    ├── resource-group/        # Resource group factory
    ├── sql/                   # Azure SQL Database
    └── storage/               # Storage Account

Key Files

FilePurpose
main.tfOrchestrates 5-pillar architecture: Networking → Identity → Management → Security → Governance
variables.tf80+ configurable parameters for customization
terraform.tfvarsYour environment-specific values (gitignored)
outputs.tfConnection info (IPs, URLs, FQDNs)
locals.tfLocation short codes, naming conventions

5-Pillar Architecture

PillarLanding ZoneResponsibilities
1. Networkinglanding-zones/networking/Hub VNet, Azure Firewall, VPN Gateway, App Gateway, VNet Peering
2. Identitylanding-zones/identity-management/Domain Controllers, DNS servers, Identity VNet
3. Governancelanding-zones/governance/Management Groups, Azure Policy, Cost Management, RBAC, Compliance
4. Securitylanding-zones/security/Shared Services VNet, Key Vault, Storage, SQL, Private Endpoints
5. Managementlanding-zones/management/Jumpbox, Log Analytics, Monitoring, Backup, Workload zones

Providers Used

ProviderVersionPurpose
azurerm~> 4.0Primary Azure resource management
azapi~> 2.0VNet Flow Logs (modern API)
random~> 3.6.0Unique naming suffixes

📚 Configuration Reference

Essential Variables

# terraform.tfvars

# =============================================================================
# REQUIRED - Must be set
# =============================================================================
subscription_id    = "your-subscription-id"
admin_password     = "YourSecureP@ssw0rd!"    # Minimum 12 chars, complexity required
sql_admin_password = "SqlSecureP@ssw0rd!"
vpn_shared_key     = "YourVPNSharedKey123!"   # Required if VPN enabled

# =============================================================================
# CORE SETTINGS
# =============================================================================
project     = "azlab"                          # Resource naming prefix
environment = "lab"                            # Environment tag
location    = "westus2"                        # Azure region
owner       = "Lab-User"                       # Owner tag value

# =============================================================================
# DEPLOYMENT FLAGS - Enable/disable components
# =============================================================================
deploy_firewall          = true                # Azure Firewall (~$350/mo)
deploy_vpn_gateway       = false               # VPN Gateway (~$140/mo)
deploy_onprem_simulation = false               # Simulated on-premises
deploy_load_balancer     = true                # Public Load Balancer
deploy_aks               = false               # Kubernetes cluster
deploy_application_gateway = false             # App Gateway with WAF

# =============================================================================
# VM CONFIGURATION
# =============================================================================
vm_size              = "Standard_B2s"          # Default VM size
lb_web_server_count  = 2                       # Number of IIS servers
lb_web_server_size   = "Standard_B1ms"         # Web server size
enable_auto_shutdown = true                    # Shutdown VMs at 7 PM

# =============================================================================
# PAAS SERVICES (All optional, most are free tier)
# =============================================================================
deploy_functions      = false                  # Azure Functions (FREE)
deploy_static_web_app = false                  # Static Web Apps (FREE)
deploy_logic_apps     = false                  # Logic Apps (pay per run)
deploy_event_grid     = false                  # Event Grid (FREE 100k)
deploy_service_bus    = false                  # Service Bus (~$0.05/mo)
deploy_app_service    = false                  # App Service (~$13/mo)
deploy_container_apps = false                  # Container Apps (placeholder flag; not wired)
deploy_cosmos_db      = false                  # Cosmos DB (serverless)

# =============================================================================
# NETWORK ADD-ONS & OBSERVABILITY
# =============================================================================
deploy_private_dns_zones         = false       # Private DNS zones for PaaS
deploy_private_endpoints         = false       # Private Link endpoints
deploy_nat_gateway               = false       # Fixed outbound IP
deploy_application_security_groups = false     # ASGs for micro-segmentation
enable_vnet_flow_logs            = false       # VNet flow logs (uses AzAPI)
enable_traffic_analytics         = false       # Traffic analytics
create_network_watcher           = false       # Create NW for new subs

# =============================================================================
# GOVERNANCE & COMPLIANCE (Advanced)
# =============================================================================
deploy_azure_policy              = false       # Azure Policy assignments
deploy_regulatory_compliance     = false       # HIPAA/PCI-DSS (audit mode)
deploy_cost_management           = false       # Budget alerts
deploy_backup                    = false       # Recovery Services Vault

All Deployment Flags Reference

FlagDescriptionDefaultCost Impact
Core Infrastructure
deploy_firewallAzure Firewall for egress controltrue~$350/mo
deploy_vpn_gatewayVPN Gateway for hybrid connectivityfalse~$140/mo
deploy_onprem_simulationSimulated on-premises with VPNfalse~$60/mo
deploy_application_gatewayApp Gateway with WAFfalse~$36/mo
Identity & Management
deploy_secondary_dcSecond Domain Controllerfalse~$30/mo
enable_jumpbox_public_ipPublic IP on jumpboxtrue~$3/mo
deploy_log_analyticsLog Analytics workspacetrue~$10/mo
Shared Services
deploy_keyvaultAzure Key Vaulttrue~$3/mo
deploy_storageStorage Accounttrue~$5/mo
deploy_sqlAzure SQL Databasefalse~$5/mo
Workloads
deploy_workload_prodProduction workload VNettrueVaries
deploy_workload_devDevelopment workload VNetfalseVaries
deploy_load_balancerPublic Load Balancer + IIS VMstrue~$55/mo
deploy_aksAzure Kubernetes Servicefalse~$30+/mo
PaaS Services
deploy_functionsAzure Functions (Consumption)falseFREE
deploy_static_web_appStatic Web Apps (Free)falseFREE
deploy_logic_appsLogic Apps (Consumption)false~$0
deploy_event_gridEvent GridfalseFREE
deploy_service_busService Bus (Basic)false~$0.05/mo
deploy_app_serviceApp Service (F1)falseFREE
deploy_cosmos_dbCosmos DB (Serverless)false~$0-5/mo
Network Add-ons
deploy_nat_gatewayNAT Gateway for fixed outbound IPfalse~$4-5/mo
deploy_private_dns_zonesPrivate DNS zonesfalseMinimal
deploy_private_endpointsPrivate Link endpointsfalseNone
deploy_application_security_groupsASGs for segmentationfalseNone
Observability
create_network_watcherNetwork Watcher (new subs)falseNone
enable_vnet_flow_logsVNet flow logsfalseStorage
enable_traffic_analyticsTraffic AnalyticsfalseLog ingest
Governance
deploy_azure_policyAzure Policy assignmentsfalseNone
deploy_regulatory_complianceHIPAA/PCI-DSS policiesfalseNone
deploy_cost_managementBudget and alertsfalseNone
deploy_backupRecovery Services Vaultfalse~$10/mo

Network Address Space Reference

NetworkCIDRSubnets
Hub10.0.0.0/16Gateway (10.0.0.0/24), Firewall (10.0.1.0/24), Mgmt (10.0.2.0/24), AppGW (10.0.3.0/24)
Identity10.1.0.0/16DC Subnet (10.1.1.0/24)
Management10.2.0.0/16Jumpbox (10.2.1.0/24)
Shared10.3.0.0/16App (10.3.1.0/24), Private Endpoint (10.3.2.0/24)
Workload Prod10.10.0.0/16Web (10.10.1.0/24), App (10.10.2.0/24), Data (10.10.3.0/24), ContainerApps (10.10.8.0/23), AKS (10.10.16.0/20)
Workload Dev10.11.0.0/16Web (10.11.1.0/24), App (10.11.2.0/24), Data (10.11.3.0/24)
On-Premises10.100.0.0/16Gateway (10.100.0.0/24), Servers (10.100.1.0/24)
VPN Clients172.16.0.0/24P2S VPN address pool

🚀 Quick Command Reference

# Initialize
terraform init

# Plan with specific var file
terraform plan -var-file="environments/prod.tfvars" -out=tfplan

# Apply
terraform apply tfplan

# Get outputs
terraform output

# Get specific output
terraform output lb_frontend_ip
terraform output -raw keyvault_uri

# Destroy everything
terraform destroy -auto-approve

# Destroy specific resource
terraform destroy -target=module.workload_prod

# Import existing resource
terraform import azurerm_resource_group.hub /subscriptions/.../resourceGroups/rg-hub-lab-<location_short>

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.


🙏 Acknowledgments


📞 Support

ResourceLink
IssuesGitHub Issues
Terraform DocsAzureRM Provider
Azure DocsAzure Documentation

Built with ❤️ for learning Azure infrastructure

Last Updated: June 2026

关于 About

Learn Azure infrastructure with this hands-on Terraform lab. Deploys a complete enterprise environment with hub-spoke networking, firewall, VPN, identity services, and production workloads. Great for training, certifications, and PoCs.

语言 Languages

HCL95.1%
PowerShell3.2%
Open Policy Agent0.8%
Go0.8%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
121
Total Commits
峰值: 45次/周
Less
More

核心贡献者 Contributors