I remember my first Azure project. We spun up a subscription, deployed a few VMs, pointed a DNS record at them, and called it "the cloud." Six months later we had security alerts nobody read, cost overruns nobody tracked, and a network topology that looked like spaghetti thrown at a whiteboard. Sound familiar?

That experience is exactly why Azure Landing Zones exist — and why I want to show you how to deploy one from scratch in a single weekend. Not a theoretical whitepaper exercise. A real, working, production-ready environment you can stand up on Saturday morning and have governance, security, networking, and identity wired in by Sunday evening.

If you have been putting off landing zone adoption because it feels like a three-month consulting engagement, this guide is for you. I am going to walk through the entire process using Azure CLI and Bicep, covering every major component. By the end, you will have a management group hierarchy, hub-spoke network, Azure Policy enforcement, Defender for Cloud, Sentinel SIEM, and Entra ID integration — all automated and reproducible.

Let us get started.

Why Landing Zones Matter (and Why 2026 Is the Right Time)

Microsoft Azure Landing Zone is not new — the Cloud Adoption Framework has been around since 2019. But two things have changed in 2026 that make this the perfect moment to adopt.

First, the Malaysia West (Kuala Lumpur) Azure region is fully operational, with three availability zones and in-country data residency for customer data. For Southeast Asian enterprises — especially those answering to PDPA or Bank Negara requirements — this removes the last major blocker for in-region deployment.

Second, the landing zone accelerators — ALZ-Bicep, ALZ-Terraform, and the portal-based Azure Landing Zone Accelerator — have matured significantly. They are faster, more modular, and far easier to customize than they were even a year ago. You no longer need to wade through nested ARM templates manually; the heavy lifting is done by well-maintained, community-reviewed modules.

The business case is straightforward: without a landing zone, every team provisions resources their own way. Network isolation is inconsistent. Policies are either nonexistent or bolted on after a breach. Cost management is an afterthought. A landing zone gives you guardrails that are automated, auditable, and scalable.

The 48-Hour Roadmap

Here is how I break down the weekend:

  • Saturday Morning (Hours 0-4): Management group hierarchy and subscription governance
  • Saturday Afternoon (Hours 4-8): Hub-spoke networking and connectivity
  • Sunday Morning (Hours 8-14): Identity, Azure Policy, and Defender for Cloud
  • Sunday Afternoon (Hours 14-20): Sentinel, monitoring, and landing zone subscriptions

This is aggressive but achievable, especially with the code I am sharing below. Let me walk through each phase.

Phase 1: Management Groups and Subscription Structure

Everything starts with governance hierarchy. Azure Management Groups let you apply policies and RBAC at scale, above individual subscriptions. The default hierarchy gives you "Tenant Root Group" — but you should not leave it empty.

First, log in and set your defaults:

# Login and set your subscription context
az login
az account set --subscription "your-platform-subscription-id"
az account show --query id --output tsv

# Set default values for all commands
az configure --defaults location=malaysiawest

Now create the management group structure. I recommend four child groups under the tenant root, following the Cloud Adoption Framework pattern:

# Create management group hierarchy
MG_ROOT="your-tenant-id"

az account management-group create --name "Contoso"
az account management-group create --name "Contoso-Platform" --parent "Contoso"
az account management-group create --name "Contoso-Online" --parent "Contoso"
az account management-group create --name "Contoso-Sandbox" --parent "Contoso"
az account management-group create --name "Contoso-Management" --parent "Contoso"

This gives you a clean separation: Platform for shared services, Online for internet-facing workloads, Sandbox for experimentation, and Management for monitoring and logging.

Next, create the subscriptions themselves:

# Create platform subscriptions using alias
az account alias create \
  --name "contoso-connectivity-alias" \
  --display-name "contoso-connectivity" \
  --billing-scope "/providers/Microsoft.Billing/billingAccounts/{your-billing-account-id}" \
  --workload "Production"

az account alias create \
  --name "contoso-management-alias" \
  --display-name "contoso-management" \
  --billing-scope "/providers/Microsoft.Billing/billingAccounts/{your-billing-account-id}" \
  --workload "Production"

# Move subscriptions under the right management group
az account management-group subscription add \
  --name "Contoso-Platform" \
  --subscription "contoso-connectivity"

Weekend Tip: Run this in parallel with creating your Bicep parameter files. While the subscriptions are provisioning (it takes a few minutes), start drafting your network topology.

Phase 2: Hub-Spoke Networking with Bicep

The hub-spoke topology is the backbone of any landing zone. The hub subscription hosts shared services — VPN/ExpressRoute gateways, Azure Firewall, DNS, and jump boxes — while spoke subscriptions connect to the hub via Virtual Network Peering.

Here is a simplified Bicep template for the hub network. Note that every subnet that needs protection gets an NSG — define them explicitly rather than relying on defaults:

// hub-network.bicep
param location string = resourceGroup().location
param hubVnetName string = 'vnet-hub'
param hubVnetAddressPrefix string = '10.0.0.0/16'

resource nsgBastion 'Microsoft.Network/networkSecurityGroups@2025-01-01' = {
  name: 'nsg-bastion'
  location: location
  properties: {} // Add Bastion service tag rules before production use
}

resource nsgManagement 'Microsoft.Network/networkSecurityGroups@2025-01-01' = {
  name: 'nsg-management'
  location: location
  properties: {} // Deny internet inbound; allow management traffic only
}

resource hubVnet 'Microsoft.Network/virtualNetworks@2025-01-01' = {
  name: hubVnetName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [hubVnetAddressPrefix]
    }
    subnets: [
      {
        name: 'GatewaySubnet'
        properties: {
          addressPrefix: '10.0.1.0/24'
        }
      }
      {
        name: 'AzureFirewallSubnet'
        properties: {
          addressPrefix: '10.0.2.0/24'
        }
      }
      {
        name: 'AzureBastionSubnet'
        properties: {
          addressPrefix: '10.0.3.0/24'
          networkSecurityGroup: {
            id: nsgBastion.id
          }
        }
      }
      {
        name: 'Management'
        properties: {
          addressPrefix: '10.0.4.0/24'
          networkSecurityGroup: {
            id: nsgManagement.id
          }
          routeTable: {
            id: rtHub.id
          }
        }
      }
    ]
  }
}

resource rtHub 'Microsoft.Network/routeTables@2025-01-01' = {
  name: 'rt-hub'
  location: location
  properties: {
    routes: [
      {
        name: 'DefaultToFirewall'
        properties: {
          addressPrefix: '0.0.0.0/0'
          nextHopType: 'VirtualAppliance'
          nextHopIpAddress: '10.0.2.4'
        }
      }
    ]
  }
}

output hubVnetId string = hubVnet.id

For a spoke subscription, create a separate Bicep module that deploys a virtual network and peers it to the hub:

// spoke-network.bicep
param location string = resourceGroup().location
param spokeVnetName string = 'vnet-workload'
param spokeVnetAddressPrefix string = '10.1.0.0/16'
param hubVnetId string

resource spokeVnet 'Microsoft.Network/virtualNetworks@2025-01-01' = {
  name: spokeVnetName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [spokeVnetAddressPrefix]
    }
    subnets: [
      {
        name: 'WorkloadSubnet'
        properties: {
          addressPrefix: '10.1.1.0/24'
        }
      }
      {
        name: 'DataSubnet'
        properties: {
          addressPrefix: '10.1.2.0/24'
        }
      }
    ]
  }
}

resource spokeToHubPeer 'Microsoft.Network/virtualNetworks/peerings@2025-01-01' = {
  parent: spokeVnet
  name: 'peering-to-hub'
  properties: {
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    remoteVirtualNetwork: {
      id: hubVnetId
    }
  }
}

Deploy the hub first, then the spokes. The key addresses you need to plan upfront:

  • Hub VNet: 10.0.0.0/16 (gateways, firewall, bastion, management)
  • Spoke VNet 1: 10.1.0.0/16 (production workloads)
  • Spoke VNet 2: 10.2.0.0/16 (non-production / DevTest)
  • Spoke VNet 3: 10.3.0.0/16 (shared services like AKS)

Do not forget to enable "Use Remote Gateway Transit" on the hub-side peering when you add VPN or ExpressRoute gateways later. I have seen this forgotten more times than I can count.

Weekend Tip: Use a parameter file to separate environment-specific values. Create hub.bicepparam and spoke-prod.bicepparam with different address spaces. This keeps your templates clean and reusable.

Phase 3: Identity with Entra ID

No landing zone is complete without proper identity governance. Entra ID (formerly Azure AD) is the backbone of your zero-trust architecture. Here is what you need to wire up on Sunday morning.

First, ensure your subscriptions are governed by Entra ID groups, not individual user accounts. Create groups for RBAC using the Azure CLI:

# Create an Entra ID group for production workloads
az ad group create \
  --display-name "Contoso-Prod-Contributors" \
  --mail-nickname "contoso-prod-contributors"

# Assign Contributor role at the landing zone subscription scope
az role assignment create \
  --assignee-object-id "$(az ad group show \
    --group 'Contoso-Prod-Contributors' \
    --query id -o tsv)" \
  --role "Contributor" \
  --scope "/subscriptions/your-prod-subscription-id"

For privileged access, deploy Azure Privileged Identity Management (PIM) and Conditional Access policies. This is one area where I recommend using the Microsoft Graph PowerShell SDK alongside the Azure CLI:

# Require MFA and compliant device for admin access
# (run in PowerShell with Microsoft.Graph module)
New-MgIdentityConditionalAccessPolicy -DisplayName "Require MFA for Admins" -State "enabled" -Conditions @{
    Applications = @{IncludeApplications = @("All")}
    Users = @{IncludeGroups = @("your-admin-group-id")}
    Platforms = @{IncludePlatforms = @("windows", "macOS", "linux")}
} -GrantControls = @{
    Operator = "OR"
    BuiltInControls = @("mfa")
}

Weekend Tip: Use Microsoft Entra Connect Cloud Sync if you have an on-premises AD. If you are cloud-native, configure Entra ID as your primary identity provider and enable passwordless authentication (FIDO2 or Windows Hello) from day one.

Phase 4: Azure Policy for Governance

Azure Policy is your automated compliance engine. Without it, teams will inevitably create resources in the wrong regions, use unapproved VM sizes, or deploy public endpoints that should be private.

Start with Azure Policy built-ins — every Azure tenant ships with them. A good first guardrail is the built-in "Allowed locations" policy, which restricts where resources can be created:

# Assign the built-in "Allowed locations" policy at the root management group
# (definition ID: e56962a6-4747-49cd-b67b-bf8b01975c4c)
az policy assignment create \
  --name "AllowedLocations" \
  --display-name "Allowed locations for Contoso" \
  --policy "e56962a6-4747-49cd-b67b-bf8b01975c4c" \
  --scope "/providers/Microsoft.Management/managementGroups/Contoso" \
  --params '{
    "listOfAllowedLocations": {
      "value": ["malaysiawest", "southeastasia"]
    }
  }'

# Browse more built-ins you may want
az policy definition list \
  --query "[?contains(displayName, 'Landing Zone') || contains(displayName, 'Azure Security')].{name:name, displayName:displayName} | [0:10]" \
  --output table

For the full enterprise governance stack, you do not need to author these yourself: the ALZ-Bicep modules and the Azure Landing Zone Accelerator assign the complete Azure Landing Zone policy initiatives for you. For a weekend build, start with a handful of built-ins.

For more granular control, create custom policies. For example, a policy that requires tags on all resources. Note the quoting: the rule references a tag name with square brackets, so keep the JSON in a file or escape carefully — this trips people up constantly:

cat > /tmp/require-costcenter.json <<'EOF'
{
  "if": {
    "not": {
      "field": "tags['CostCenter']",
      "exists": true
    }
  },
  "then": {
    "effect": "audit"
  }
}
EOF

az policy definition create \
  --name "RequireCostCenterTag" \
  --display-name "Require CostCenter tag on resources" \
  --rules @/tmp/require-costcenter.json \
  --mode All

I cannot stress this enough: start with "audit" effect and switch to "deny" once you are confident in your policy definitions. Deploying a deny policy on a Saturday night without testing will generate angry Slack messages on Sunday morning.

Phase 5: Defender for Cloud and Sentinel

Security is not optional, and in 2026 it should be the first thing you turn on, not the last.

Enable Defender for Cloud across your subscriptions:

# Enable Defender for Cloud plans
az security pricing create --name "VirtualMachines" --tier "Standard"
az security pricing create --name "AppServices" --tier "Standard"
az security pricing create --name "SqlServers" --tier "Standard"
az security pricing create --name "StorageAccounts" --tier "Standard"
az security pricing create --name "KeyVaults" --tier "Standard"

# Set security contact (current CLI parameter set)
az security contact create \
  --name "default" \
  --emails "[email protected]" \
  --notifications-by-role '{"state":"On","roles":["Owner"]}' \
  --alert-notifications '{"state":"On","minimalSeverity":"High"}'

For Sentinel, deploy a Log Analytics Workspace and enable the solution:

# Create Log Analytics Workspace
az monitor log-analytics workspace create \
  --resource-group "rg-security" \
  --workspace-name "law-contoso-security" \
  --location "malaysiaweast"

# Onboard Sentinel (Microsoft.SecurityInsights onboardingStates, stable API)
az rest --method put \
  --uri "https://management.azure.com/subscriptions/{subscription-id}/resourceGroups/rg-security/providers/Microsoft.OperationalInsights/workspaces/law-contoso-security/providers/Microsoft.SecurityInsights/onboardingStates/default?api-version=2024-03-01" \
  --body '{}'

Connect your diagnostic logs from the hub firewall, NSGs, and key resources to the workspace. Then enable the Microsoft Sentinel solution for built-in analytics rules and workbooks.

Weekend Tip: Use the Sentinel content hub to deploy ready-made detection rules. The "Microsoft Network Session Essentials" solution and the "Microsoft Defender for Cloud" connector give you immediate visibility without writing custom KQL queries.

Common Pitfalls I Have Seen

After helping organizations across Southeast Asia deploy landing zones, here are the mistakes that cost the most time:

  1. Address space overlap. Plan your IP addressing before you write a single line of code. Overlapping address spaces between on-prem and Azure will block VPN/ExpressRoute integration and force you to rebuild subnets.
  2. Forgetting DNS. Private DNS zones for Azure services (Blob, SQL, Key Vault) are not optional. Without them, service endpoints and private endpoints do not resolve correctly. Include privatelink.blob.core.windows.net and peers in your hub deployment.
  3. Over-engineering on day one. You do not need Azure Arc, Arc-enabled Kubernetes, and a full DevOps pipeline in the first weekend. Get the foundation solid — networking, identity, policy — and layer advanced capabilities on top incrementally.
  4. Ignoring cost. A landing zone subscription with Azure Firewall Premium, Bastion, and Defender for every resource tier can easily run $2,000+/month before you deploy a single workload. Right-size for your actual needs and use the Azure Advisor cost recommendations from day one.
  5. No automation for subscription provisioning. If your team needs a new landing zone subscription and the process is "file a ticket and wait two weeks," your governance model is already broken. Automate subscription creation through ServiceNow, GitHub Actions, or Azure DevOps pipelines.

Wrapping Up: Your Monday Morning Checklist

If you followed this guide over the weekend, here is what you should have on Monday:

  • Management group hierarchy under your tenant root
  • At least three subscriptions: connectivity, management, and one workload zone
  • Hub-spoke VNet peering with routing through Azure Firewall
  • Azure Policy assigned at the management group level (start with audit)
  • Defender for Cloud enabled on all subscriptions
  • Sentinel deployed and receiving logs
  • Entra ID RBAC groups mapped to subscription scopes
  • All infrastructure defined in Bicep and committed to a repo

That is a production-ready foundation. Not perfect — no landing zone is on day one — but governed, secure, and scalable.

Key Takeaways

  1. Start with governance, not resources. Management groups and Azure Policy give you guardrails before any workload lands. Design the hierarchy first, then provision subscriptions into it.
  2. Automate everything with Bicep. Manual portal deployments are not repeatable and not auditable. Every networking component, policy assignment, and security configuration should be defined as code.
  3. Plan your IP addressing like your career depends on it. Overlapping address spaces between on-premises and Azure are the number one cause of rework in landing zone deployments. Document your scheme before you start.
  4. Enable Defender and Sentinel early, not late. Security visibility is not a Phase 2 activity. Turn on Defender for Cloud and Sentinel on day one so you can catch misconfigurations while they are cheap to fix.
  5. Iterate, do not boil the ocean. A weekend landing zone is a foundation. Layer advanced capabilities — private endpoints, Azure Arc, FinOps dashboards, CI/CD pipelines — in subsequent sprints as your team matures.

This guide is part of my ongoing series on Azure architecture for Southeast Asian enterprises. If you are deploying in the Malaysia West region or need help with PDPA compliance in Azure, feel free to reach out. More articles at [wenfeng.my](https://wenfeng.my).