Cyber Intelligence
Cloud Security20 min read

Azure Landing Zone Security Baseline: Step-by-Step Implementation

The CAF accelerator deploys the scaffolding but leaves the security controls unconfigured. This guide covers the specific steps needed after the accelerator runs: policy assignments with correct effects, management group RBAC design, the logging baseline, and network controls that must be explicitly enforced.

I
Microsoft Cloud Solution Architect
Azure Landing ZoneCloud Adoption FrameworkAzure PolicyManagement GroupsRBACAzure FirewallDefender for Cloud

The Gap Between a Deployed Landing Zone and a Secure One

Most teams deploy Azure Landing Zones from the Cloud Adoption Framework Terraform or Bicep accelerators and consider the security baseline done. It is not done. The CAF accelerators give you the structural scaffolding: management groups, connectivity subscriptions, a policy hierarchy. What they do not give you is the correct default policy effects, the right RBAC scope assignments, or the monitoring configuration that actually catches deviations. The gap between "deployed" and "secure" is where most large Azure environments accumulate their technical security debt.

This guide covers the specific controls that need to be configured after the CAF accelerator runs: policy assignments with the correct effects, management group RBAC design, logging baseline, and the network controls that the accelerator stubs out but does not enforce.

---

Management Group Hierarchy and Policy Inheritance

Why the Hierarchy Design Matters for Security

Azure Policy evaluates against the most restrictive applicable assignment. If you assign a Deny policy at the Tenant Root Management Group, it applies to every subscription in your tenant — including platform subscriptions, identity subscriptions, and everything else. A common mistake is assigning policies with Deny effect too high in the hierarchy, then creating exemptions for every platform team subscription. Exemptions are fine in limited numbers; more than ten exemptions on a single policy assignment is a sign that the assignment scope is wrong.

The CAF Landing Zone hierarchy uses three levels below the tenant root:

  • Platform management group: connectivity, identity, and management subscriptions
  • Landing Zones management group: corp (private connectivity required) and online (internet-facing, less restrictive network policies)
  • Sandbox management group: development environments with relaxed policies but still within tenant governance

The security principle for policies: assign at the lowest scope that achieves consistent enforcement without generating exemptions.

Policy Effect Reference

Before assigning any policy, be deliberate about the effect. The wrong effect on a policy does nothing to improve security and creates alert fatigue.

EffectWhat happensWhen to use
DenyBlocks the resource operationFor controls you enforce at deployment time (e.g., public IP on subnets)
AuditLogs a compliance state, no blockingFor visibility on existing deployments without breaking changes
DeployIfNotExistsDeploys a companion resource if missingFor logging agents, encryption settings, diagnostic settings
ModifyAdds or changes a property on an existing resourceFor tagging standards, network profile changes
AuditIfNotExistsLogs non-compliance when a companion resource is missingFor missing diagnostic settings, missing backup configs
For a new landing zone deployment, start with Audit for most controls. Promote to Deny only after confirming that existing resources in the scope are already compliant; otherwise you block your own deployment pipelines.

Assigning the Security Baseline Policy Initiative

The CAF provides a built-in initiative that maps to MCSB (Microsoft Cloud Security Benchmark). Assign it at the Landing Zones management group, not tenant root, to avoid breaking platform subscriptions:

# Get the MCSB initiative definition ID
az policy set-definition list \
  --query "[?displayName=='Microsoft Cloud Security Benchmark'].id" \
  --output tsv

# Assign at Landing Zones management group with system-assigned MI for remediation az policy assignment create \ --name 'landing-zone-mcsb-baseline' \ --display-name 'Landing Zone MCSB Security Baseline' \ --policy-set-definition '<mcsb-initiative-id>' \ --scope '/providers/Microsoft.Management/managementGroups/landingzones' \ --mi-system-assigned \ --location 'eastus' \ --identity-scope '/providers/Microsoft.Management/managementGroups/landingzones'

The --mi-system-assigned flag is required because MCSB includes DeployIfNotExists policies that need a managed identity to remediate non-compliant resources. Without it, those policies stay in non-compliant state permanently with no remediation.

After assignment, trigger a remediation task for the DeployIfNotExists policies immediately:

az policy remediation create \
  --name 'mcsb-initial-remediation' \
  --policy-assignment 'landing-zone-mcsb-baseline' \
  --resource-discovery-mode ReEvaluateCompliance \
  --scope '/providers/Microsoft.Management/managementGroups/landingzones'

---

RBAC Design for Landing Zone Subscriptions

The Separation of Duties Problem in Azure

Azure's built-in Owner role is too broad for production subscriptions. Owner includes the ability to change RBAC assignments, bypass resource locks, and delete the subscription's core infrastructure. Yet many landing zone deployments end up with Owner assignments on platform engineers because the teams "need to manage everything."

The correct model separates three concerns:

  1. Infrastructure deployment: permissions to create and modify resources
  2. RBAC management: permissions to grant access to others
  3. Security configuration: permissions to configure diagnostic settings, policies, and Defender plans

Azure has no built-in role that covers only (1) without (2). Contributor covers (1) but not (2). The practical design is to grant Contributor to pipeline service principals and restrict Owner to break-glass accounts with PIM activation.

Management Group RBAC Assignment Pattern

Assign roles at management group scope sparingly. Every role assigned at management group scope applies to all subscriptions that now exist and all subscriptions added in the future. A Contributor assignment at management group scope to an engineering team is a standing access grant to all future landing zone subscriptions.

Principal typeRoleScope
Platform team service principalContributorPlatform management group
Application team service principalContributorSpecific application subscription
Security team service principalSecurity ReaderTenant Root Management Group
Break-glass accountOwnerTenant Root (PIM, time-limited)
Compliance auditorReaderTenant Root Management Group
The security team needs Security Reader at tenant root to operate Defender for Cloud and Sentinel across all subscriptions. Giving anything above Reader at tenant root to non-platform teams is a pattern to avoid.

Bicep Role Assignment at Management Group Scope

targetScope = 'managementGroup'

param managementGroupId string param securityTeamPrincipalId string param platformPipelinePrincipalId string

// Security Reader for security team at MG scope var securityReaderRoleId = '39bc4728-0917-49c7-9d2c-d95423bc2eb4' resource securityTeamMgRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(managementGroupId, securityTeamPrincipalId, securityReaderRoleId) properties: { roleDefinitionId: tenantResourceId('Microsoft.Authorization/roleDefinitions', securityReaderRoleId) principalId: securityTeamPrincipalId principalType: 'ServicePrincipal' } }

// Contributor for platform pipeline at platform MG only var contributorRoleId = 'b24988ac-6180-42a0-ab88-20f7382dd24c' resource pipelinePlatformRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(managementGroupId, platformPipelinePrincipalId, contributorRoleId) properties: { roleDefinitionId: tenantResourceId('Microsoft.Authorization/roleDefinitions', contributorRoleId) principalId: platformPipelinePrincipalId principalType: 'ServicePrincipal' } }

---

Logging Baseline

What the CAF Accelerator Does Not Configure

The CAF accelerators create a Log Analytics workspace and storage account in the management subscription. They do not configure diagnostic settings on individual subscriptions' Activity Logs or on the platform resources themselves. Deploying the accelerator and assuming your audit trail is complete is a significant misunderstanding.

Every subscription needs its Activity Log forwarded to the central Log Analytics workspace. The Activity Log records all control plane operations: who created what resource, who changed RBAC, who modified a network security group rule. Without it, incident investigation in Azure is guesswork.

Use a DeployIfNotExists policy assigned at tenant root scope to enforce diagnostic settings on subscriptions:

az policy assignment create \
  --name 'deploy-subscription-diag-settings' \
  --display-name 'Deploy Activity Log Diagnostic Settings to Log Analytics' \
  --policy '/providers/Microsoft.Authorization/policyDefinitions/2465583e-4e78-4c15-b6be-a36cbc7c8b0f' \
  --scope '/providers/Microsoft.Management/managementGroups/<tenant-root-mg-id>' \
  --mi-system-assigned \
  --location 'eastus' \
  --params '{"logAnalytics":{"value":"/subscriptions/<mgmt-sub-id>/resourceGroups/<rg>/providers/Microsoft.OperationalInsights/workspaces/<workspace-name>"}}'

After assigning, run a remediation task to cover existing subscriptions.

KQL: Finding Subscriptions Without Diagnostic Settings

let AllSubscriptions = ResourceContainers
| where type == 'microsoft.resources/subscriptions'
| project subscriptionId, subscriptionName = name;

let ConfiguredSubscriptions = DiagnosticSettings | where resourceType == 'microsoft.resources/subscriptions' | project subscriptionId = substring(resourceId, indexof(resourceId, '/subscriptions/') + 15, 36);

AllSubscriptions | join kind=leftanti ConfiguredSubscriptions on subscriptionId | project subscriptionId, subscriptionName | order by subscriptionName asc

Run this from the central Log Analytics workspace monthly. Any subscription appearing here is invisible to your central audit trail.

Minimum Log Categories Per Service

ServiceLog categoriesRetention
Activity LogAdministrative, Security, Policy, Alert90 days hot, 365 days archive
Entra IDAuditLogs, SignInLogs, NonInteractiveUserSignInLogs90 days
Azure FirewallApplicationRule, NetworkRule, DnsProxy90 days
Key VaultAuditEvent, AzurePolicyEvaluationDetails90 days
NSG Flow LogsAll flows via Network Watcher30 days
---

Network Security Baseline

Hub-and-Spoke vs. Virtual WAN

The CAF Connectivity subscription supports two topologies: traditional hub-and-spoke with Azure Firewall, or Azure Virtual WAN with Firewall Manager. The security implications differ:

FeatureHub-and-SpokeVirtual WAN
Routing controlFull control via UDRs on each spokeWAN-managed routing, limited UDR support
Firewall policy managementAzure Firewall Policy (full ARM/Bicep support)Firewall Manager policy (limited ARM support)
RBAC granularityPer-resource controlCoarse-grained WAN resource
Cost at scaleLinear with spoke countMore cost-efficient at large hub counts
For security teams that need policy-as-code control over firewall rules and routing, hub-and-spoke is the better choice. Virtual WAN reduces operational overhead but limits the precision of security controls.

Baseline Azure Firewall Policy Configuration

Every landing zone with outbound internet connectivity needs Azure Firewall configured with at minimum:

  1. DNS proxy enabled: routes all DNS through the firewall for FQDN-based network rules and DNS logging
  2. Threat intelligence mode set to Deny: blocks known-malicious IPs and domains at the firewall layer
  3. Default-deny outbound: explicit deny rule at priority 65000 for all protocols to all destinations
  4. Windows Update FQDN rules: *.update.microsoft.com and *.windowsupdate.com as application rules
  5. Azure Monitor agent outbound: required FQDNs for the monitoring agent to reach Log Analytics
# Set threat intelligence mode to Alert and Deny
az network firewall policy update \
  --name <firewall-policy-name> \
  --resource-group <connectivity-rg> \
  --threat-intel-mode 'Deny'

# Enable DNS proxy on the firewall policy az network firewall policy update \ --name <firewall-policy-name> \ --resource-group <connectivity-rg> \ --enable-dns-proxy true

NSG Baseline and Flow Log Requirements

Every subnet in a landing zone VNet needs an NSG. Enforce this via the built-in Azure Policy definition e37c4fe4-3e89-4748-b843-bce89bba6c6e (Subnets should have a Network Security Group) with Deny effect at the Landing Zones management group.

NSG rules that must be explicit:

  • Deny inbound RDP (3389) from Internet: explicit deny, not relying on default deny
  • Deny inbound SSH (22) from Internet: explicit deny
  • Allow inbound from Azure Bastion subnet only for management access
  • Deny all outbound to the Internet on subnets containing sensitive workloads, with application-specific allow rules above it

Flow logs are required on all NSGs in production subscriptions. Enable via Network Watcher:

az network watcher flow-log create \
  --name 'nsg-flow-log-prod' \
  --nsg '<nsg-resource-id>' \
  --storage-account '<storage-account-id>' \
  --enabled true \
  --retention 30 \
  --resource-group <network-rg> \
  --location <region>

---

Identity Subscription Controls

Break-Glass Account Configuration

Every landing zone deployment needs at minimum two break-glass accounts configured before going to production. These accounts:

  • Are cloud-only (not synced from on-premises Active Directory)
  • Are excluded from all Conditional Access policies
  • Have Global Administrator assigned permanently (not via PIM)
  • Have sign-in activity monitored with a high-priority alert

The KQL alert for any break-glass sign-in event:

SignInLogs
| where UserPrincipalName in ('<break-glass-account-1-upn>', '<break-glass-account-2-upn>')
| project TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress,
    ResultType, ConditionalAccessStatus, Location
| order by TimeGenerated desc

This alert should fire within 5 minutes of any sign-in attempt. Configure as a Microsoft Sentinel analytics rule with Critical severity and a suppression period of zero (every event matters, no suppression).

Privileged Identity Management Enforcement

All Azure resource roles above Reader in production subscriptions must be delivered via PIM. Permanent active Contributor or Owner assignments outside of break-glass accounts are a policy violation in any well-governed landing zone.

Enforce this with an Azure Policy custom rule targeting Microsoft.Authorization/roleAssignments/write conditions, combined with a PIM access review scheduled monthly for the roles:

  • Owner at subscription scope
  • Contributor at subscription scope
  • User Access Administrator at any scope

For the federated identity pattern (GitHub Actions, pipelines deploying to Azure), use federated credentials instead of client secrets or certificates. The federated credentials guide covers the Terraform and GitHub Actions configuration.

---

Defender for Cloud Baseline

Plans to Enable on Day One

Defender for Cloud bills per-resource-type. For a landing zone security baseline, enable these plans across all subscriptions in the Landing Zones management group:

PlanMonthly cost estimateWorkloads covered
Defender for Servers P2~$15/server/monthVMs, Arc-enabled servers
Defender for Storage~$10/storage account/monthBlob, File, Queue, Table
Defender for Key Vault~$0.02/10K operationsKey Vault secret operations
Defender for Azure Resource Manager$4/subscription/month (flat)All ARM operations
Defender for CSPM (enhanced)Per-billable resourceAttack path analysis, cloud security graph
For Defender for Containers and AKS-specific controls, see the AKS security guide. For a detailed comparison of Defender for Cloud CSPM against third-party tools, the CSPM comparison guide covers the trade-offs.

Auto-Provisioning Agents Across All Subscriptions

Enable auto-provisioning in Defender for Cloud for the Azure Monitor Agent across all landing zone subscriptions. This ensures that any new VM deployed in a landing zone subscription gets the monitoring agent automatically:

# Enable auto-provisioning for Azure Monitor Agent
az security auto-provisioning-setting update \
  --name 'mma-agent' \
  --auto-provision 'On' \
  --subscription <subscription-id>

# Run for all landing zone subscriptions for sub in $(az account list --query "[?tenantId=='<tenant-id>'].id" --output tsv); do az security auto-provisioning-setting update \ --name 'mma-agent' \ --auto-provision 'On' \ --subscription $sub done

Defender for Cloud Export to Sentinel

Defender for Cloud security alerts need to flow into your central SIEM. Configure the continuous export from Defender for Cloud to the Log Analytics workspace that Sentinel uses:

az security export-settings update \
  --name 'default' \
  --resource-group <management-rg> \
  --exported-data-types 'Alerts,Assessments,SecureScoreControls' \
  --workspace-resource-id '<log-analytics-workspace-id>'

---

Policy Governance: What to Exempt and What to Never Exempt

Policies That Must Never Have Production Exemptions

These controls protect fundamental guarantees that an exemption would directly undermine:

  • Require encryption at rest: no unencrypted storage or managed disk resources
  • Deny public blob access on storage accounts, including static website containers (use Azure CDN with SAS tokens instead)
  • Deny resource creation outside approved regions: data residency requirement
  • Deny SKUs without private endpoint support for Key Vault, Storage, and SQL in corp landing zones

KQL: Monitoring Policy Compliance Drift

// Resources non-compliant for more than 7 days without active remediation
PolicyResources
| where type == 'microsoft.policyinsights/policystates'
| where properties.complianceState == 'NonCompliant'
| where properties.timestamp < ago(7d)
| extend PolicyDefinitionName = tostring(properties.policyDefinitionName),
    ResourceType = tostring(properties.resourceType),
    SubscriptionId = tostring(properties.subscriptionId)
| summarize OldestNonCompliance = min(properties.timestamp), Count = count()
    by PolicyDefinitionName, ResourceType, SubscriptionId
| order by Count desc

Resources non-compliant for more than 7 days without an active remediation task or exemption represent unmanaged configuration drift. Review this query weekly in the central Log Analytics workspace.

---

Zero Trust Alignment

The landing zone security baseline described here implements the network and identity layers of a zero trust architecture: explicit verification via Conditional Access and PIM, least-privilege RBAC at the correct scope, and assume-breach posture via full logging and Defender for Cloud. The zero trust complete guide covers the policy framework that this technical baseline implements.

One control that landing zones frequently miss: workload identity governance. Service principals and managed identities deployed by application teams in landing zone subscriptions are not governed by PIM or access reviews by default. Implement a quarterly review of all service principal role assignments using Entra ID Access Reviews, scoped to Azure resource roles, to catch accumulation of standing privileges in application subscriptions. The NHI security guide covers the governance framework for these identities.

---

Hardening Checklist

  • [ ] Management group hierarchy deployed with Platform, Landing Zones, and Sandbox separate from tenant root
  • [ ] MCSB initiative assigned at Landing Zones management group with system-assigned MI for remediation
  • [ ] Initial remediation task triggered for all DeployIfNotExists policies after assignment
  • [ ] Activity Log diagnostic settings deployed to all subscriptions forwarding to central Log Analytics workspace
  • [ ] KQL query scheduled monthly to identify subscriptions without diagnostic settings
  • [ ] No Owner assignments at management group scope for non-break-glass accounts
  • [ ] Break-glass accounts configured: cloud-only, excluded from CA policies, Global Admin, sign-in alert deployed in Sentinel
  • [ ] All Azure resource Owner and Contributor roles delivered via PIM except break-glass accounts
  • [ ] Federated credentials used for pipeline service principals instead of client secrets
  • [ ] Azure Firewall deployed in connectivity subscription with threat intelligence mode set to Deny
  • [ ] DNS proxy enabled on Azure Firewall policy
  • [ ] NSG required on all subnets enforced via Deny policy at Landing Zones management group
  • [ ] NSG Flow Logs enabled on all production NSGs with 30-day retention minimum
  • [ ] RDP (3389) and SSH (22) denied from Internet explicitly in all production NSGs
  • [ ] Defender for Servers P2 enabled across all subscriptions containing VMs
  • [ ] Defender for Storage enabled across all subscriptions containing storage accounts
  • [ ] Defender for Azure Resource Manager enabled on all subscriptions
  • [ ] Auto-provisioning enabled for Azure Monitor Agent across all landing zone subscriptions
  • [ ] Continuous export configured from Defender for Cloud to Sentinel Log Analytics workspace
  • [ ] Public IP creation denied in identity subscription
  • [ ] No public blob access policy enforced with Deny effect at Landing Zones management group
  • [ ] Policy compliance drift query scheduled weekly in Log Analytics
  • [ ] Quarterly access review configured for service principal Azure resource role assignments
N

Recommended tool: Nordpass

Up to 40% commission

Get weekly security insights

Cloud security, zero trust, and identity guides — straight to your inbox.

I

Microsoft Cloud Solution Architect

Cloud Solution Architect with deep expertise in Microsoft Azure and a strong background in systems and IT infrastructure. Passionate about cloud technologies, security best practices, and helping organizations modernize their infrastructure.

Share this article

Questions & Answers

Related Articles

Need Help with Your Security?

Our team of security experts can help you implement the strategies discussed in this article.

Contact Us