Invoke-EntraRoleCheck.ps1 is a PowerShell 7+ script designed to enumerate and analyze Azure Entra ID users with privileged directory roles including Global Administrators, Privileged Role Administrators, and other high-privilege roles. This tool is part of the EvilMist toolkit and helps security teams identify users with privileged access, analyze their security posture, and assess risks associated with role assignments.
Privileged directory roles provide extensive administrative capabilities. This script helps:
- Security Auditors: Identify users with privileged roles and assess their security posture
- Penetration Testers: Discover high-value targets and privilege escalation paths
- IT Administrators: Audit role assignments and ensure proper security controls
- Compliance Teams: Generate reports for privileged access governance and PIM compliance
- ✅ PowerShell 7+ Compatible: Modern PowerShell for cross-platform support
- ✅ Multiple Authentication Methods: Supports Azure CLI, Azure PowerShell, and interactive auth
- ✅ Comprehensive Role Coverage: Enumerates all directory roles including CRITICAL, HIGH, MEDIUM, and LOW risk roles
- ✅ PIM Support: Identifies both permanent (Active) and PIM-managed (Eligible/Active) role assignments
- ✅ Assignment Tracking: Shows assignment dates, duration, and expiration dates
- ✅ MFA Status Detection: Identifies privileged users without Multi-Factor Authentication
- ✅ Last Sign-In Tracking: Shows last login date/time and activity patterns
- ✅ Risk Assessment: Categorizes users by risk level based on role criticality and security posture
- ✅ Activity Analytics: Sign-in statistics, stale accounts, inactive users
- ✅ Stealth Mode: Configurable delays and jitter to avoid detection
- ✅ Export Options: CSV and JSON export formats
- ✅ Matrix View: Table format with analytics for quick visual scanning
- ✅ Filtering Options: Show only users without MFA, only permanent assignments, or include disabled accounts
The script analyzes all directory roles and categorizes them by risk level:
These roles have the highest level of privilege and pose the greatest security risk:
-
Global Administrator
- Full control over all Azure AD resources
- Can manage all other administrators
- Can reset passwords for all users including other Global Admins
- Can modify tenant-wide settings and policies
-
Privileged Role Administrator
- Can manage role assignments in Azure AD
- Can activate PIM roles for other users
- Can modify role definitions and assignments
- Critical for privilege escalation attacks
-
Privileged Authentication Administrator
- Can manage authentication methods for all users
- Can reset passwords for all users including Global Admins
- Can modify MFA settings
- High risk for credential attacks
These roles provide significant administrative capabilities:
- Exchange Administrator: Full control over Exchange Online
- SharePoint Administrator: Full control over SharePoint Online
- Security Administrator: Manages security policies and threat protection
- Compliance Administrator: Manages compliance policies and eDiscovery
- Application Administrator: Manages application registrations and service principals
- Cloud Application Administrator: Manages cloud applications and app proxy
- Hybrid Identity Administrator: Manages hybrid identity and directory sync
- Identity Governance Administrator: Manages access reviews and lifecycle policies
These roles provide moderate administrative capabilities:
- User Administrator: Can create and manage users
- Helpdesk Administrator: Can reset passwords for non-admins
- License Administrator: Manages license assignments
- Billing Administrator: Manages billing and subscriptions
- Authentication Administrator: Can manage authentication methods for non-admins
- Groups Administrator: Manages groups and group settings
These roles provide limited administrative capabilities:
- Directory Readers: Read-only access to directory data
- Directory Writers: Can create directory objects
- Guest Inviter: Can invite guest users
The script identifies three types of role assignments:
- Type: Direct, permanent role assignment
- Risk: Higher risk - roles are always active
- Use Case: Legacy assignments or roles that require constant access
- Recommendation: Consider migrating to PIM for high-privilege roles
- Type: User is eligible for the role but must activate it
- Risk: Lower risk - role is not active until activated
- Use Case: Just-in-time access for occasional administrative tasks
- Recommendation: Preferred method for privileged access
- Type: User has activated their eligible role assignment
- Risk: Medium risk - role is currently active (time-limited)
- Use Case: Temporary elevated access for specific tasks
- Recommendation: Monitor activation duration and ensure proper expiration
-
PowerShell 7+
- Download: https://aka.ms/powershell-release?tag=stable
- The script will check and warn if older version is detected
-
Microsoft Graph PowerShell SDK
Install-Module Microsoft.Graph -Scope CurrentUser
Or install individual modules:
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser Install-Module Microsoft.Graph.Users -Scope CurrentUser Install-Module Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser Install-Module Microsoft.Graph.Identity.SignIns -Scope CurrentUser
The script requires the following Microsoft Graph API permissions:
-
Primary Scopes (preferred):
Directory.Read.All- Read directory data and rolesRoleManagement.Read.Directory- Read role assignments and PIM dataUser.Read.All- Read all user profilesUserAuthenticationMethod.Read.All- Read authentication methodsAuditLog.Read.All- Read audit logs and sign-in activity (optional)
-
Fallback Scopes (if full access unavailable):
Directory.Read.All- Read directory data and rolesRoleManagement.Read.Directory- Read role assignmentsUser.ReadBasic.All- Read basic user info
Note: If AuditLog.Read.All is not available, the script will automatically fall back to retrieving users without sign-in activity data. If RoleManagement.Read.Directory is not available, PIM data will not be retrieved. All other features will continue to work normally.
# Simple scan of all users with privileged roles
.\scripts\powershell\Invoke-EntraRoleCheck.ps1# Export to CSV
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -ExportPath "privileged-roles.csv"
# Export to JSON
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -ExportPath "role-results.json"# Scan all users including disabled accounts
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -IncludeDisabledUsers -ExportPath "all-roles.csv"# Filter to show only privileged users without MFA
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -OnlyNoMFA
# Matrix view with MFA filter
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -OnlyNoMFA -Matrix# Show only permanent (non-PIM) role assignments
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -OnlyPermanent
# Export permanent assignments
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -OnlyPermanent -ExportPath "permanent-admins.csv"# Display results in compact matrix format
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -Matrix
# Matrix view with export
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -Matrix -ExportPath "results.csv"# Use Azure CLI cached credentials
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -UseAzCliToken
# Use Azure PowerShell cached credentials
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -UseAzPowerShellToken
# Specify tenant
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -TenantId "your-tenant-id"# Enable stealth mode with default settings (500ms delay + 300ms jitter)
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -EnableStealth
# Stealth mode with minimal output
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -EnableStealth -QuietStealth
# Custom delay and jitter
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -RequestDelay 1.5 -RequestJitter 0.5
# Maximum stealth with custom retry
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -EnableStealth -MaxRetries 5 -QuietStealth# Comprehensive audit: all users, all roles, with export
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -IncludeDisabledUsers -Matrix -ExportPath "full-audit.csv"
# Security focus: high-risk users only (no MFA, permanent assignments)
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -OnlyNoMFA -OnlyPermanent -Matrix -ExportPath "high-risk-roles.csv"
# Stealth reconnaissance with Azure CLI token
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -UseAzCliToken -EnableStealth -QuietStealth -ExportPath "recon.json"| Parameter | Type | Description | Default |
|---|---|---|---|
-ExportPath |
String | Path to export results (CSV or JSON based on extension) | None |
-TenantId |
String | Optional Tenant ID. Uses home tenant if not specified | None |
-UseAzCliToken |
Switch | Try to use Azure CLI cached token first | False |
-UseAzPowerShellToken |
Switch | Try to use Azure PowerShell cached token first | False |
-IncludeDisabledUsers |
Switch | Include disabled user accounts in results | False |
-OnlyNoMFA |
Switch | Show only users without MFA enabled | False |
-OnlyPermanent |
Switch | Show only permanent (non-PIM) role assignments | False |
-Matrix |
Switch | Display results in matrix/table format | False |
| Parameter | Type | Range | Description | Default |
|---|---|---|---|---|
-EnableStealth |
Switch | - | Enable stealth mode with default delays (500ms + 300ms jitter) | False |
-RequestDelay |
Double | 0-60 | Base delay in seconds between API requests | 0 |
-RequestJitter |
Double | 0-30 | Random jitter range in seconds (+/-) | 0 |
-MaxRetries |
Int | 1-10 | Maximum retries on throttling (429) responses | 3 |
-QuietStealth |
Switch | - | Suppress stealth-related status messages | False |
The script provides detailed information about each role assignment:
[CRITICAL] john.admin@company.com - Global Administrator
Display Name: John Admin
User Type: Member
Email: john.admin@company.com
Job Title: IT Administrator
Department: IT
Account Status: Enabled
Role: Global Administrator
Role Risk Level: CRITICAL
Assignment Type: Active
Assignment Date: 2023-01-15T10:30:00Z (342 days ago)
MFA Enabled: No
Auth Methods: Password Only
Last Sign-In: 2024-12-20 14:23:45 (3 days ago) (Interactive)
Created: 2022-05-10T08:15:00Z (591 days old)
Licenses: 2 assigned
Risk Role Risk MFA Type Status User Principal Name Display Name Role Last Sign-In Duration Department
---- --------- --- ---- ------ ------------------- ------------ ---- ------------ -------- ----------
CRITICAL CRITICAL No Active Enabled john.admin@company.com John Admin Global Administrator 3d ago 342d IT
HIGH HIGH Yes PIM Eligible Enabled jane.secure@company.com Jane Secure Exchange Administrator 1d ago - Security
The script provides comprehensive statistics:
[SUMMARY]
Total role assignments: 25
Unique users with roles: 18
- CRITICAL risk: 3
- HIGH risk: 8
- MEDIUM risk: 10
- LOW risk: 4
[MFA STATUS]
With MFA enabled: 15
Without MFA: 10
[ASSIGNMENT TYPES]
Permanent (Active): 12
PIM Eligible: 8
PIM Active: 5
[USERS BY ROLE]
Global Administrator: 3
Exchange Administrator: 5
Security Administrator: 4
User Administrator: 6
SharePoint Administrator: 3
Application Administrator: 2
Compliance Administrator: 2
[TOP DEPARTMENTS]
IT: 10
Security: 5
Operations: 3
[SIGN-IN ACTIVITY]
Never signed in: 1
Recent (≤30 days): 18
Stale (>90 days): 6
The script assigns risk levels based on role criticality, assignment type, and MFA configuration:
| Risk Level | Criteria | Color | Recommendation |
|---|---|---|---|
| CRITICAL | CRITICAL role without MFA OR HIGH role without MFA with permanent assignment | Red | IMMEDIATE ACTION REQUIRED: Enable MFA or migrate to PIM |
| HIGH | HIGH role without MFA OR MEDIUM role without MFA with permanent assignment | Yellow | URGENT: Enable MFA or migrate to PIM |
| MEDIUM | CRITICAL/HIGH role with MFA OR MEDIUM role without MFA OR PIM-managed assignment | Green | REVIEW: Consider PIM for permanent assignments |
| LOW | LOW risk role OR disabled account | Gray | MONITOR: Acceptable risk |
IF role is CRITICAL:
IF no MFA AND permanent assignment:
RISK = CRITICAL
ELSE IF no MFA:
RISK = CRITICAL
ELSE IF permanent assignment:
RISK = HIGH
ELSE:
RISK = MEDIUM
ELSE IF role is HIGH:
IF no MFA AND permanent assignment:
RISK = CRITICAL
ELSE IF no MFA:
RISK = HIGH
ELSE IF permanent assignment:
RISK = HIGH
ELSE:
RISK = MEDIUM
ELSE IF role is MEDIUM:
IF no MFA AND permanent assignment:
RISK = HIGH
ELSE IF no MFA:
RISK = MEDIUM
ELSE:
RISK = MEDIUM
ELSE:
RISK = LOW
Users with privileged directory roles can:
- Full Tenant Control: Global Admins can modify any setting, create/delete any object
- Privilege Escalation: Privileged Role Admins can grant themselves or others Global Admin
- Credential Attacks: Privileged Authentication Admins can reset passwords for all users
- Data Exfiltration: Exchange/SharePoint Admins can access all emails and documents
- Security Bypass: Security Admins can modify security policies and disable protections
- Application Control: Application Admins can create service principals with high permissions
- Identity Manipulation: Can modify authentication methods, bypass MFA, reset credentials
-
Global Admin Without MFA (CRITICAL Risk)
- Single point of failure for entire tenant
- Can be compromised with just username/password
- Immediate remediation required
-
Permanent High-Privilege Assignments (HIGH Risk)
- Roles are always active, increasing attack surface
- No time-limited access controls
- Should be migrated to PIM
-
Stale Privileged Accounts (MEDIUM-HIGH Risk)
- Users who haven't signed in for 90+ days but still have roles
- May indicate forgotten or orphaned accounts
- Potential for account takeover if credentials leaked
-
Multiple CRITICAL Roles (CRITICAL Risk)
- Users with multiple high-privilege roles
- Increased attack surface and blast radius
- Should be reviewed for business justification
- Migrate Permanent Assignments: Convert permanent assignments to PIM eligible
- Time-Limited Activations: Set maximum activation duration (e.g., 8 hours)
- Approval Requirements: Require approval for high-privilege role activations
- Regular Access Reviews: Review eligible assignments quarterly
- MFA Enforcement: Require MFA for all role activations
- Alerting: Monitor role activations and unusual patterns
- Regular Audits: Run monthly to track role assignments and changes
- MFA Enforcement: Ensure all privileged users have MFA enabled
- PIM Migration: Migrate permanent assignments to PIM for high-privilege roles
- Least Privilege: Review assignments and remove unnecessary roles
- Monitor Activity: Track sign-in patterns and role activations
- Document Changes: Maintain records of role assignments and business justification
- Initial Reconnaissance: Identify users with privileged roles
- Target Selection: Prioritize CRITICAL risk users without MFA
- Privilege Escalation: Document role assignments for attack paths
- PIM Analysis: Identify PIM-eligible roles that could be activated
- Stealth Operations: Use
-EnableStealthto avoid detection
- Documentation: Export results regularly for audit trails
- Policy Alignment: Verify role assignments align with business needs
- Trend Analysis: Compare results over time
- Remediation Tracking: Monitor MFA adoption and PIM migration rates
- Access Reviews: Use reports for quarterly access certification
Includes all fields for analysis:
- DisplayName, UserPrincipalName, Email
- AccountEnabled, UserType
- JobTitle, Department
- CreatedDateTime, DaysOld
- RoleName, RoleId, RoleRiskLevel
- AssignmentType, AssignmentDate, AssignmentDuration, AssignmentEndDate
- LastSignIn, LastSignInDisplay, DaysSinceLastSignIn, SignInType
- MFAEnabled, AuthMethods, MethodCount
- HasLicenses, LicenseCount
- RiskLevel
Structured format for automation:
[
{
"DisplayName": "John Admin",
"UserPrincipalName": "john.admin@company.com",
"Email": "john.admin@company.com",
"AccountEnabled": true,
"UserType": "Member",
"JobTitle": "IT Administrator",
"Department": "IT",
"RoleName": "Global Administrator",
"RoleRiskLevel": "CRITICAL",
"AssignmentType": "Active",
"AssignmentDate": "2023-01-15T10:30:00Z",
"AssignmentDuration": 342,
"MFAEnabled": false,
"AuthMethods": "Password Only",
"RiskLevel": "CRITICAL",
"LastSignInDisplay": "2024-12-20 14:23:45 (3 days ago)",
"DaysSinceLastSignIn": 3
}
]Cause: No users have been assigned directory roles, or roles haven't been activated.
Solution:
- Verify roles exist in your tenant
- Check if roles are assigned via PIM (may require different permissions)
- Ensure you have proper read permissions
Cause: Insufficient permissions for PIM data.
Solution:
# Disconnect and reconnect with proper scopes
Disconnect-MgGraph
.\scripts\powershell\Invoke-EntraRoleCheck.ps1
# Accept permission consent when prompted (RoleManagement.Read.Directory)Cause: Insufficient Graph API permissions.
Solution:
# Disconnect and reconnect with proper scopes
Disconnect-MgGraph
.\scripts\powershell\Invoke-EntraRoleCheck.ps1
# Accept permission consent when promptedCause: Missing or outdated Microsoft.Graph modules.
Solution:
# Update all Graph modules
Update-Module Microsoft.Graph -Force
# Or reinstall
Uninstall-Module Microsoft.Graph -AllVersions
Install-Module Microsoft.Graph -Scope CurrentUserCause: Large number of role assignments or throttling.
Solution:
# Use stealth mode to handle throttling
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -EnableStealth -MaxRetries 5
# Or reduce load with filtering
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -OnlyNoMFA# Identify all users with privileged roles
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -Matrix -ExportPath "audit_$(Get-Date -Format 'yyyy-MM-dd').csv"Output: CSV file with all role assignments, risk levels, and MFA status.
# Find privileged users without MFA
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -OnlyNoMFA -Matrix
# Review output, then remediateUse Case: Identify immediate security risks for remediation.
# Find permanent assignments (should be migrated to PIM)
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -OnlyPermanent -Matrix -ExportPath "permanent-assignments.csv"Use Case: Identify roles that should be migrated to PIM.
# Stealth mode scan using existing Azure CLI token
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -UseAzCliToken -EnableStealth -QuietStealth -ExportPath "targets.json"Use Case: Silent enumeration of high-value targets during engagement.
# Monthly audit including disabled accounts
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -IncludeDisabledUsers -Matrix -ExportPath "compliance_report.csv"
# Compare with previous month's reportUse Case: Track role assignments and MFA adoption over time.
# Scan specific tenant
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -TenantId "customer-tenant-id" -ExportPath "customer_roles.csv"
# Repeat for each tenantUse Case: MSP or consulting engagement across multiple tenants.
# Schedule weekly scans
$scheduledScript = {
$date = Get-Date -Format "yyyy-MM-dd"
$path = "C:\SecurityAudits\RoleCheck_$date.csv"
C:\Tools\Invoke-EntraRoleCheck.ps1 -Matrix -ExportPath $path
# Send alert if critical-risk users found
$results = Import-Csv $path
$criticalRisk = $results | Where-Object { $_.RiskLevel -eq "CRITICAL" }
if ($criticalRisk.Count -gt 0) {
Send-MailMessage -To "security@company.com" `
-Subject "ALERT: $($criticalRisk.Count) critical-risk privileged users found" `
-Body "Review attached report." `
-Attachments $path `
-SmtpServer "smtp.company.com"
}
}
# Create scheduled task (run as admin)
$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday -At 6am
$action = New-ScheduledTaskAction -Execute "pwsh.exe" -Argument "-File C:\Scripts\WeeklyRoleCheck.ps1"
Register-ScheduledTask -TaskName "Weekly Role Audit" -Trigger $trigger -Action $action# Export JSON for SIEM ingestion
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -ExportPath "siem_feed.json"
# Post-process for your SIEM format
$results = Get-Content "siem_feed.json" | ConvertFrom-Json
$siemEvents = $results | ForEach-Object {
@{
timestamp = (Get-Date).ToString("o")
event_type = "azure_role_assignment"
severity = $_.RiskLevel
user = $_.UserPrincipalName
role = $_.RoleName
role_risk = $_.RoleRiskLevel
assignment_type = $_.AssignmentType
mfa_enabled = $_.MFAEnabled
last_signin = $_.LastSignInDisplay
}
}
$siemEvents | ConvertTo-Json | Out-File "siem_formatted.json"# Run remotely on jump box or admin workstation
$session = New-PSSession -ComputerName "admin-server.company.com"
Invoke-Command -Session $session -ScriptBlock {
cd C:\Tools
.\scripts\powershell\Invoke-EntraRoleCheck.ps1 -Matrix -ExportPath "C:\Reports\roles.csv"
}
# Retrieve results
Copy-Item -FromSession $session -Path "C:\Reports\roles.csv" -Destination ".\local_copy.csv"
Remove-PSSession $session- Initial implementation
- Support for all directory roles
- PIM eligible and active assignment detection
- MFA detection and risk assessment
- Matrix view and export capabilities
- Stealth mode with configurable delays
- Multiple authentication methods
- Comprehensive user analytics
This script is part of the EvilMist toolkit.
Copyright (C) 2025 Logisek
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
See the LICENSE file for more details.
Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.
Visit: https://github.com/Logisek/EvilMist
For questions, issues, or feature requests:
- GitHub Issues: https://github.com/Logisek/EvilMist/issues
- Email: info@logisek.com
- Website: https://logisek.com
- Invoke-EntraRecon.ps1: Comprehensive Azure AD reconnaissance
- Invoke-EntraMFACheck.ps1: MFA compliance audit
- Invoke-EntraGuestCheck.ps1: Guest account security analysis
- Invoke-EntraAppAccess.ps1: Critical administrative application access audit