In this module
MSA0.1 What Security Architecture Actually Is
You've configured M365 security features — MFA, Conditional Access, DLP, Defender policies. You can navigate the Entra admin center and the Microsoft 365 security portals. This sub draws the line between configuring individual features and designing an M365 security architecture — and makes that line concrete with real tenant artifacts, Graph API output, and sign-in logs that show exactly what the gap looks like in production.
What security architecture means — and why configuration alone fails
Your tenant has Conditional Access policies, MFA enforced, DLP rules active, and a Secure Score that looks respectable. But when the auditor asks why push notification MFA instead of passkeys, when a new hire inherits the tenant and asks what depends on what, when an AiTM phishing campaign bypasses MFA and leadership asks how that was possible — nobody can answer from the configuration alone.
Security architecture is the documented reasoning behind your tenant's configuration — the decisions, the dependencies between decisions, the trade-offs accepted, and the risks knowingly left open. Configuration lives in the portal. Architecture lives in documentation that explains why the portal looks the way it does. Most tenants have configuration. Most tenants lack architecture. This sub makes that gap concrete by pulling real artifacts from an M365 tenant and showing you exactly where the reasoning lives, where it's missing, and what breaks when it's absent. By the end, you'll have three diagnostic questions you'll apply to every security setting for the rest of the course.
Estimated time: 30 minutes.
Figure MSA0.1 — Configuration returns the what. Architecture documents the why, the dependencies, and the accepted risk. The gap between them is where incidents become crises.
Security architecture is not a list of settings
Every M365 tenant has settings. Conditional Access policies, MFA configurations, DLP rules, Defender policies — the admin center is full of them. The question this course answers isn't how to enable those settings. Microsoft Learn, Pluralsight, and every SC-300 prep course cover that. The question is: why did you choose these settings, what happens when one of them breaks, and what are you knowingly leaving unprotected?
That's the difference between configuration and architecture. Configuration is the state of the tenant — what's enabled, what's disabled, what values are set. Architecture is the reasoning behind that state — the decisions, the dependencies between decisions, the trade-offs accepted, and the risks documented. Configuration lives in the portal. Architecture lives in documentation, in the architect's head, or — in most tenants — nowhere at all.
The consequence of that gap shows up in three predictable situations. When someone new inherits the tenant, they can see every setting but can't answer "why" for any of them. When an auditor asks for evidence that controls are deliberately configured, the admin can show the portal but can't produce the reasoning. And when an incident happens — when an AiTM attack bypasses MFA, when a consent phishing grant gives an attacker persistent access — leadership asks how that was possible, and the answer is that the controls were configured to allow it, but nobody documented the decision or the residual risk.
This sub makes that gap visible by pulling real artifacts from an M365 tenant. You'll see exactly what the Graph API returns for a CA policy, what it doesn't return, and where the architecture needs to live to close the gap.
What the Graph API shows you — and what it can't
Open any M365 tenant with Conditional Access policies. This PowerShell command queries every CA policy through the Microsoft Graph. You need the Microsoft.Graph.Identity.SignIns module and Policy.Read.All permission:
Connect-MgGraph -Scopes "Policy.Read.All"
Get-MgIdentityConditionalAccessPolicy | Select-Object DisplayName, State, IdHere's what comes back from a typical tenant — one that looks healthy in the portal:
DisplayName State Id
----------- ----- --
Require MFA - All Users enabled 7a2e8f31-...
Block Legacy Authentication enabled 4c91b2a7-...
Require Compliant Device - Corp Apps enabled e3f7d924-...
Require MFA - Admin Roles enabled 1b8c4e56-...
Block High Risk Sign-ins enabled 9d2a7f13-...
Guest Access - Restricted Apps Only enabled f5e1c8b2-...
Require App Protection - Mobile enabled 2a9d6e47-...
Session Timeout - Unmanaged Devices enabled 8c3b1f95-...
Block Countries - Except Allowed enabled d7e4a2c1-...
Require Phishing-Resistant MFA - Admins enabled 5f8b3d72-...
Require Terms of Use - External enabled a1c9e4f6-...
MFA Registration - Trusted Locations Only enabled 3e7d2b89-...
Block Non-Compliant Devices - Exchange enabled 6b4f8a15-...
Require MFA - Risky Sign-ins enabled c2d5e7a3-...Fourteen policies. All enabled. The admin who configured these would say the tenant is secured. Now pull the full JSON on the first policy. This is the actual response structure from the Graph v1.0 API — every field Microsoft returns:
$policy = Get-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId "7a2e8f31-..."
$policy | ConvertTo-Json -Depth 5{
"id": "7a2e8f31-b4c9-4d2a-8e15-3f6a9c7b1d84",
"templateId": null,
"displayName": "Require MFA - All Users",
"createdDateTime": "2024-09-15T10:22:41Z",
"modifiedDateTime": "2025-01-03T14:18:09Z",
"state": "enabled",
"conditions": {
"userRiskLevels": [],
"signInRiskLevels": [],
"clientAppTypes": ["all"],
"servicePrincipalRiskLevels": [],
"insiderRiskLevels": null,
"platforms": null,
"locations": null,
"devices": null,
"clientApplications": null,
"applications": {
"includeApplications": ["All"],
"excludeApplications": [],
"includeUserActions": [],
"includeAuthenticationContextClassReferences": [],
"applicationFilter": null
},
"users": {
"includeUsers": ["All"],
"excludeUsers": [],
"includeGroups": [],
"excludeGroups": ["8f3a2b91-c4d7-4e8f-b156-7a9e3d2c1f84"],
"includeRoles": [],
"excludeRoles": [],
"includeGuestsOrExternalUsers": null,
"excludeGuestsOrExternalUsers": null
}
},
"grantControls": {
"operator": "OR",
"builtInControls": ["mfa"],
"customAuthenticationFactors": [],
"termsOfUse": [],
"authenticationStrength": null
},
"sessionControls": null
}Read this carefully. Every field tells you something about the state of architecture in this tenant.
signInRiskLevels: [] — the policy doesn't condition on sign-in risk. Even if Identity Protection detects a high-risk sign-in (impossible travel, anonymous IP, known attacker infrastructure), this policy doesn't care. It requires MFA regardless of risk level. That's not necessarily wrong — but is it deliberate? Did the architect decide that risk-based differentiation wasn't needed for this policy because a separate policy handles risk? Or did nobody configure the risk condition because they didn't know it existed?
insiderRiskLevels: null — Insider Risk signals aren't consumed by any CA policy. This is an E5 feature that connects Purview's Insider Risk Management to Conditional Access. If you have E5 and this is null across all policies, it means a signal chain from Layer 2 (data protection) to Layer 1 (access control) doesn't exist. Insider Risk may flag a user as elevated risk, but no CA policy acts on it.
authenticationStrength: null — the policy uses builtInControls: ["mfa"], which means any registered MFA method satisfies the requirement. Push notification, phone call, SMS, TOTP, passkey, FIDO2 key — all treated identically. The policy can't distinguish between a phishing-resistant method that binds to the TLS session and a push notification that an AiTM proxy captures after the user approves it.
excludeGroups: ["8f3a2b91-..."] — one group is excluded. Who's in it? Why?
Every exclusion tells a story — most are undocumented
The JSON above reveals an exclusion group. In architecture, every exclusion is a decision. In configuration, most exclusions are accidents that nobody revisited. The difference matters because exclusions are the first thing an attacker looks for — they represent identities with weaker controls. Let's trace this one and see which it is.
Get-MgGroup -GroupId "8f3a2b91-c4d7-4e8f-b156-7a9e3d2c1f84" |
Select-Object DisplayName, Description, CreatedDateTimeDisplayName Description CreatedDateTime
----------- ----------- ---------------
CA-Exclude-MFA (blank) 2024-09-15T10:18:33ZNo description. Created the same day as the CA policy — probably created specifically for this exclusion. The group name tells you it's an MFA exclusion group but nothing about why it exists, who approved it, or when it should be reviewed.
Get-MgGroupMember -GroupId "8f3a2b91-c4d7-4e8f-b156-7a9e3d2c1f84" -All |
ForEach-Object {
$user = Get-MgUser -UserId $_.Id -Property DisplayName, UserPrincipalName, JobTitle
[PSCustomObject]@{
UPN = $user.UserPrincipalName
Name = $user.DisplayName
JobTitle = $user.JobTitle
}
}UPN Name JobTitle
--- ---- --------
svc-crm-sync@yourtenant.onmicrosoft.com CRM Sync Service (blank)
svc-erp-integration@yourtenant.onmicrosoft.com ERP Integration (blank)
admin-user@yourtenant.onmicrosoft.com the IT Director IT Director
boardroom-display@yourtenant.onmicrosoft.com Boardroom Display (blank)
svc-backup-exec@yourtenant.onmicrosoft.com Backup Service (blank)Five identities bypassing MFA. Two service accounts for line-of-business integrations — if those accounts authenticate using OAuth client credentials flow (application-only, no interactive sign-in), MFA doesn't apply to them anyway and the exclusion is unnecessary. That's something you'd know with architecture and guess without it.
One is the IT Director, the IT Director. No documented reason. Check what roles he holds:
Get-MgUserMemberOf -UserId "admin-user@yourtenant.onmicrosoft.com" |
Where-Object { $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.directoryRole' } |
ForEach-Object { $_.AdditionalProperties.displayName }Global Administrator
Exchange Administrator
SharePoint AdministratorThree admin roles, including Global Administrator — the highest-privilege role in the tenant. An IT Director with GA, excluded from MFA, with no documented reason. In an AiTM phishing campaign, this is the identity the attacker targets first. The password gets them everything.
One is a shared device account for a boardroom display. Shared devices can't do interactive MFA — but that's a solvable problem with device compliance requirements or Conditional Access session controls. Is the exclusion a technical necessity or a shortcut nobody revisited?
One is a backup service account. If this account runs as an application with a client secret or certificate, it doesn't need an MFA exclusion — it authenticates via the client credentials flow, which CA policies targeting "All users" don't evaluate. The exclusion is either redundant or an indication that someone configured the account to authenticate interactively, which is itself an architectural problem.
None of this is visible from the CA policy JSON alone. The JSON tells you the what. The investigation above tells you the why is missing. That's the gap.
What architecture adds to the same tenant
The same tenant, with architecture, answers three questions for every exclusion, every policy, every significant setting. These aren't abstract principles — they're the questions that separate a tenant an auditor trusts from a tenant an auditor flags. You'll answer them in every ADR you write from MSA1 onward. The artifacts below show you what the answers look like when they're grounded in the same API output you just read.
Why this and not something else? The "Require MFA — All Users" policy uses builtInControls: ["mfa"] with authenticationStrength: null. That means any MFA method satisfies the policy — including push notification MFA, which doesn't prevent AiTM token theft. An architected tenant differentiates by using authentication strength policies. Here's what the grantControls object looks like when authentication strength is configured:
{
"operator": "OR",
"builtInControls": [],
"customAuthenticationFactors": [],
"termsOfUse": [],
"authenticationStrength": {
"id": "00000000-0000-0000-0000-000000000004",
"createdDateTime": "2021-12-01T08:00:00Z",
"modifiedDateTime": "2021-12-01T08:00:00Z",
"displayName": "Phishing-resistant MFA",
"description": "Phishing-resistant, passwordless methods",
"policyType": "builtIn",
"requirementsSatisfied": "mfa",
"allowedCombinations": [
"windowsHelloForBusiness",
"fido2",
"x509CertificateMultiFactor"
]
}
}That's the built-in "Phishing-resistant MFA" policy (ID 00000000-0000-0000-0000-000000000004). It only accepts Windows Hello for Business, FIDO2 security keys, or X.509 certificate-based MFA. Push notifications, phone calls, SMS, and TOTP are all rejected. The allowedCombinations array is the mechanism — it enumerates exactly which authentication method combinations satisfy the requirement.
The difference between builtInControls: ["mfa"] and authenticationStrength: {"displayName": "Phishing-resistant MFA"} is one JSON field. The difference in security posture is whether an AiTM proxy can steal a session token after MFA completion. Microsoft documented three built-in authentication strength policies: MFA (00000000-0000-0000-0000-000000000002), Passwordless MFA (00000000-0000-0000-0000-000000000003), and Phishing-resistant MFA (00000000-0000-0000-0000-000000000004). You can also create custom policies with up to 15 additional combinations. The Graph API output is identical in structure either way. Only the reasoning behind the choice — documented in an ADR — explains why one is correct for this environment.
What depends on this? Pull the "Require Compliant Device" policy and read the conditions:
{
"displayName": "Require Compliant Device - Corp Apps",
"conditions": {
"users": { "includeUsers": ["All"] },
"applications": {
"includeApplications": [
"00000002-0000-0ff1-ce00-000000000000",
"00000003-0000-0ff1-ce00-000000000000"
]
}
},
"grantControls": {
"operator": "OR",
"builtInControls": ["compliantDevice"]
}
}The two application GUIDs are Exchange Online (00000002-0000-0ff1-ce00-000000000000) and SharePoint Online (00000003-0000-0ff1-ce00-000000000000). These are well-known service principal IDs that Microsoft publishes — you'll see them frequently in CA policy configurations. The compliantDevice grant control requires that Intune has evaluated the device and marked it as meeting the compliance policy.
That compliance signal is a dependency. If Intune compliance policies aren't configured, or if they're configured but set to mark devices as compliant by default during a grace period that nobody shortened, the compliantDevice control evaluates against a meaningless baseline. The CA policy looks enforced. The compliance signal is empty.
Test whether the upstream signal exists:
Connect-MgGraph -Scopes "DeviceManagementConfiguration.Read.All"
Get-MgDeviceManagementDeviceCompliancePolicy |
Select-Object DisplayName,
@{N='Platform';E={
$odata = $_.AdditionalProperties.'@odata.type'
switch -Wildcard ($odata) {
'*windows10*' { 'Windows' }
'*ios*' { 'iOS' }
'*android*' { 'Android' }
'*macOS*' { 'macOS' }
default { $odata }
}
}}If that returns nothing, the "Require Compliant Device" CA policy is enforcing a signal that doesn't exist. Architecture maps these signal chains explicitly — which policy consumes which signal, from which source, and what happens if the source breaks.
What risk do we accept? Go back to the MFA exclusion group. An architected tenant documents each exception in an ADR:
ADR-MSA2-EX-001: svc-crm-sync@yourtenant.onmicrosoft.com
Reason: CRM vendor (Dynamics integration) authenticates via OAuth
client credentials flow — no interactive sign-in. MFA not
applicable. Exclusion is technically redundant but retained
as defense-in-depth against future configuration drift.
Compensating control: App registration scoped to Dynamics CRM API
(api://dynamics.yourtenant.onmicrosoft.com) only.
Client secret expiry: 90 days. Secret
rotation alert in Sentinel.
Review date: 2026-06-01
Approved by: the CISO (CISO), 2026-01-15
ADR-MSA2-EX-003: admin-user@yourtenant.onmicrosoft.com
Reason: NONE DOCUMENTED.
Status: EXCEPTION WITHOUT DOCUMENTATION.
Risk: IT Director account holds Global Administrator, Exchange
Administrator, and SharePoint Administrator roles. Bypasses
MFA. AiTM phishing against this identity = full tenant
compromise within minutes.
Action required: Immediate remediation — remove from exclusion
group, enrol in phishing-resistant MFA, assign
GA via PIM (eligible, not permanent).The first entry is architecture — a documented exception with reasoning, a compensating control, a review date, and an approval. The third entry is the gap that kills you in an incident. An IT Director with three admin roles, no MFA, and no documentation.
Reading architecture from the sign-in log
The sign-in log is the ultimate evidence of whether architecture exists. Every field records what actually happened during authentication — which policies evaluated, which controls enforced, which device was used, which risk signals were present. Two sign-in events for the same user, to the same application, from the same IP, can look completely different depending on whether the tenant has architecture or just configuration.
Here's a sign-in log entry from the Entra ID audit log. This is the Graph v1.0 signIn resource — what Entra records every time someone authenticates. The full set of fields from the API:
{
"id": "66ea54eb-6301-4ee5-be62-ff5a759b0100",
"createdDateTime": "2026-04-15T09:23:41Z",
"userDisplayName": "Admin User",
"userPrincipalName": "admin-user@yourtenant.onmicrosoft.com",
"userId": "a3d8f2c1-b4e7-4a91-8c56-2d9e7f3b1a04",
"appId": "00000006-0000-0ff1-ce00-000000000000",
"appDisplayName": "Microsoft Office 365 Portal",
"ipAddress": "198.51.100.47",
"clientAppUsed": "Browser",
"correlationId": "d79f5bee-5860-4832-928f-3133e22ae912",
"conditionalAccessStatus": "notApplied",
"isInteractive": true,
"riskDetail": "none",
"riskLevelAggregated": "none",
"riskLevelDuringSignIn": "none",
"riskState": "none",
"riskEventTypes": [],
"resourceDisplayName": "Microsoft Office 365",
"resourceId": "00000006-0000-0ff1-ce00-000000000000",
"status": {
"errorCode": 0,
"failureReason": null,
"additionalDetails": null
},
"deviceDetail": {
"deviceId": "",
"displayName": null,
"operatingSystem": "Windows 10",
"browser": "Edge 124.0",
"isCompliant": null,
"isManaged": null,
"trustType": ""
},
"location": {
"city": "London",
"state": "England",
"countryOrRegion": "GB",
"geoCoordinates": {
"latitude": 51.5074,
"longitude": -0.1278
}
},
"appliedConditionalAccessPolicies": [],
"authenticationDetails": [
{
"authenticationMethod": "Password",
"authenticationMethodDetail": "Password in the cloud",
"succeeded": true,
"authenticationStepResultDetail": "MFA not required"
}
]
}Walk through the fields that matter for architecture.
conditionalAccessStatus: "notApplied" — no CA policy evaluated this sign-in. Phil is in the exclusion group. Every policy that applies to "All Users" skipped him. appliedConditionalAccessPolicies: [] confirms it — the array is empty.
authenticationDetails[0].authenticationMethod: "Password" with authenticationStepResultDetail: "MFA not required" — password-only authentication. No second factor. No challenge.
deviceDetail.deviceId: "" — the device isn't enrolled in Entra ID. isCompliant: null — no compliance signal exists for this device. isManaged: null — Intune has never evaluated this device. trustType: "" — not Azure AD joined, not hybrid joined, not registered.
riskLevelDuringSignIn: "none" — Identity Protection found no risk indicators. This is accurate — the sign-in is from London, Phil works in London, the IP is a known office IP. Nothing is anomalous. The problem isn't that Identity Protection failed. The problem is that even if it detected risk, appliedConditionalAccessPolicies is empty — no policy is evaluating Phil's sign-ins, so no policy can act on a risk signal.
riskLevelAggregated: "none" — no aggregated risk across Phil's recent sign-in activity. But aggregated risk only accumulates if Identity Protection is evaluating the sign-ins. For users excluded from CA policies, the risk engine still runs — but nothing acts on the output.
An IT Director with Global Administrator privileges signed in with a password, from an unmanaged device, with no MFA, no device compliance check, no conditional access evaluation. The sign-in succeeded. The audit log recorded it. Nobody was alerted because no policy required anything stronger.
Now compare it with what the same sign-in looks like in an architected tenant — where Phil has been removed from the exclusion group, enrolled in phishing-resistant MFA, and assigned GA via PIM:
{
"id": "7b2f1c84-9e03-4d6a-a5b3-8f4c2d1e7a39",
"createdDateTime": "2026-04-15T09:23:41Z",
"userDisplayName": "Admin User",
"userPrincipalName": "admin-user@yourtenant.onmicrosoft.com",
"userId": "a3d8f2c1-b4e7-4a91-8c56-2d9e7f3b1a04",
"appId": "00000006-0000-0ff1-ce00-000000000000",
"appDisplayName": "Microsoft Office 365 Portal",
"ipAddress": "198.51.100.47",
"clientAppUsed": "Browser",
"conditionalAccessStatus": "success",
"isInteractive": true,
"riskDetail": "none",
"riskLevelDuringSignIn": "none",
"riskState": "none",
"resourceDisplayName": "Microsoft Office 365",
"status": {
"errorCode": 0,
"failureReason": null,
"additionalDetails": null
},
"deviceDetail": {
"deviceId": "3f8a2c91-d4b7-4e5f-a168-9b7e2d3c1f56",
"displayName": "PHIL-LAPTOP-01",
"operatingSystem": "Windows 11",
"browser": "Edge 124.0",
"isCompliant": true,
"isManaged": true,
"trustType": "AzureAd"
},
"appliedConditionalAccessPolicies": [
{
"id": "5f8b3d72-a1c4-4e9b-b267-8d3f2a6c1e05",
"displayName": "Require Phishing-Resistant MFA - Admins",
"result": "success",
"enforcedGrantControls": ["RequireAuthenticationStrength"]
},
{
"id": "e3f7d924-b2a8-4c1f-9d56-7e4b3a2f1c83",
"displayName": "Require Compliant Device - All Apps",
"result": "success",
"enforcedGrantControls": ["CompliantDevice"]
}
],
"authenticationDetails": [
{
"authenticationMethod": "FIDO2 security key",
"authenticationMethodDetail": "FIDO2 security key",
"succeeded": true,
"authenticationStepResultDetail": "MFA requirement satisfied by strong authentication"
}
]
}Same user. Same application. Same IP. Same time. But appliedConditionalAccessPolicies now contains two entries — both evaluated and both succeeded. enforcedGrantControls shows "RequireAuthenticationStrength" (not just "Mfa" — this is the authentication strength control, which only accepts phishing-resistant methods) and "CompliantDevice". The authenticationDetails show FIDO2 security key instead of password. The deviceDetail shows an enrolled, compliant, Azure AD-joined device with a device name.
The sign-in log is the evidence that architecture exists. Not because the settings are different — but because the settings are connected, enforced, and documented.
How this course builds architecture: the four-stage cycle
Understanding the difference between configuration and architecture is the first step. The rest of this course teaches you to close the gap systematically. Every module from MSA1 onward follows the same four-stage cycle. This cycle is what makes the output architecture rather than configuration — because each stage forces reasoning, not just action.
Design. You analyze the architectural problem, including the real-world constraints that textbook solutions ignore: licensing limitations (E3 vs E5), legacy applications that break modern authentication, hybrid Active Directory with sync complications, political resistance to security controls that create user friction, service accounts that vendors refuse to update, and Copilot workloads that expose data through new vectors. You design a solution that works within those constraints — not the ideal, but the best achievable architecture for the environment.
Justify. You document the decision as an Architecture Decision Record (ADR). Context, decision, alternatives considered and rejected, consequences, residual risk, and the 30-second version you'd give your CISO. This is the step that most security implementations skip — and the reason most tenants have configuration without architecture. MSA0.3 teaches the ADR format in depth.
Implement. You build the solution in your M365 developer tenant. Portal configurations, PowerShell commands, Graph API calls. Every implementation includes a verification step — query the API, confirm the setting is active and returning the expected JSON.
Validate. You test whether the implementation achieves what the design intended. Attack simulations using Defender for Office 365's built-in tools. Conditional Access What If evaluation. Sign-in log analysis confirming enforcement. If the control doesn't stop what it's supposed to stop, you find out during validation — not during an incident.
Three questions that test any security setting
Everything you've seen above — the CA policy JSON, the exclusion group trace, the sign-in log comparison — reduces to three questions. These are the lens you'll apply to every security setting in every module from here forward. They work because they test for the three things that separate architecture from configuration: deliberate choice, mapped dependencies, and honest risk documentation.
You can evaluate whether any security setting in your M365 tenant has architecture behind it by asking these three questions. Every ADR you write answers all three. By MSA14, you'll have documented answers for every significant security decision in the stack.
Why this and not something else? Every setting represents a choice. If the reasoning was deliberate — alternatives evaluated against the threat model, constraints acknowledged, trade-offs documented — that's architecture. If the setting exists because it was the Secure Score recommendation or the vendor default, that's configuration.
What depends on this? Security controls in M365 form signal chains. Conditional Access consumes device compliance signals from Intune, risk signals from Identity Protection, authentication strength signals from registered methods, insider risk signals from Purview. If you can map the upstream and downstream dependencies without checking the portal, those dependencies are part of the architecture. If you'd need to investigate to find them, they're unmapped — and unmapped dependencies are where changes cause unexpected failures.
What risk do we accept? Every architecture has gaps. The organization running E3 can't use Identity Protection risk policies. The legacy application that can't support modern authentication gets a CA exception. The guest users who can't enrol in Intune bypass device compliance. Architecture documents these as accepted risks — with the residual risk stated, compensating controls identified, and a remediation timeline defined. Configuration leaves them invisible until they're exploited.
Run this against your own tenant now. Pick one CA policy. Pull the JSON with Get-MgIdentityConditionalAccessPolicy. For each field — the included users, the excluded groups, the grant controls, the session controls, the risk level conditions — ask the three questions. Count how many have documented, specific answers.
Before moving on, test your understanding against your own tenant. Pull a Conditional Access policy and answer the three diagnostic questions for one field — an excluded group, a grant control, a session control. If you can't answer all three from existing documentation, that's a gap you'll close across this course. Compare the two sign-in log entries for the IT Director above and identify four fields that differ between the unconfigured and architected versions — pay attention to appliedConditionalAccessPolicies, authenticationDetails, deviceDetail.isCompliant, and deviceDetail.trustType.
Reusable script — the diagnostic commands from this sub assembled for operational use:
# MSA0.1 — Tenant Architecture Diagnostic
# 1. List all CA policies and their state
Connect-MgGraph -Scopes "Policy.Read.All"
Get-MgIdentityConditionalAccessPolicy |
Select-Object DisplayName, State, CreatedDateTime, ModifiedDateTime |
Format-Table -AutoSize
# 2. Find all MFA exclusion groups and their members
Get-MgIdentityConditionalAccessPolicy |
ForEach-Object { $_.Conditions.Users.ExcludeGroups } |
Where-Object { $_ } |
Sort-Object -Unique |
ForEach-Object {
$g = Get-MgGroup -GroupId $_ -Property DisplayName, Description
$members = (Get-MgGroupMember -GroupId $_ -All).AdditionalProperties.userPrincipalName
[PSCustomObject]@{
Group = $g.DisplayName
Description = if ($g.Description) { $g.Description } else { '(NONE)' }
Members = ($members -join ", ")
}
} | Format-List
# 3. Check which policies use AuthenticationStrength vs plain MFA
Get-MgIdentityConditionalAccessPolicy |
Select-Object DisplayName,
@{N='GrantType';E={
if ($_.GrantControls.AuthenticationStrength) {
"AuthStrength: $($_.GrantControls.AuthenticationStrength.DisplayName)"
}
elseif ($_.GrantControls.BuiltInControls -contains 'mfa') { 'BasicMFA' }
elseif ($_.GrantControls.BuiltInControls -contains 'block') { 'Block' }
elseif ($_.GrantControls.BuiltInControls -contains 'compliantDevice') { 'CompliantDevice' }
else { 'Other' }
}} |
Format-Table -AutoSize
# 4. Check for risk-based CA policies
Get-MgIdentityConditionalAccessPolicy |
Where-Object {
$_.Conditions.SignInRiskLevels.Count -gt 0 -or
$_.Conditions.UserRiskLevels.Count -gt 0
} |
Select-Object DisplayName,
@{N='SignInRisk';E={$_.Conditions.SignInRiskLevels -join ","}},
@{N='UserRisk';E={$_.Conditions.UserRiskLevels -join ","}} |
Format-Table -AutoSizeFor each CA policy using BasicMFA instead of AuthStrength, note it. For each exclusion group with no description, note it. For each missing risk-based policy, note it. This inventory is your starting point — by MSA14, every entry will have documented architecture behind it.
You're reading the free modules of m365-security-architecture
The full course continues with advanced topics, production detection rules, worked investigation scenarios, and deployable artifacts.