Earlier this year I sat down and took apart the published Conditional Access baselines the way I’d audit a customer tenant. Every policy definition at the JSON level, every group reference resolved, every grant control checked against what it actually does rather than what its name says. I did this because I was about to standardize my own practice on one of them, and I wanted to know what I was standing on.
What I found changed my mind about what a baseline is.
The policies were mostly fine. The community has largely converged on the right controls: block legacy authentication, block device code flow, require MFA everywhere, put device trust behind the important things. The persona idea that runs through this generation of baselines, with different policy families for admins, users, and guests, is sound, and I’ve kept it. If Conditional Access were a policy problem, the problem would be solved.
It isn’t a policy problem. Every baseline I examined left the same five things as exercises for whoever deploys it: what happens when someone needs an exception, how a brand-new user with no MFA methods gets their first credential safely, what a personal laptop is actually allowed to do, what to do with service accounts that can’t answer an MFA prompt, and who looks at any of it three months later. Those five gaps are not edge cases. They are where tenants get compromised and where deployments quietly rot. A folder of policy JSON, however well designed, doesn’t touch any of them.
So the framework I built starts from a different premise. A Conditional Access baseline is an operating model, not a set of JSON files. The policies are maybe a third of it. The rest is the exception governance, the onboarding method, the deployment parameters, and the review cadence, shipped as part of the product instead of left as advice.
Credit where it belongs before I describe the departures. The persona structure descends from Claus Jespersen’s Conditional Access for Zero Trust work. Joey Verlinden’s ConditionalAccessBaseline proved that a persona framework could be packaged and imported by ordinary mortals. The modular discipline will be familiar to anyone who has deployed OpenIntuneBaseline. This framework stands on all three. It also disagrees with them in specific, deliberate ways.
Two axes instead of one
Personas answer who: privileged accounts, employees, external users, non-interactive identities, and (new this generation) agents. That part is inherited. What personas can’t answer is what, and this is where every packaged baseline hits the same wall. It cannot know which applications your customer runs, so it either targets everything or ships placeholder app lists with a note telling you to figure it out.
The framework’s answer is resource tiers. Tier 0 is the control plane: Azure management, the admin portals, and whatever the customer adds. Access to it requires phishing-resistant MFA plus a compliant device, for every identity, with no exceptions. Not just for admins. A Tier 0 policy scoped to all users means a standard account that somehow acquires ARM access still meets the control-plane bar. Tier 1 is business data, reachable from managed devices or through a contained browser session. Tier 2 is everything else, behind MFA. Classifying a customer’s apps into tiers is a one-time conversation during deployment. The policies never change afterward, which converts “the baseline can’t know your apps” from a design flaw into a worksheet row.
Some applications need more than their tier default. The CRM, the finance system, the HR platform. For those there are four instantiable per-app templates: deny unmanaged devices outright, step up with device-or-MFA, fence to known egress, or require a containerized mobile client. Each is a two-field edit and a named exclusion group, not an improvisation. Per-app policy sprawl is how careful tenants become archaeology sites, and the templates exist so it never starts.
The policy map
| Block | Persona / scope | What lives there |
|---|---|---|
| 000 | Everyone | Foundation: legacy auth and device-code blocks, the MFA catch-all, registration protection, platform hygiene |
| 100 | Privileged + Tier 0 | Phishing-resistant MFA, admin device trust, session limits, the control-plane policy |
| 200 | Managed users | Device compliance, enrollment MFA, mobile MAM, session hygiene |
| 300 | BYOD | The contained browser path |
| 400 | Externals | Guest MFA, default-deny apps, session limits, portal block |
| 500 | Non-interactive | IP fencing, app restriction, and deliberately no MFA theater |
| 600 | Agents | Default-deny and risk blocks, report-only until an inventory exists |
| 700 | P2 overlay | Risk-based enforcement scoped to all users, token protection pilot |
| 800 | Templates | The four per-app postures |
Twenty-seven policies in the core edition, which runs on Entra ID P1. Business Premium customers are first-class citizens here, not an afterthought. Six more policies sit in a P2 overlay folder, because risk-based enforcement is a licensing decision and the packaging should respect that. The overlay’s risk policies cover all users, not just employees. The identities most worth blocking on a risk signal are precisely the admins and guests that user-persona risk policies tend to miss.
Fewer, stronger primitives
Two grant controls do most of the enforcement: authentication strength and device trust. I use authentication strengths only where they constrain methods, which means phishing-resistant on the privileged and Tier 0 blocks, and the classic MFA control everywhere any second factor will do. A strength that just means “any MFA” adds interop friction with external methods and B2B trust while buying nothing. Reserving strengths for actual constraints also keeps the policy set legible: when you see a strength, a method restriction applies.
Device trust is expressed as compliance, not ownership. A personally-owned device the customer permits to enroll, once compliant, is a managed device. Whether it may enroll at all is an Intune enrollment-restrictions decision from Phase 3, not a Conditional Access one. And policies target resources, never client applications, because clients lie about what they are and resources don’t.
Hybrid join is handled honestly. The default requires compliance, and a clearly labeled transition variant accepts hybrid join for the customers who need the stop-gap, with the expectation, written down, that it has an exit date.
Exceptions are the design, not the failure of it
Every finding from my baseline research traced back to an exception path nobody designed. So in this framework every policy ships with exactly one dedicated exclusion group: empty, with a named owner, a quarterly access review, and membership-change alerting. No exclusion group is ever referenced by two policies. That last rule matters more than it looks. There is a chaining pattern in the wild where the global MFA policy excludes the exclusion groups of other policies, which means one group membership can silently remove MFA everywhere. It reads as tidy design and it is an attacker’s favorite object in the tenant. The validator that gates every release of this framework checks for it, along with inert policy scopes, dangling group references, and missing break-glass exclusions. Each check corresponds to a real defect I found in the wild.
Break-glass follows the same philosophy. The framework ships the group, excluded from everything, and states the standard: two cloud-only accounts, phishing-resistant credentials, sign-in alerting, a quarterly test. It does not prescribe account names and it does not assume compliance. The prerequisite script verifies membership at exactly one moment, the gate before the first policies enforce, because that is the moment assumption stops being acceptable.
The lifecycle is a policy family
The most consequential unprotected event in most tenants is a user registering their first MFA method. The second is a device enrolling, because a compliant device is itself a credential from that day forward. The framework treats both as first-class scenarios. Security-info registration sits behind MFA. Enrollment sits behind MFA with a fresh challenge every time. The Temporary Access Pass is the spine that makes both livable: issued at onboarding, satisfying the registration and enrollment policies in one motion, and bootstrapping Windows Hello so the user lands passwordless on day one. Bulk provisioning has its own answers in pre-provisioning and self-deploying mode. The point is that onboarding was designed, not discovered by the help desk.
The unmanaged path was designed too. A personal Windows or macOS machine gets a contained experience: browser only, MFA, limited sessions, no persistence, and on Windows an Edge session governed by MAM. The alternatives are a hard wall that drives people into exclusion groups, or an unexamined openness that makes device trust meaningless. A designed degraded experience beats an undesigned bypass every time. The customers who want the hard wall get it as a documented variant rather than a redesign.
What actually ships
The repository holds the policy JSON in IntuneManagement-importable form, the groups and named locations, companion App Protection baselines for iOS, Android, and Windows with their assignment filters, a prerequisites script that creates what the import tool can’t and verifies what it shouldn’t assume, and the release validator in both PowerShell and Python. Four documents are the real product: an implementation guide with the expected output of every step, a deployment-parameters worksheet with no functional defaults, the onboarding model, and the operating runbook with its four-ring enablement order.
Nothing enforces on import day. Everything tenant-specific is a required parameter. There is no country list or IP range in the package that could lock out a tenant that imported it blind, a failure mode I have watched happen with baselines that ship someone else’s geography as a default.
Releases are date-versioned, and the first one, 2026.7.1, was validated end to end in a live tenant before it earned the number. That validation surfaced failures, and they are documented in the changelog rather than smoothed over. Some of what’s in the field notes exists nowhere else that I’ve found, like which object types the import tooling silently cannot carry, and the platform-specific App Protection behavior that only reveals itself when you actually deploy into a tenant.
The next article walks the build, from a bare admin workstation to a tenant with a green prerequisite gate, including everything that went sideways the first time. The sideways parts are the ones you’ll actually hit.
Repo: github.com/VirtualCaffeineIO/vcio-ca-framework · Continue with [CA 4.1] Building the Framework: From Zip to Green Gate.
Conditional Access
‹ Previous: [CA 3] Named Locations, Risk Policies, and Identity Protection
Next: [CA 4.1] Building the Framework: From Zip to Green Gate ›




