A well-architected Azure environment is crucial for any cloud adoption effort. This guide outlines a production-ready Azure Landing Zone implemented entirely as code, providing a secure, scalable, and automated foundation for your workloads. We'll leverage Terraform for Infrastructure as Code (IaC) and Azure DevOps for a robust Continuous Integration/Continuous Deployment (CI/CD) pipeline.
Github: https://github.com/dhanuka84/azure_landing_zone_project
✅ Azure Landing Zone Hierarchy
🔹 Billing and Identity
Billing Account → Single top-level EA or MCA. (an Enterprise Agreement (EA), which is a traditional, multi-year contract for large enterprises, or a Microsoft Customer Agreement (MCA), which is a more modern, flexible, and digital pay-as-you-go model)
Microsoft Entra ID Tenant → Hosts users, groups, and 3 app registrations:
app-cicd-dev, app-cicd-qa, app-cicd-prod.
🔹 Management Group Hierarchy
Tenant Root MG
│
├── Platform MG
│ └── Subscription: Connectivity
│ └── rg-platform-connectivity
│ ├── vnet-hub-weu
│ │ ├── AzureFirewallSubnet
│ │ ├── GatewaySubnet
│ │ └── AzureBastionSubnet
│ └── Private DNS Zones
│
├── Non-Production MG
│ ├── Subscription: Dev Environment
│ │ └── rg-dev-user-a
│ │ └── vnet-dev-spoke (peered with hub)
│ └── Subscription: QA Environment
│ └── rg-qa-app-services
│ └── vnet-qa-spoke (peered with hub)
│
└── Production MG
└── Subscription: Prod Environment
└── rg-prod-app-services
├── vnet-prod-spoke (peered with hub)
├── aks-prod-main
├── acr-prod-main
└── kv-prod-secrets
🔹 Networking Model
Hub VNet (vnet-hub-weu) in Connectivity subscription.
Spoke VNets (vnet-dev-spoke, vnet-qa-spoke, vnet-prod-spoke) in each environment subscription.
Peering from each spoke to Hub.
UDR in spokes forcing outbound traffic via Azure Firewall in Hub.
Private DNS zones hosted centrally in Hub.
🔹 CI/CD (Azure DevOps)
Uses 3 service connections:
azrm-platform → Connectivity subscription
azrm-nonprod → Dev + QA subscriptions
azrm-prod → Production subscription
Stages: platform_mg → platform_connectivity → env_dev → env_qa → env_prod
🧭 1. The Mental Model: What We’re Building
Governance
Management Groups
Platform → Non-Production → Production
Apply policies, logging, and guardrails at each level.
Networking
https://dhanuka84.blogspot.com/p/azure-landing-zone.html
Hub-and-Spoke Topology
Hub VNet (vnet-hub-weu) → Shared services:
AzureFirewallSubnet with Azure Firewall
GatewaySubnet reserved for VPN/ExpressRoute Gateway
AzureBastionSubnet for secure RDP/SSH
Private DNS Zones for Key Vault, ACR etc.
Spoke VNets (vnet-dev-spoke, vnet-qa-spoke, vnet-prod-spoke) peer back to the hub.
UDRs route 0.0.0.0/0 through the firewall for outbound inspection.
Subscriptions
Connectivity → Hub + shared infra
Dev, QA, Prod → isolated spokes
🧱 2. Identity and Security – Microsoft Entra ID
https://dhanuka84.blogspot.com/p/azure-entra-id.html
Terraform assigns RBAC roles declaratively:
module "rbac" {
assignments = [
{
scope_id = module.acr.id
role_definition = "AcrPush"
principal_objectId = var.spn_app_cicd_prod
},
{
scope_id = module.kv.id
role_definition = "Key Vault Secrets User"
principal_objectId = var.spn_key_vault_api_prod
}
]
}
🧩 3. The Project Layout
infra/
├─ modules/ # Reusable IaC building blocks
│ ├─ networking-hub/
│ ├─ azure-firewall/
│ ├─ udr/
│ ├─ networking-spoke/
│ ├─ aks/ acr/ keyvault/ private-endpoint/ rbac/
│
├─ platform/
│ ├─ mg/ # Management Groups & Policy
│ └─ connectivity/ # Hub + Firewall + DNS
│
├─ envs/
│ ├─ dev/ qa/ prod/ # Spoke VNets, AKS, etc.
│
└─ pipelines/
└─ azure-pipelines.yml # CI/CD definition
⚙️ 4. Step-by-Step Deployment
Step 1 – Remote State
az group create -n rg-tfstate -l westeurope
az storage account create -n saterraformstate123 -g rg-tfstate \
--sku Standard_LRS --encryption-services blob
az storage container create -n tfstate --account-name saterraformstate123
Record these for the variable group.
Step 2 – Azure DevOps Setup
Service Connections
azrm-platform → Connectivity subscription
azrm-nonprod → Dev + QA
azrm-prod → Production
Variable Group vg-terraform
TF_STATE_RG=rg-tfstate
TF_STATE_SA=saterraformstate123
TF_STATE_CONTAINER=tfstate
TF_STATE_KEY_PLATFORM_MG=platform_mg.tfstate
TF_STATE_KEY_CONNECTIVITY=connectivity.tfstate
TF_STATE_KEY_DEV=dev.tfstate
TF_STATE_KEY_QA=qa.tfstate
TF_STATE_KEY_PROD=prod.tfstate
Step 3 – Run the Pipeline
In Pipelines → New Pipeline
Choose existing YAML → /infra/pipelines/azure-pipelines.yml
Stages executed:
🌐 5. Inspecting the Hub
After the pipeline:
rg-platform-connectivity
├─ vnet-hub-weu
│ ├─ AzureFirewallSubnet → Azure Firewall
│ ├─ GatewaySubnet → (optional) VPN/ER Gateway
│ └─ AzureBastionSubnet → Bastion
└─ Private DNS Zones
Tip: you can extend the hub with:
resource "azurerm_virtual_network_gateway" "vpngw" { … }
to enable hybrid connectivity via the existing GatewaySubnet.
🧠 6. Environment Spokes
Each environment’s main file composes modules.
For example, Prod creates:
Spoke VNet + subnets
AKS cluster (aks-prod-main)
ACR (acrprodmain)
Key Vault (kv-prod-secrets)
Private Endpoints
UDR routing → Firewall
✅ 7. Validate Deployment
az network vnet list -g rg-platform-connectivity -o table
az network firewall show -n afw-hub -g rg-platform-connectivity
az aks get-credentials -n aks-prod-main -g rg-prod-app-services
Confirm traffic between spokes routes through the firewall (check UDRs).
🧱 8. Extending the Landing Zone
Add Azure Policy Assignments to enforce tagging, TLS, logging.
Add Log Analytics workspace and diagnostics for Firewall/AKS.
Implement Private DNS zone links for name resolution across spokes.
Enable ExpressRoute Gateway using the existing GatewaySubnet.
🏁 Conclusion
You now have a production-ready Azure Landing Zone implemented entirely as code:
Governance with Management Groups
Secure hub with Firewall + UDRs
Isolated spokes for Dev/QA/Prod
Automated CI/CD via Azure DevOps
Identity and RBAC managed by Entra ID
This foundation scales as you onboard new workloads—just add new spokes or modules without breaking compliance or security boundaries.
No comments:
Post a Comment