Introduction: Why Zero Trust Is No Longer Optional
The traditional perimeter-based security model is dead. With remote work, BYOD, cloud applications, and sophisticated threat actors, organizations must adopt a "never trust, always verify" approach. This deep dive implements a complete Zero Trust architecture using Microsoft's security stack — from identity verification through Entra ID, to device compliance with Intune, to threat detection with Defender XDR. Every access request is continuously evaluated against identity, device health, location, and risk signals.
Prerequisites
- Microsoft 365 E5 or E3 + Security Add-on
- Azure AD Premium P2 (Microsoft Entra ID P2)
- Microsoft Intune licenses
- Microsoft Defender for Endpoint Plan 2
- Azure subscription for Log Analytics and Sentinel
- Global Administrator or Security Administrator role
Phase 1: Identity Foundation with Microsoft Entra ID
Configuring Authentication Strength Policies
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess", "Policy.Read.All"
# Create custom authentication strength for privileged access
$authStrength = @{
displayName = "Privileged Access - Phishing Resistant"
description = "Requires phishing-resistant MFA for admin operations"
allowedCombinations = @(
"fido2",
"windowsHelloForBusiness",
"x509CertificateMultiFactor"
)
requirementsSatisfied = "mfa"
}
New-MgPolicyAuthenticationStrengthPolicy -BodyParameter $authStrength
# Configure Entra ID Protection risk policies
$signInRiskPolicy = @{
displayName = "High Risk Sign-In - Block"
state = "enabled"
conditions = @{
signInRiskLevels = @("high")
applications = @{ includeApplications = @("All") }
users = @{ includeUsers = @("All"); excludeUsers = @("breakglass@contoso.com") }
}
grantControls = @{
operator = "OR"
builtInControls = @("block")
}
}
Expected output:
Welcome to Microsoft Graph!
Privileged Identity Management (PIM) Configuration
# Configure PIM for Global Admin role
$roleDefinitionId = (Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq 'Global Administrator'").Id
# Set eligible assignment policy (max 4 hours, require justification + approval)
$pimPolicy = @{
rules = @(
@{
"@odata.type" = "#microsoft.graph.unifiedRoleManagementPolicyExpirationRule"
id = "Expiration_EndUser_Assignment"
isExpirationRequired = $true
maximumDuration = "PT4H"
target = @{ caller = "EndUser"; operations = @("All"); level = "Assignment" }
},
@{
"@odata.type" = "#microsoft.graph.unifiedRoleManagementPolicyApprovalRule"
id = "Approval_EndUser_Assignment"
setting = @{
isApprovalRequired = $true
approvalStages = @(@{
approvalStageTimeOutInDays = 1
isApproverJustificationRequired = $true
primaryApprovers = @(@{
"@odata.type" = "#microsoft.graph.groupMembers"
groupId = "security-admins-group-id"
})
})
}
},
@{
"@odata.type" = "#microsoft.graph.unifiedRoleManagementPolicyNotificationRule"
id = "Notification_Admin_EndUser_Assignment"
notificationType = "Email"
recipientType = "Admin"
notificationLevel = "Critical"
isDefaultRecipientsEnabled = $true
}
)
}
Phase 2: Conditional Access Policies
Implementing Adaptive Access Controls
{
"policies": [
{
"displayName": "CA001 - Require MFA for all users",
"state": "enabled",
"conditions": {
"users": {
"includeUsers": ["All"],
"excludeUsers": ["breakglass-account-id"]
},
"applications": { "includeApplications": ["All"] },
"clientAppTypes": ["browser", "mobileAppsAndDesktopClients"]
},
"grantControls": {
"operator": "OR",
"builtInControls": ["mfa"],
"authenticationStrength": { "id": "phishing-resistant-strength-id" }
}
},
{
"displayName": "CA002 - Require compliant device for Office 365",
"state": "enabled",
"conditions": {
"users": { "includeUsers": ["All"] },
"applications": { "includeApplications": ["Office365"] },
"platforms": { "includePlatforms": ["android", "iOS", "windows", "macOS"] }
},
"grantControls": {
"operator": "AND",
"builtInControls": ["compliantDevice", "mfa"]
},
"sessionControls": {
"signInFrequency": {
"value": 12,
"type": "hours",
"authenticationType": "primaryAndSecondaryAuthentication"
},
"persistentBrowser": { "mode": "never", "isEnabled": true }
}
},
{
"displayName": "CA003 - Block legacy authentication",
"state": "enabled",
"conditions": {
"users": { "includeUsers": ["All"] },
"applications": { "includeApplications": ["All"] },
"clientAppTypes": ["exchangeActiveSync", "other"]
},
"grantControls": {
"operator": "OR",
"builtInControls": ["block"]
}
},
{
"displayName": "CA004 - Require compliant network for admin portals",
"state": "enabled",
"conditions": {
"users": {
"includeRoles": [
"Global Administrator",
"Security Administrator",
"Exchange Administrator"
]
},
"applications": {
"includeApplications": [
"Microsoft Admin Portals"
]
},
"locations": {
"includeLocations": ["All"],
"excludeLocations": ["trusted-corporate-locations"]
}
},
"grantControls": {
"operator": "OR",
"builtInControls": ["block"]
}
}
]
}
Phase 3: Device Compliance with Intune
Windows Compliance Policy
{
"@odata.type": "#microsoft.graph.windows10CompliancePolicy",
"displayName": "Windows - Corporate Compliance",
"scheduledActionsForRule": [
{
"ruleName": "PasswordRequired",
"scheduledActionConfigurations": [
{
"actionType": "block",
"gracePeriodHours": 24,
"notificationTemplateId": "compliance-warning-template"
},
{
"actionType": "retire",
"gracePeriodHours": 720,
"notificationTemplateId": "compliance-retire-template"
}
]
}
],
"passwordRequired": true,
"passwordMinimumLength": 14,
"passwordRequiredType": "alphanumeric",
"passwordExpirationDays": 90,
"osMinimumVersion": "10.0.22621",
"secureBootEnabled": true,
"bitLockerEnabled": true,
"tpmRequired": true,
"antivirusRequired": true,
"antiSpywareRequired": true,
"defenderEnabled": true,
"defenderVersion": "4.18.2301",
"firewallEnabled": true,
"codeIntegrityEnabled": true,
"storageRequireEncryption": true,
"deviceThreatProtectionEnabled": true,
"deviceThreatProtectionRequiredSecurityLevel": "secured",
"advancedThreatProtectionRequiredSecurityLevel": "secured"
}
Configuration Profile for Security Hardening
# Create Intune configuration profile via Graph API
$securityBaseline = @{
"@odata.type" = "#microsoft.graph.windows10EndpointProtectionConfiguration"
displayName = "Zero Trust - Endpoint Hardening"
firewallBlockAllIncoming = $true
firewallProfileDomain = @{
firewallEnabled = "allowed"
inboundConnectionsBlocked = $true
outboundConnectionsRequired = $true
stealthModeBlocked = $false
}
applicationGuardEnabled = $true
applicationGuardAllowPrintToLocalPrinters = $false
applicationGuardAllowPersistence = $false
bitLockerEnableStorageCardEncryptionOnMobile = $true
bitLockerEncryptDevice = $true
bitLockerSystemDrivePolicy = @{
encryptionMethod = "xtsAes256"
startupAuthenticationRequired = $true
startupAuthenticationTpmUsage = "required"
startupAuthenticationTpmPinUsage = "required"
recoveryOptions = @{
enableRecoveryInformationSaveToStore = $true
recoveryKeyUsage = "required"
hideRecoveryOptions = $false
}
}
defenderSecurityCenterBlockExploitProtectionOverride = $true
defenderAdobeReaderLaunchChildProcess = "enable"
defenderOfficeAppsLaunchChildProcess = "enable"
defenderOfficeMacroCodeAllowWin32Imports = "block"
defenderScriptObfuscatedMacroCode = "block"
defenderScriptDownloadedPayloadExecution = "block"
}
Phase 4: Microsoft Defender XDR Integration
Configuring Advanced Hunting Queries
// Detect impossible travel - user signs in from two geographically distant locations
let impossibleTravelThreshold = 500; // km
IdentityLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonSuccess"
| project Timestamp, AccountUpn, IPAddress, City = tostring(Location.City),
Latitude = todouble(Location.Latitude), Longitude = todouble(Location.Longitude)
| order by AccountUpn, Timestamp asc
| serialize
| extend PrevTimestamp = prev(Timestamp, 1), PrevLat = prev(Latitude, 1),
PrevLon = prev(Longitude, 1), PrevUser = prev(AccountUpn, 1)
| where AccountUpn == PrevUser
| extend TimeDiffMinutes = datetime_diff('minute', Timestamp, PrevTimestamp)
| extend DistanceKm = geo_distance_2points(Longitude, Latitude, PrevLon, PrevLat) / 1000
| where DistanceKm > impossibleTravelThreshold and TimeDiffMinutes < 120
| project Timestamp, AccountUpn, DistanceKm, TimeDiffMinutes,
RequiredSpeedKmh = DistanceKm / (TimeDiffMinutes / 60.0)
// Detect credential stuffing patterns
IdentityLogonEvents
| where Timestamp > ago(1h)
| where ActionType == "LogonFailed"
| summarize FailedAttempts = count(), DistinctAccounts = dcount(AccountUpn),
Accounts = make_set(AccountUpn, 10) by IPAddress, bin(Timestamp, 5m)
| where FailedAttempts > 20 and DistinctAccounts > 5
| order by FailedAttempts desc
// Detect suspicious mailbox rules (BEC indicator)
CloudAppEvents
| where Timestamp > ago(7d)
| where ActionType == "New-InboxRule"
| extend RuleName = tostring(RawEventData.Parameters[0].Value),
ForwardTo = tostring(RawEventData.Parameters[?(@.Name == "ForwardTo")].Value),
DeleteMessage = tostring(RawEventData.Parameters[?(@.Name == "DeleteMessage")].Value)
| where ForwardTo != "" or DeleteMessage == "True"
| project Timestamp, AccountUpn = AccountObjectId, RuleName, ForwardTo, DeleteMessage
Automated Investigation and Response
{
"automationRules": [
{
"name": "Auto-contain compromised device",
"trigger": "alertCreated",
"conditions": [
{
"property": "alertSeverity",
"operator": "equals",
"value": "high"
},
{
"property": "alertCategory",
"operator": "in",
"value": ["Malware", "Ransomware", "CredentialAccess"]
}
],
"actions": [
{
"type": "isolateDevice",
"comment": "Auto-isolated due to high-severity threat detection"
},
{
"type": "disableUser",
"comment": "Account disabled pending investigation"
},
{
"type": "collectInvestigationPackage"
},
{
"type": "assignToAnalyst",
"analystGroup": "Tier2-SOC"
}
]
}
]
}
Phase 5: Network Segmentation with Azure Private Link
Securing Application Access
// Private endpoint for Key Vault
resource keyVaultPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-09-01' = {
name: 'pe-keyvault-zerotrust'
location: resourceGroup().location
properties: {
subnet: {
id: privateEndpointSubnet.id
}
privateLinkServiceConnections: [
{
name: 'keyvault-connection'
properties: {
privateLinkServiceId: keyVault.id
groupIds: ['vault']
}
}
]
}
}
// Network Security Group with zero-trust rules
resource nsgZeroTrust 'Microsoft.Network/networkSecurityGroups@2023-09-01' = {
name: 'nsg-zerotrust-workloads'
location: resourceGroup().location
properties: {
securityRules: [
{
name: 'DenyAllInbound'
properties: {
priority: 4096
direction: 'Inbound'
access: 'Deny'
protocol: '*'
sourceAddressPrefix: '*'
destinationAddressPrefix: '*'
sourcePortRange: '*'
destinationPortRange: '*'
}
}
{
name: 'AllowAppGatewayInbound'
properties: {
priority: 100
direction: 'Inbound'
access: 'Allow'
protocol: 'Tcp'
sourceAddressPrefix: 'GatewayManager'
destinationAddressPrefix: '*'
sourcePortRange: '*'
destinationPortRange: '443'
}
}
{
name: 'AllowAzureLoadBalancer'
properties: {
priority: 110
direction: 'Inbound'
access: 'Allow'
protocol: '*'
sourceAddressPrefix: 'AzureLoadBalancer'
destinationAddressPrefix: '*'
sourcePortRange: '*'
destinationPortRange: '*'
}
}
]
}
}
Phase 6: Microsoft Sentinel SIEM Integration
Centralized Security Monitoring
// Zero Trust health score dashboard
let identityScore = toscalar(
SigninLogs | where TimeGenerated > ago(24h)
| summarize MFARate = 1.0 * countif(AuthenticationRequirement == "multiFactorAuthentication") / count()
);
let deviceScore = toscalar(
IntuneDeviceComplianceOrg | where TimeGenerated > ago(24h)
| summarize ComplianceRate = 1.0 * countif(ComplianceState == "compliant") / count()
);
let networkScore = toscalar(
AzureNetworkAnalytics_CL | where TimeGenerated > ago(24h)
| summarize BlockedRate = 1.0 * countif(FlowStatus_s == "D") / count()
);
print IdentityScore = round(identityScore * 100, 1),
DeviceScore = round(deviceScore * 100, 1),
NetworkScore = round((1 - networkScore) * 100, 1),
OverallZeroTrustScore = round((identityScore + deviceScore + (1 - networkScore)) / 3 * 100, 1)
Zero Trust Maturity Assessment
| Pillar | Level 1 (Traditional) | Level 2 (Advanced) | Level 3 (Optimal) |
|---|---|---|---|
| Identity | Password + SMS MFA | FIDO2 + Risk-based CA | Passwordless + Continuous evaluation |
| Devices | Antivirus only | Compliance policies | Real-time risk assessment |
| Network | VPN perimeter | Micro-segmentation | Identity-aware proxy |
| Apps | On-prem only | Cloud w/ SSO | CASB + App protection |
| Data | No classification | Sensitivity labels | Auto-classification + DLP |
| Monitoring | Basic logs | SIEM correlation | AI-driven threat hunting |
Architecture Decision and Tradeoffs
When designing integrated solutions solutions with Azure + Power Platform, consider these key architectural trade-offs:
| Approach | Best For | Tradeoff |
|---|---|---|
| Managed / platform service | Rapid delivery, reduced ops burden | Less customisation, potential vendor lock-in |
| Custom / self-hosted | Full control, advanced tuning | Higher operational overhead and cost |
Recommendation: Start with the managed approach for most workloads and move to custom only when specific requirements demand it.
Validation and Versioning
- Last validated: April 2026
- Validate examples against your tenant, region, and SKU constraints before production rollout.
- Keep module, CLI, and SDK versions pinned in automation pipelines and review quarterly.
Security and Governance Considerations
- Apply least-privilege access using RBAC roles and just-in-time elevation for admin tasks.
- Store secrets in managed secret stores and avoid embedding credentials in scripts or source files.
- Enable audit logging, data protection policies, and periodic access reviews for regulated workloads.
Cost and Performance Notes
- Define budgets and alerts, then monitor usage and cost trends continuously after go-live.
- Baseline performance with synthetic and real-user checks before and after major changes.
- Scale resources with measured thresholds and revisit sizing after usage pattern changes.
Official Microsoft References
- https://learn.microsoft.com/azure/architecture/
- https://learn.microsoft.com/azure/well-architected/
- https://learn.microsoft.com/power-platform/guidance/
Public Examples from Official Sources
- These examples are sourced from official public Microsoft documentation and sample repositories.
- Documentation examples: https://learn.microsoft.com/azure/well-architected/
- Sample repositories: https://github.com/Azure/ArchitectureCenter
- Prefer adapting these examples to your tenant, subscriptions, and governance requirements before production use.
Key Takeaways
- Zero Trust is a journey, not a destination — start with identity and expand systematically
- Conditional Access policies are the policy engine of Zero Trust in Microsoft's ecosystem
- Device compliance combined with Conditional Access ensures only healthy devices access resources
- Microsoft Defender XDR provides cross-workload detection and automated response
- Network segmentation with Private Link eliminates public endpoint exposure
- Continuous monitoring through Sentinel ties all pillars together for unified security posture
Discussion