Pull up your Entra ID Roles and administrators blade right now and look at Global Administrator. How many people are on that list? Now ask yourself the harder question: how many of them are there because they activated the role through PIM twenty minutes ago and it will drop off automatically — and how many of them have just been Global Administrator, permanently, since some project two years ago that nobody remembers the details of? If you cannot answer that second question in under thirty seconds, you have a standing access problem, and it is almost certainly bigger than you think.
This post explains exactly what "standing" privileged access means in Entra ID, why it survives even in tenants that have PIM switched on, how to manually spot-check it in the portal, and then gives you a complete PowerShell script that audits every privileged directory role tenant-wide and tells you precisely who to fix.
Get-PIMStandingAdminReport.ps1 is free and open source. Download it from the Imran76Awan/Daily-Tasks repository on GitHub — no sign-in required. This script is read-only — it audits and reports, it never modifies an assignment.
The problem: what "standing" Global Admin access actually means
There are two completely different ways a user can hold a privileged Entra ID directory role — Global Administrator, Privileged Role Administrator, Security Administrator, Exchange Administrator, whatever it might be. The distinction between them is the single most important control in your entire identity estate, and most tenants get it wrong without realising it.
Standing (active) access means the user has the role right now, permanently, with no expiry and no action required to use it. They sign in tomorrow, the day after, next year — the privilege is simply there, all the time, attached to their account like a permanent feature.
Eligible access via Privileged Identity Management (PIM) means the user is allowed to hold the role, but does not hold it by default. To actually use Global Administrator privileges, they have to go into the PIM blade (or use the My Roles pane), request activation, supply a business justification, satisfy an MFA challenge, and — depending on policy — wait for an approver. The role is then granted for a limited window, typically one to eight hours, and automatically expires. Nothing happens silently. Every activation is logged, every activation can trigger an alert, and every activation can require a second person to approve it.
Here is why this distinction is the single biggest thing an attacker cares about. If an attacker phishes, password-sprays, or token-steals their way into an account that is a standing Global Administrator, they are Global Administrator the moment they get in — full stop. No activation step to catch. No MFA challenge on privilege use, beyond whatever got them in the door in the first place. No approval workflow to trip an alert. No time limit forcing them to re-authenticate. If that same account only holds Global Administrator as an eligible PIM assignment, the attacker gets a compromised identity with zero directory privileges — they would then have to go through activation, and activation is exactly the kind of anomalous, loud, logged event that Conditional Access, Microsoft Entra ID Protection, and your SOC are built to catch.
This is why standing privileged access is, without exception, the number one finding in every external security review, penetration test, and Microsoft Secure Score assessment. It is not an exotic misconfiguration — it is the default state of every tenant that has never actively gone looking for it. And critically: turning PIM on for your tenant does not fix existing standing assignments. PIM governs how new eligible assignments get activated. It does nothing whatsoever to the active/permanent assignments that already existed before PIM was configured. Every tenant that adopted PIM part-way through its life — which is almost all of them — has a tail of legacy standing assignments left over from before anyone was thinking about just-in-time access. Those assignments do not go away on their own. They just sit there, quietly, until someone goes looking.
Why it happens: active and eligible are two entirely separate objects in Microsoft Graph
The reason this problem is so persistent is structural, not just a matter of admin discipline. In the Microsoft Graph data model that backs Entra ID PIM, "who has this role right now" and "who is allowed to activate this role" are represented by two completely separate resource types:
roleAssignmentScheduleInstance— the current, in-effect ACTIVE assignments. Every entry here represents someone who holds the role right now, this second, whether that is because they permanently hold it or because they just activated an eligible assignment through PIM.roleEligibilityScheduleInstance— the current ELIGIBLE assignments. Every entry here represents someone who is allowed to request activation but does not hold the role by default.
Crucially, an active assignment instance carries an assignmentType property that can be either Assigned (a direct, standing assignment made outside of PIM's activation workflow) or Activated (a temporary assignment that exists because someone activated a matching eligible assignment, and which will disappear automatically at its EndDateTime). These two values look identical in the classic Entra ID Roles and administrators blade — both simply show up as "this person has this role" — but they represent fundamentally different risk profiles.
This is exactly why nobody catches this by browsing the portal. The classic Roles and administrators blade is designed to answer "who currently has this role," which is the right question for day-to-day admin work but the wrong question for a security audit. It does not clearly flag, in a single bulk view, which of those assignees are standing and which are temporary PIM activations. And Entra ID ships with well over 60 built-in directory roles. Manually opening PIM, clicking into each role, flipping to the Assignments tab, and cross-checking Eligible against Active for every single one of them — across every privileged and semi-privileged role — is the kind of tedious, error-prone task that simply never gets done proactively. It only gets done reactively, after a pen test flags it, or worse, after an incident.
How to verify — manually checking one role in PIM
Before reaching for a script, it is worth knowing how to spot-check a single role by hand, because this is exactly what an auditor or a pen tester will do to validate any finding you report.
- Sign in to the Microsoft Entra admin center as at least a Privileged Role Administrator or Global Administrator
- Go to Identity Governance › Privileged Identity Management
- Under Manage, select Microsoft Entra roles
- Click Roles, then select Global Administrator (or any role you want to spot-check)
- Select the Assignments tab — this shows two sub-tabs: Eligible assignments and Active assignments
The Active assignments tab is where the standing-access problem hides in plain sight. Every row on this tab currently holds the role. What the portal does not do particularly well is make it obvious, at a glance, which of those rows are permanent and which are time-boxed PIM activations that will disappear in a few hours:
Look closely and the pattern is visible on this one role: two rows show Assigned with an end date of Permanent — those are the standing assignments — and one row shows Activated with a hard four-hour expiry — that is PIM working exactly as intended. Spotting this on one role, for one tenant, takes about two minutes once you know what to look for. Doing it across 60-plus built-in roles, for every principal assigned to each of them, tenant-wide, by hand, does not scale. That is the entire reason the script in the next section exists.
The fix — Get-PIMStandingAdminReport.ps1
The script uses the Microsoft.Graph.Identity.Governance PowerShell module to do programmatically, across every role in the tenant, exactly what you just did by hand for one role. It connects to Microsoft Graph, enumerates every directory role definition, pulls every current active assignment instance and every current eligible assignment instance, and cross-references them by principal ID to work out which active assignments have no eligible counterpart at all — meaning they were never intended to be time-boxed through PIM in the first place.
# Connect with read-only Graph scopes Connect-MgGraph -Scopes 'RoleManagement.Read.Directory','Directory.Read.All','User.Read.All' -NoWelcome # Every directory role that exists in the tenant (60+ built-in roles) $roleDefinitions = Get-MgRoleManagementDirectoryRoleDefinition -All # Everyone who holds a role RIGHT NOW — standing AND PIM-activated, mixed together $activeInstances = Get-MgRoleManagementDirectoryRoleAssignmentScheduleInstance -All # Everyone who is ALLOWED to activate a role but does not hold it by default $eligibleInstances = Get-MgRoleManagementDirectoryRoleEligibilityScheduleInstance -All # Build a lookup of principal+role combos that DO have a matching eligible schedule $eligibleLookup = @{} foreach ($e in $eligibleInstances) { $key = "$($e.PrincipalId)|$($e.RoleDefinitionId)" $eligibleLookup[$key] = $true } # A standing (permanent) finding = AssignmentType 'Assigned' AND no eligible counterpart foreach ($active in $activeInstances) { if ($active.AssignmentType -ne 'Assigned') { continue } # 'Activated' = time-boxed PIM, skip $key = "$($active.PrincipalId)|$($active.RoleDefinitionId)" if ($eligibleLookup.ContainsKey($key)) { continue } # ...role name resolved, severity assigned RED/AMBER, added to $findings }
The severity logic is deliberately simple: any standing finding on Global Administrator, Privileged Role Administrator, Privileged Authentication Administrator, or Security Administrator is flagged RED — these are the roles that can escalate further privilege or bypass MFA/Conditional Access controls entirely. A standing finding on any other built-in directory role — Exchange Administrator, SharePoint Administrator, Helpdesk Administrator, and so on — is flagged AMBER. Here is a realistic run against a test tenant before any remediation has happened:
[INFO] Connecting to Microsoft Graph... === PIM Standing Admin Role Audit === Tenant : ████████-████-████-████-████████████ Account: i.awan@contoso.com [INFO] Enumerating directory role definitions... [INFO] Found 63 directory role definitions [INFO] Retrieving ACTIVE role assignment instances (roleAssignmentScheduleInstance)... [INFO] Found 118 active assignment instances [INFO] Retrieving ELIGIBLE role assignment instances (roleEligibilityScheduleInstance)... [INFO] Found 41 eligible assignment instances === Findings: standing (permanent) admin assignments outside PIM === [RED ] Global Administrator j.mercer@contoso.com held 612 day(s) (since 2024-11-19) [RED ] Global Administrator svc-legacyadmin@contoso.com held 1102 day(s) (since 2023-07-10) [AMBER] Exchange Administrator r.oduya@contoso.com held 340 day(s) (since 2025-08-11) --- Summary --- Total active assignment instances : 118 Total eligible assignment instances : 41 Standing (permanent) findings : 3 (RED: 2, AMBER: 1) [INFO] Audit complete.
Three findings, two of them on Global Administrator. One is a named human account that has clearly just never been migrated to PIM. The other, svc-legacyadmin, is worse — a service-style account with a standing Global Administrator assignment for over three years, exactly the kind of forgotten credential attackers go looking for.
AssignmentType of Assigned can also appear for entirely legitimate short-term active assignments made directly by another admin outside of PIM — for example, a break-glass account, or a temporary direct assignment made during an incident before PIM eligibility was set up for that user. Don't treat every single hit from this script as an automatic finding to remediate blindly. Cross-check each one against your change log or ticketing system, and confirm with the assigning admin before you touch it.Once you have a validated list of standing assignments, remediation is a five-step process for each flagged principal:
Proof it worked — re-running the script after remediation
After working through the five remediation steps for all three flagged users — removing the standing assignments and replacing them with eligible ones, with an 8-hour maximum activation window and approval required on Global Administrator — re-run the exact same script with no other changes:
[INFO] Connecting to Microsoft Graph... === PIM Standing Admin Role Audit === Tenant : ████████-████-████-████-████████████ Account: i.awan@contoso.com [INFO] Enumerating directory role definitions... [INFO] Found 63 directory role definitions [INFO] Retrieving ACTIVE role assignment instances (roleAssignmentScheduleInstance)... [INFO] Found 74 active assignment instances [INFO] Retrieving ELIGIBLE role assignment instances (roleEligibilityScheduleInstance)... [INFO] Found 85 eligible assignment instances === Findings: standing (permanent) admin assignments outside PIM === [OK] No standing active assignments found on any directory role. Every active assignment is either PIM-activated (time-boxed) or the role has no assignees. --- Summary --- Total active assignment instances : 74 Total eligible assignment instances : 85 Standing (permanent) findings : 0 (RED: 0, AMBER: 0) [INFO] Audit complete.
Notice what moved, not just the finding count. The total number of active assignment instances dropped from 118 to 74 — because most of what looked like "active" assignments before were actually just-in-time activations that had simply not expired yet at the moment the script ran, and the genuinely standing ones are now gone. Meanwhile eligible assignments jumped from 41 to 85, because the tenant now has real PIM coverage instead of a mix of legacy permanent grants and a partial PIM rollout. That eligible-to-active ratio is a good one-number health metric to track over time: a healthy PIM tenant should show far more eligible assignments than active ones at any given moment, because active assignments should mostly represent short activation windows, not permanent grants.
Get-PIMStandingAdminReport.ps1 to run weekly — via an Azure Automation runbook with a system-assigned managed identity holding the read-only Graph scopes, or a scheduled task on a jump box — with -ExportCsv enabled. Standing access creeps back in constantly: a new hire gets a direct assignment "just for now" during onboarding, a contractor is added permanently instead of eligibly under deadline pressure, and six months later it's a forgotten finding again.I'll audit your tenant's PIM configuration, flag every permanent privileged assignment, and help you migrate to eligible-only access without breaking anything your team depends on.
References
- Start using Privileged Identity Management — Microsoft Learn
- Configure Microsoft Entra role settings in PIM — Microsoft Learn
- Privileged Identity Management (PIM) API overview — Microsoft Graph
- Microsoft Graph PowerShell SDK overview — Microsoft Learn
How many standing Global Admins does YOUR tenant actually have? Most admins guess wrong — usually low. Run the script, and see whether your number matches what you expected before you looked.