Skip to main content

Setup and Compliance

This page shows how to classify directory roles and enforce a target PIM policy. For the concepts and the on/off switch, see the Overview.

The fastest way in: open the EAM Role Catalog, use a Copy AccessModel JSON button to get a ready-made file, drop it in AccessModel/, and scan. The rest of this page explains what that file contains so you can adjust it.

Worked example: Global Administrator

This walks one role end to end.

1. Read the catalog row. Global Administrator sits on the Control plane, at the Privileged level. The catalog recommends: 1 hour activation, MFA, approval, justification, and a phishing-resistant auth context. Control-plane roles get High severity.

2. Put it in an access-model file. Create AccessModel/ControlPlane.json:

{
"name": "Control Plane - Identity Infrastructure",
"description": "Roles with direct tenant-admin impact.",
"severity": "High",
"roles": [
{ "id": "62e90394-69f5-4237-9190-012177145e10", "displayName": "Global Administrator" }
],
"expectedConfig": {
"maxActivationDuration": "PT1H",
"requireMFA": true,
"requireApproval": true,
"requireJustification": true,
"allowPermanentEligible": false,
"allowPermanentActive": false
}
}

3. Scan. If the live policy matches, nothing happens. If, say, approval is switched off in Entra, the next scan reports a High compliance violation:

[High] Global Administrator (directory-roles)
requireApproval expected: true actual: false

4. Respond. Either restore approval in Entra, or, if the change is intentional and temporary, suppress it.

That is the whole loop. Everything below is detail on step 2.

File format

One file per group of roles, usually one per plane.

{
"name": "Control Plane - Identity Infrastructure",
"severity": "High",
"roles": [
{ "id": "62e90394-69f5-4237-9190-012177145e10", "displayName": "Global Administrator" }
],
"expectedConfig": { "...": "..." }
}
FieldRequiredDescription
nameYesDisplay name used in notifications (typically the plane name)
severityYesHigh, Medium, or Low. Applied to every compliance violation from this file. This is the severity column from the Overview table.
rolesYesArray of { "id": "<roleId>", "displayName": "..." }. Only id is matched.
descriptionNoInformational.
expectedConfigNoThe target policy. Omit it entirely to classify by severity only, with no enforcement.

Classification only, no enforcement

Drop expectedConfig to put a role under coverage and assign it a severity, without checking its policy:

{
"name": "High Risk Roles",
"severity": "High",
"roles": [
{ "id": "194ae4cb-b126-40b2-bd5b-6091b380977d", "displayName": "Security Administrator" }
]
}

Enforce only part of the policy

expectedConfig is sparse: only the fields you include are checked. Missing fields mean no constraint.

{
"name": "Production Admins",
"severity": "Medium",
"roles": [ { "id": "...", "displayName": "..." } ],
"expectedConfig": {
"requireMFA": true,
"requireApproval": true
}
}

Unknown fields produce a warning but do not break the scan.

expectedConfig fields

These map to the catalog's recommendation columns as follows:

Catalog columnexpectedConfig field(s)
Max activationmaxActivationDuration
MFArequireMFA
ApprovalrequireApproval
JustificationrequireJustification
Auth contextnot an expectedConfig field. requireMFA checks that some MFA or auth-context rule is on. To verify the specific auth context (phishing-resistant, sign-in frequency) is correctly enforced, use Auth Context CA Compliance.

The full field list:

FieldGraph ruleDescription
maxActivationDurationExpiration_EndUser_Assignment.maximumDurationMax activation duration (ISO 8601, e.g. PT1H)
requireMFAAuthenticationContext_EndUser_Assignment.isEnabledMFA required (auth context or enabled rules)
requireJustificationEnablement_EndUser_Assignment.enabledRulesJustification required on activation
requireTicketingEnablement_EndUser_Assignment.enabledRulesTicket number required on activation
requireApprovalApproval_EndUser_Assignment.setting.isApprovalRequiredApproval required on activation
allowPermanentEligibleExpiration_Admin_Eligibility.isExpirationRequiredAllow permanent eligible assignments
maxEligibleDurationExpiration_Admin_Eligibility.maximumDurationMax duration of an eligible assignment (ISO 8601, e.g. P365D)
allowPermanentActiveExpiration_Admin_Assignment.isExpirationRequiredAllow permanent active assignments
maxActiveDurationExpiration_Admin_Assignment.maximumDurationMax duration of a directly active assignment (ISO 8601, e.g. P90D)

Where violations appear

A compliance violation lands in the regular High / Medium / Low section that matches the file's severity, alongside other PIM changes. Email adds a Classification N count to the stat bar; Teams, Slack, and Discord append a dedicated block. (Coverage findings, by contrast, go to the separate Classification section. See Coverage & Exclusions.)

Advanced: suppressing a known deviation

If a deviation is intentional and temporary, silence it until a deadline instead of fixing the policy. Add an entry to expected-changes.json in the repository root:

{
"expected": [
{
"workload": "directory-roles",
"entity": "global-administrator",
"fileType": "access-model-compliance",
"ruleId": "requireApproval",
"reason": "Approval requirement being phased out by 2026-06-15",
"expiresUtc": "2026-06-15T23:59:59Z"
}
]
}
  • The top-level key must be expected (a bare array is ignored).
  • fileType is access-model-compliance for directory-role compliance violations.
  • ruleId is the expectedConfig field that deviated.
  • expiresUtc is when the suppression lapses.

After each scan, PIM Monitor rewrites expected-changes.json: entries that matched a change and entries past their expiresUtc are removed; future-dated entries that have not matched yet are kept. See Expected Changes for the full mechanism.

Frequently asked questions

Can I classify a role without enforcing a policy?

Yes. Omit the expectedConfig field entirely to put the role under coverage and assign it a severity, without checking its policy.

Does expectedConfig have to list every policy field?

No. expectedConfig is sparse: only the fields you include are checked, and missing fields mean no constraint. Unknown fields produce a warning but do not break the scan.

How do I silence a deviation I know about?

Add an entry to expected-changes.json in the repository root with workload, entity, fileType set to access-model-compliance, the ruleId that deviated, a reason, and an expiresUtc deadline. The suppression lapses automatically after that date.