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.

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.
| Effect | What happens | When to use |
|---|---|---|
| Deny | Blocks the resource operation | For controls you enforce at deployment time (e.g., public IP on subnets) |
| Audit | Logs a compliance state, no blocking | For visibility on existing deployments without breaking changes |
| DeployIfNotExists | Deploys a companion resource if missing | For logging agents, encryption settings, diagnostic settings |
| Modify | Adds or changes a property on an existing resource | For tagging standards, network profile changes |
| AuditIfNotExists | Logs non-compliance when a companion resource is missing | For 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:
- Infrastructure deployment: permissions to create and modify resources
- RBAC management: permissions to grant access to others
- 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 type | Role | Scope |
|---|---|---|
| Platform team service principal | Contributor | Platform management group |
| Application team service principal | Contributor | Specific application subscription |
| Security team service principal | Security Reader | Tenant Root Management Group |
| Break-glass account | Owner | Tenant Root (PIM, time-limited) |
| Compliance auditor | Reader | Tenant 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 ascRun this from the central Log Analytics workspace monthly. Any subscription appearing here is invisible to your central audit trail.
Minimum Log Categories Per Service
| Service | Log categories | Retention |
|---|---|---|
| Activity Log | Administrative, Security, Policy, Alert | 90 days hot, 365 days archive |
| Entra ID | AuditLogs, SignInLogs, NonInteractiveUserSignInLogs | 90 days |
| Azure Firewall | ApplicationRule, NetworkRule, DnsProxy | 90 days |
| Key Vault | AuditEvent, AzurePolicyEvaluationDetails | 90 days |
| NSG Flow Logs | All flows via Network Watcher | 30 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:
| Feature | Hub-and-Spoke | Virtual WAN |
|---|---|---|
| Routing control | Full control via UDRs on each spoke | WAN-managed routing, limited UDR support |
| Firewall policy management | Azure Firewall Policy (full ARM/Bicep support) | Firewall Manager policy (limited ARM support) |
| RBAC granularity | Per-resource control | Coarse-grained WAN resource |
| Cost at scale | Linear with spoke count | More 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:
- DNS proxy enabled: routes all DNS through the firewall for FQDN-based network rules and DNS logging
- Threat intelligence mode set to Deny: blocks known-malicious IPs and domains at the firewall layer
- Default-deny outbound: explicit deny rule at priority 65000 for all protocols to all destinations
- Windows Update FQDN rules: *.update.microsoft.com and *.windowsupdate.com as application rules
- 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 trueNSG 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 descThis 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](/blog/flexible-federated-identity-credentials-entra-github-terraform) 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:
| Plan | Monthly cost estimate | Workloads covered |
|---|---|---|
| Defender for Servers P2 | ~$15/server/month | VMs, Arc-enabled servers |
| Defender for Storage | ~$10/storage account/month | Blob, File, Queue, Table |
| Defender for Key Vault | ~$0.02/10K operations | Key Vault secret operations |
| Defender for Azure Resource Manager | $4/subscription/month (flat) | All ARM operations |
| Defender for CSPM (enhanced) | Per-billable resource | Attack path analysis, cloud security graph |
For Defender for Containers and AKS-specific controls, see the [AKS security guide](/blog/aks-container-security-defender-for-containers-2026). For a detailed comparison of Defender for Cloud CSPM against third-party tools, the [CSPM comparison guide](/blog/best-cspm-tools-2026-defender-for-cloud-vs-wiz-vs-orca-vs-prisma-cloud) 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
doneDefender 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 descResources 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](/blog/what-is-zero-trust-security-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](/blog/non-human-identities-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
Frequently Asked Questions
What is the difference between assigning Azure Policy at the management group scope versus the subscription scope?
Assigning at management group scope applies the policy to all subscriptions and resource groups under that management group, including any subscriptions added in the future. Assigning at subscription scope limits coverage to that specific subscription and must be repeated manually for each new subscription. For a landing zone with multiple subscriptions under a Landing Zones management group, management group scope is the correct choice for baseline controls such as requiring diagnostic settings and NSG deployment.
What does the Microsoft Cloud Security Benchmark initiative actually enforce versus audit?
MCSB contains a mix of policy effects. Audit policies record non-compliant resources in the Policy compliance dashboard but do not block deployments. DeployIfNotExists policies automatically deploy a remediation resource (such as a Log Analytics diagnostic setting) when a compliant one does not exist, but require a system-assigned managed identity on the policy assignment and a remediation task to process existing resources. Deny policies block non-compliant resource deployments at the Azure Resource Manager layer before resources are created. Enabling MCSB does not immediately enforce all controls: you must review and change effects from Audit to Deny for the controls you want to enforce.
Why should Owner and Contributor roles at management group scope be avoided for regular accounts?
A management group Owner or Contributor can create, modify, or delete any resource across every subscription in the hierarchy. This scope is far broader than any individual workload or platform team needs and creates an enormous blast radius for a compromised account. Break-glass accounts are a legitimate exception because they are needed precisely when normal access controls fail. For all other administrative work, privilege should be scoped to the specific subscription or resource group where work is being performed, and delivered via PIM to eliminate standing access.
What is the correct way to implement DNS resolution for spoke workloads that need to reach private endpoints in a hub-and-spoke landing zone?
The recommended pattern is to deploy Private DNS Zones in the connectivity subscription and enable DNS proxy on the Azure Firewall. Spoke virtual networks use a custom DNS server setting pointing to the Azure Firewall's private IP. DNS queries from spokes are forwarded to the firewall, which resolves private endpoint records via the Private DNS Zones linked in the hub. This centralized model avoids duplicating Private DNS Zone links across all spoke VNets and ensures consistent resolution from any spoke in the landing zone.
How often should Azure Policy compliance drift be reviewed in a landing zone deployment?
A weekly review cadence is the operational baseline for most organizations. The KQL query for resources non-compliant for more than seven days surfaces configuration drift that has persisted without a remediation task or exemption. For high-impact controls such as NSG flow log enablement or Defender for Cloud plan activation, immediate alerts via Azure Monitor or Logic Apps provide faster detection. Quarterly reviews of exemptions and accepted risks are also recommended to prevent drift from accumulating under legitimate-looking exceptions.
Get weekly security insights
Cloud security, zero trust, and identity guides — straight to your inbox.
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