Microsoft Graph conditionalAccessPolicy schema
On this page
Microsoft Graph conditionalAccessPolicy — schema reference
Endpoint: GET https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies
Named locations: GET .../identity/conditionalAccess/namedLocations
This is the agent's canonical vocabulary for policy review. When a user pastes JSON, parse
against this. When a user describes a portal screen, map their words onto these fields so
findings are precise.
Top-level shape
{
"id": "<guid>",
"displayName": "<policy name>",
"createdDateTime": "...",
"modifiedDateTime": "...",
"state": "enabled | disabled | enabledForReportingButNotEnforced",
"conditions": { ... },
"grantControls": { ... },
"sessionControls": { ... }
}
state values map to the portal as: enabled = On, disabled = Off,
enabledForReportingButNotEnforced = Report-only.
conditions
"conditions": {
"users": {
"includeUsers": ["All" | "None" | "GuestsOrExternalUsers" | "<guid>"],
"excludeUsers": ["<guid>"],
"includeGroups": ["<guid>"],
"excludeGroups": ["<guid>"],
"includeRoles": ["<roleTemplateId>"],
"excludeRoles": ["<roleTemplateId>"],
"includeGuestsOrExternalUsers": { "guestOrExternalUserTypes": "...", "externalTenants": {...} },
"excludeGuestsOrExternalUsers": { ... }
},
"applications": {
"includeApplications": ["All" | "None" | "Office365" | "<appId>"],
"excludeApplications": ["<appId>"],
"includeUserActions": ["urn:user:registersecurityinfo" | "urn:user:registerdevice"],
"includeAuthenticationContextClassReferences": ["c1"],
"applicationFilter": { "mode": "include|exclude", "rule": "..." }
},
"clientAppTypes": ["all" | "browser" | "mobileAppsAndDesktopClients" | "exchangeActiveSync" | "other"],
"platforms": { "includePlatforms": ["windows","macOS","iOS","android","linux","all"], "excludePlatforms": [...] },
"locations": { "includeLocations": ["All" | "AllTrusted" | "<guid>"], "excludeLocations": ["AllTrusted" | "<guid>"] },
"devices": { "deviceFilter": { "mode": "include|exclude", "rule": "device.isCompliant -eq True" } },
"signInRiskLevels": ["low","medium","high","none"],
"userRiskLevels": ["low","medium","high","none"],
"servicePrincipalRiskLevels": ["low","medium","high","none"],
"authenticationFlows": { "transferMethods": "deviceCodeFlow,authenticationTransfer" },
"insiderRiskLevels": "minor | moderate | elevated"
}
Notes that matter in review:
includeUsers: ["All"]includes guests and service accounts. It does not include
service principals — those need workload identity CA.
includeRolesuses role template IDs, which are stable, not role assignment IDs.- In a PIM tenant,
includeRolesmatches only while the role is activated. Eligible-
but-inactive admins are not in scope. This surprises almost everyone.
clientAppTypescontainingexchangeActiveSyncandotheris the legacy
authentication signature . One without the other is an incomplete block.
authenticationFlows.transferMethodsis a comma-delimited string, not an array.includeApplications: ["Office365"]is a bundle, not the same as"All". An MFA policy
scoped to Office365 still leaves admin portals and the Azure Service Management API
reachable — the benchmark guidance concern.
grantControls
"grantControls": {
"operator": "AND | OR",
"builtInControls": ["block","mfa","compliantDevice","domainJoinedDevice",
"approvedApplication","compliantApplication","passwordChange","unknownFutureValue"],
"customAuthenticationFactors": [],
"termsOfUse": ["<guid>"],
"authenticationStrength": { "id": "<guid>", "displayName": "...", "allowedCombinations": [...] }
}
operator is the highest-value field in the entire schema.
AND= Require all the selected controlsOR= Require one of the selected controls
["mfa","compliantDevice"] with operator: "OR" means **a compliant device satisfies the
policy without any MFA**. This is the benchmark guidance warning and the most common serious
misconfiguration the agent will encounter. The correct construction is two policies, which
combine as AND across policies.
block is exclusive — it cannot be meaningfully combined with grant controls.
Authentication strength combination values include fido2, windowsHelloForBusiness,
x509CertificateMultiFactor, password,microsoftAuthenticatorPush, temporaryAccessPassOneTime
and others. Phishing-resistant is the built-in strength whose allowed combinations are
exactly windowsHelloForBusiness, fido2, x509CertificateMultiFactor .
Any valid authenticationStrength.id also satisfies a plain MFA requirement — strengths
are strictly stronger.
sessionControls
"sessionControls": {
"applicationEnforcedRestrictions": { "isEnabled": true },
"cloudAppSecurity": { "isEnabled": true, "cloudAppSecurityType": "monitorOnly|blockDownloads|mcasConfigured" },
"signInFrequency": {
"isEnabled": true,
"type": "days | hours",
"value": 7,
"authenticationType": "primaryAndSecondaryAuthentication | secondaryAuthentication",
"frequencyInterval": "timeBased | everyTime"
},
"persistentBrowser": { "isEnabled": true, "mode": "always | never" },
"continuousAccessEvaluation": { "mode": "strictLocation | disabled" },
"secureSignInSession": { "isEnabled": true },
"disableResilienceDefaults": false
}
secureSignInSessionis token protection. Native apps only — no browser support
.
signInFrequency.frequencyInterval: "everyTime"is honoured with a five-minute clock
skew tolerance.
disableResilienceDefaults: trueremoves the backstop that keeps users signed in during
an Entra outage. Rarely intended; flag it.
cloudAppSecurityrequires Defender for Cloud Apps.
namedLocations
Two types, distinguished by @odata.type:
// microsoft.graph.ipNamedLocation
{ "displayName": "...", "isTrusted": true, "ipRanges": [ { "@odata.type": "#microsoft.graph.iPv4CidrRange", "cidrAddress": "203.0.113.0/24" } ] }
// microsoft.graph.countryNamedLocation
{ "displayName": "...", "countriesAndRegions": ["ZA","GB"], "includeUnknownCountriesAndRegions": false,
"countryLookupMethod": "clientIpAddress | authenticatorAppGps" }
Only IP named locations can be trusted. countryNamedLocation has no isTrusted
property. This is why country-based designs cannot use the All trusted locations option
.
Review heuristics keyed to the schema
| Pattern in JSON | What to say |
|---|---|
excludeUsers empty and excludeGroups empty on an enforcing policy | No break-glass exclusion → Critical |
operator: "OR" with mfa plus a device control | Compliant device bypasses MFA → Critical |
includeApplications not ["All"] on an MFA policy | Admin portals reachable → High (benchmark guidance/2) |
state: "enabled" with no evidence of a report-only period | Unmeasured blast radius → High |
signInRiskLevels or userRiskLevels populated | Requires P2 across the scoped population |
clientAppTypes has exchangeActiveSync without other, or vice versa | Incomplete legacy block → Medium |
secureSignInSession enabled with clientAppTypes including browser | Unsupported on browsers → Medium |
disableResilienceDefaults: true | Outage backstop removed → Medium |
excludeGroups referencing a group of unknown type | Ask whether it is role-assignable → potentially Critical |
Caveat
This schema evolves — Microsoft adds conditions and controls regularly, and preview
properties appear on beta before v1.0. Treat an unrecognised property as *newer than
this file*, not as invalid, and say so rather than telling the user their export is wrong.
[VERIFY] against the current conditionalAccessPolicy resource documentation when a
field is decisive to a finding.