Invoke-EntraServicePrincipalCheck.ps1 is a PowerShell 7+ script designed to enumerate and analyze Azure Entra ID service principals (service accounts) and perform a comprehensive security audit. This tool identifies high-risk service accounts by analyzing credentials, permissions, owners, and usage patterns. This script is part of the EvilMist toolkit and helps security teams identify misconfigured or high-risk service principals in their Azure AD tenant.
Service principals are non-human accounts used by applications and services to authenticate and access Azure AD resources. This script helps:
- Security Auditors: Identify service principals with expired credentials, high permissions, or insecure configurations
- Penetration Testers: Discover high-value service accounts for privilege escalation attacks
- IT Administrators: Audit service principal security posture and compliance
- Compliance Teams: Generate reports for service account governance
- ✅ PowerShell 7+ Compatible: Modern PowerShell for cross-platform support
- ✅ Multiple Authentication Methods: Supports Azure CLI, Azure PowerShell, and interactive auth
- ✅ Credential Enumeration: Identifies service principals with secrets and certificates
- ✅ Expiration Tracking: Detects expired credentials and credentials expiring soon (≤30 days)
- ✅ Permission Analysis: Identifies high-risk and critical permissions assigned to service principals
- ✅ Owner Analysis: Checks service principal owners and their MFA status
- ✅ Risk Assessment: Categorizes service principals by risk level (CRITICAL/HIGH/MEDIUM/LOW)
- ✅ Activity Tracking: Identifies unused/inactive service principals
- ✅ 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: Filter by credentials, expired credentials, or high permissions
The script performs comprehensive security analysis on all service principals in the tenant:
-
Secret Detection
- Identifies service principals with password credentials (secrets)
- Tracks secret expiration dates
- Flags expired secrets
- Warns about secrets expiring within 30 days
-
Certificate Detection
- Identifies service principals with key credentials (certificates)
- Tracks certificate expiration dates
- Flags expired certificates
- Warns about certificates expiring within 30 days
The script identifies service principals with high-risk permissions:
RoleManagement.ReadWrite.Directory- Can grant directory rolesAppRoleAssignment.ReadWrite.All- Can assign app rolesApplication.ReadWrite.All- Can modify applicationsDirectory.ReadWrite.All- Can modify directory objectsPrivilegedAccess.ReadWrite.AzureAD- Can manage privileged access
Directory.ReadWrite.All- Full directory write accessUser.ReadWrite.All- Can modify all usersGroup.ReadWrite.All- Can modify all groupsMail.ReadWrite- Can read/write emailsFiles.ReadWrite.All- Can access all filesSites.ReadWrite.All- Can access all SharePoint sitesExchange.ManageAsApp- Exchange management permissionsAuditLog.Read.All- Can read audit logsPolicy.ReadWrite.ConditionalAccess- Can modify CA policies- And more...
- Enumerates all owners of each service principal
- Checks MFA status for each owner
- Flags service principals with owners without MFA
- Identifies service principals without owners
- Tracks service principal creation dates
- Identifies old/unused service principals
- Can be extended with audit log queries for usage patterns
-
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.Applications -Scope CurrentUser Install-Module Microsoft.Graph.Users -Scope CurrentUser Install-Module Microsoft.Graph.Identity.SignIns -Scope CurrentUser
The script requires the following Microsoft Graph API permissions:
-
Primary Scopes (preferred):
Application.Read.All- Read application registrations and credentialsDirectory.Read.All- Read directory dataUser.Read.All- Read user profiles (for owner analysis)UserAuthenticationMethod.Read.All- Read authentication methods (for MFA check)AuditLog.Read.All- Read audit logs (optional, for usage tracking)
-
Fallback Scopes (if full access unavailable):
Application.Read.All- Read application registrationsDirectory.Read.All- Read directory dataUser.ReadBasic.All- Read basic user info
Note: Application.Read.All is required to read credential information. Without it, credential analysis will be limited.
# Simple scan of all service principals
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1
# The script will analyze all service principals and show security findings# Export to CSV
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -ExportPath "service-principals.csv"
# Export to JSON
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -ExportPath "sp-results.json"# Scan all service principals including disabled ones
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -IncludeDisabled -ExportPath "all-sp.csv"# Filter to show only service principals with secrets/certificates
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -OnlyWithCredentials
# Matrix view with credentials filter
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -OnlyWithCredentials -Matrix# Filter to show only service principals with expired credentials
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -OnlyExpiredCredentials
# Matrix view with expired credentials filter
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -OnlyExpiredCredentials -Matrix# Filter to show only service principals with high-risk permissions
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -OnlyHighPermission
# Matrix view with high permission filter
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -OnlyHighPermission -Matrix# Display results in compact matrix format
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -Matrix
# Matrix view with export
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -Matrix -ExportPath "results.csv"# Use Azure CLI cached credentials
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -UseAzCliToken
# Use Azure PowerShell cached credentials
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -UseAzPowerShellToken
# Specify tenant
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -TenantId "your-tenant-id"# Enable stealth mode with default settings (500ms delay + 300ms jitter)
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -EnableStealth
# Stealth mode with minimal output
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -EnableStealth -QuietStealth
# Custom delay and jitter
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -RequestDelay 1.5 -RequestJitter 0.5
# Maximum stealth with custom retry
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -EnableStealth -MaxRetries 5 -QuietStealth# Comprehensive audit: all service principals with export
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -IncludeDisabled -Matrix -ExportPath "full-audit.csv"
# Security focus: high-risk service principals only
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -OnlyHighPermission -OnlyExpiredCredentials -Matrix -ExportPath "high-risk-sp.csv"
# Stealth reconnaissance with Azure CLI token
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.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 |
-IncludeDisabled |
Switch | Include disabled service principals in results | False |
-OnlyWithCredentials |
Switch | Show only service principals with credentials/secrets | False |
-OnlyExpiredCredentials |
Switch | Show only service principals with expired credentials | False |
-OnlyHighPermission |
Switch | Show only service principals with high-risk permissions | 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 service principal:
[CRITICAL] MyApp Service Principal
App ID: 12345678-1234-1234-1234-123456789abc
Service Principal ID: 87654321-4321-4321-4321-cba987654321
Type: Application
Account Status: Enabled
Credentials: 2 secret(s), 1 expired, 1 cert(s)
[!] EXPIRED CREDENTIALS DETECTED
Permissions: 5 assigned
[!] CRITICAL permissions detected
Permission details: Microsoft Graph, Exchange Online
Owners: 2
[!] 1 owner(s) without MFA
Owner details: admin@company.com (MFA: No); secure@company.com (MFA: Yes)
Created: 2023-01-15T10:30:00Z (365 days old)
Risk Status Display Name App ID Credentials Permissions High Risk Owners Owners No MFA
---- ------ ------------ ------ ----------- ----------- --------- ------ --------------
CRITICAL Enabled MyApp Service Principal 12345678-1234-1234-1234-123456789abc 2 secret(s), 1 expired 5 Yes 2 1
HIGH Enabled Another SP 87654321-4321-4321-4321-cba987654321 1 cert(s) 3 Yes 1 0
The script provides comprehensive statistics:
[SUMMARY]
Total service principals analyzed: 45
- CRITICAL risk: 2
- HIGH risk: 8
- MEDIUM risk: 15
- LOW risk: 20
[CREDENTIALS]
With credentials: 30
With expired credentials: 5
With expiring soon (≤30 days): 3
[PERMISSIONS]
With high-risk permissions: 12
[OWNERS]
With owners without MFA: 7
The script assigns risk levels based on credentials, permissions, and owner security:
| Risk Level | Criteria | Color | Recommendation |
|---|---|---|---|
| CRITICAL | Has critical permissions AND expired credentials | Red | IMMEDIATE ACTION REQUIRED: Rotate credentials, review permissions |
| HIGH | Has high-risk/critical permissions OR expired credentials OR owners without MFA | Yellow | URGENT: Review and remediate security issues |
| MEDIUM | Has credentials expiring soon OR has permissions assigned | Cyan | REVIEW: Monitor and plan credential rotation |
| LOW | No credentials, no high-risk permissions, secure owners | Green | ACCEPTABLE: Low risk, monitor periodically |
IF has critical permissions AND expired credentials:
RISK = CRITICAL (Highest risk - immediate action needed)
ELSE IF has high-risk permissions OR expired credentials OR owners without MFA:
RISK = HIGH (Urgent security issues)
ELSE IF has credentials expiring soon OR has permissions:
RISK = MEDIUM (Needs attention)
ELSE:
RISK = LOW (Acceptable risk)
Service principals are non-human accounts that can have extensive permissions. Compromised service principals can lead to:
- Privilege Escalation: Service principals with high permissions can grant themselves or others elevated roles
- Data Exfiltration: Service principals with read permissions can access sensitive data
- Persistent Access: Expired credentials may still be valid if not properly rotated
- Lateral Movement: Service principals can be used to move between tenants or resources
- Compliance Violations: Unmanaged service principals violate security best practices
-
Expired Credentials with Critical Permissions (CRITICAL Risk)
- Service principal can still authenticate with expired credentials in some cases
- Critical permissions allow privilege escalation
- Immediate credential rotation required
-
Owners Without MFA (HIGH Risk)
- Owners can modify service principal configuration
- Without MFA, owner accounts are vulnerable to credential attacks
- Compromised owner = compromised service principal
-
Unused Service Principals with Credentials (MEDIUM Risk)
- Old service principals may have forgotten credentials
- Unused accounts are often not monitored
- Potential for abuse if credentials are leaked
-
Service Principals Without Owners (MEDIUM Risk)
- No accountability for the service principal
- Difficult to track who manages it
- May be orphaned or forgotten
-
Credential Management
- Rotate credentials regularly (every 90 days or less)
- Use certificates instead of secrets when possible
- Set expiration dates for all credentials
- Remove expired credentials immediately
-
Permission Management
- Follow principle of least privilege
- Regularly review and audit permissions
- Remove unnecessary permissions
- Use managed identities when possible
-
Owner Management
- Assign at least 2 owners to each service principal
- Ensure all owners have MFA enabled
- Regularly review owner assignments
- Remove inactive owners
-
Monitoring
- Monitor service principal usage
- Alert on credential expiration
- Track permission changes
- Audit service principal creation
- Regular Audits: Run monthly to track service principal changes
- Credential Rotation: Ensure all credentials are rotated before expiration
- Permission Reviews: Review and remove unnecessary permissions quarterly
- Owner Verification: Verify all owners have MFA enabled
- Documentation: Maintain records of service principal purpose and owners
- Initial Reconnaissance: Identify high-value service principals for targeting
- Credential Hunting: Focus on service principals with expired or expiring credentials
- Permission Analysis: Identify service principals with privilege escalation capabilities
- Owner Targeting: Target service principals with owners without MFA
- Stealth Operations: Use
-EnableStealthto avoid detection
- Documentation: Export results regularly for audit trails
- Policy Alignment: Verify service principals align with security policies
- Trend Analysis: Compare results over time to track improvements
- Remediation Tracking: Monitor credential rotation and permission cleanup
- Access Reviews: Use reports for quarterly service principal certification
Includes all fields for analysis:
- DisplayName, AppId, ServicePrincipalId
- ServicePrincipalType, AccountEnabled
- CreatedDateTime, DaysOld
- HasSecrets, HasCertificates
- SecretCount, CertificateCount
- ExpiredSecretsCount, ExpiredCertificatesCount
- ExpiringSoonSecretsCount, ExpiringSoonCertificatesCount
- CredentialSummary
- PermissionCount, HasHighRiskPerms, HasCriticalPerms
- Permissions
- OwnerCount, OwnersWithoutMFA
- OwnerDetails
- RiskLevel
Structured format for automation:
[
{
"DisplayName": "MyApp Service Principal",
"AppId": "12345678-1234-1234-1234-123456789abc",
"ServicePrincipalId": "87654321-4321-4321-4321-cba987654321",
"ServicePrincipalType": "Application",
"AccountEnabled": true,
"HasSecrets": true,
"HasCertificates": true,
"SecretCount": 2,
"CertificateCount": 1,
"ExpiredSecretsCount": 1,
"ExpiredCertificatesCount": 0,
"PermissionCount": 5,
"HasHighRiskPerms": true,
"HasCriticalPerms": true,
"OwnerCount": 2,
"OwnersWithoutMFA": 1,
"RiskLevel": "CRITICAL"
}
]Cause: Filters are too restrictive or no service principals match.
Solution:
- Remove filters to see all service principals
- Check if filters are correct for your use case
- Verify you have permissions to read service principals
Cause: Insufficient Graph API permissions.
Solution:
# Disconnect and reconnect with proper scopes
Disconnect-MgGraph
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.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 service principals or throttling.
Solution:
# Use stealth mode to handle throttling
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -EnableStealth -MaxRetries 5
# Or reduce load with filtering
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -OnlyWithCredentialsCause: Missing Application.Read.All permission.
Solution:
- Ensure you have
Application.Read.Allpermission - This permission is required to read credential details
- Without it, credential analysis will be limited
# Identify all service principals with security issues
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -Matrix -ExportPath "sp-audit_$(Get-Date -Format 'yyyy-MM-dd').csv"Output: CSV file with all service principals, risk levels, and security findings.
# Find service principals with expired credentials
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -OnlyExpiredCredentials -MatrixUse Case: Identify immediate security risks requiring credential rotation.
# Find service principals with high-risk permissions
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -OnlyHighPermission -ExportPath "high-perm-sp.csv"Use Case: Review service principals with elevated permissions for compliance.
# Stealth mode scan using existing Azure CLI token
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -UseAzCliToken -EnableStealth -QuietStealth -ExportPath "targets.json"Use Case: Silent enumeration of high-value service principals during engagement.
# Monthly audit including disabled service principals
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -IncludeDisabled -Matrix -ExportPath "compliance_report.csv"
# Compare with previous month's reportUse Case: Track service principal security posture changes over time.
# Find service principals with credentials expiring soon
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.ps1 -OnlyWithCredentials -Matrix | Where-Object { $_.ExpiringSoonSecretsCount -gt 0 -or $_.ExpiringSoonCertificatesCount -gt 0 }Use Case: Proactive credential rotation planning.
# Schedule weekly scans
$scheduledScript = {
$date = Get-Date -Format "yyyy-MM-dd"
$path = "C:\SecurityAudits\ServicePrincipals_$date.csv"
C:\Tools\Invoke-EntraServicePrincipalCheck.ps1 -Matrix -ExportPath $path
# Send alert if critical-risk service principals 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 service principals 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\WeeklySPCheck.ps1"
Register-ScheduledTask -TaskName "Weekly Service Principal Audit" -Trigger $trigger -Action $action# Export JSON for SIEM ingestion
.\scripts\powershell\Invoke-EntraServicePrincipalCheck.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_service_principal_audit"
severity = $_.RiskLevel
service_principal = $_.DisplayName
app_id = $_.AppId
has_expired_credentials = ($_.ExpiredSecretsCount -gt 0 -or $_.ExpiredCertificatesCount -gt 0)
has_high_permissions = $_.HasHighRiskPerms
owners_without_mfa = $_.OwnersWithoutMFA
}
}
$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-EntraServicePrincipalCheck.ps1 -Matrix -ExportPath "C:\Reports\sp-audit.csv"
}
# Retrieve results
Copy-Item -FromSession $session -Path "C:\Reports\sp-audit.csv" -Destination ".\local_copy.csv"
Remove-PSSession $session- Initial implementation
- Service principal enumeration
- Credential detection and expiration tracking
- Permission analysis
- Owner MFA status checking
- Risk assessment framework
- Matrix view and export capabilities
- Stealth mode with configurable delays
- Multiple authentication methods
- Comprehensive security 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 access audit
- Invoke-EntraRoleCheck.ps1: Privileged role assignment audit