In Microsoft Entra ID, one of the most critical security risks arises when a service principal:
This combination creates a dangerous scenario because:
đ These are often referred to as orphaned high-privilege identities, and they should be prioritized during security audits.
This script helps administrators identify custom Entra service principals that have high-risk permissions and no owners, enabling immediate remediation.
Try the M365Corner Microsoft 365 Reporting Tool â your DIY pack with 20+ out-of-the-box M365 reports for Users, Groups, and Teams.
# Connect to Microsoft Graph
Connect-MgGraph -Scopes Application.Read.All, Directory.Read.All
Write-Host "Scanning CUSTOM SPs with HIGH-RISK permissions AND NO owners..." -ForegroundColor Cyan
# High-risk permissions list
$HighRiskPermissions = @(
"Directory.ReadWrite.All",
"User.ReadWrite.All",
"Application.ReadWrite.All",
"RoleManagement.ReadWrite.Directory",
"Group.ReadWrite.All",
"AppRoleAssignment.ReadWrite.All",
"Directory.Read.All",
"User.Read.All",
"Group.Read.All"
)
# Get all service principals
$ServicePrincipals = Get-MgServicePrincipal -All -Property Id,DisplayName,AppId,Tags
# Cache for resource SPs
$ResourceSPCache = @{}
$Results = @()
foreach ($SP in $ServicePrincipals) {
# -------------------------
# Exclude Microsoft-managed SPs
# -------------------------
if (
$SP.Tags -contains "WindowsAzureActiveDirectoryIntegratedApp" -or
$SP.Tags -contains "MicrosoftApplication"
) {
continue
}
# -------------------------
# Check Owners FIRST (early exit optimization)
# -------------------------
$Owners = Get-MgServicePrincipalOwner -ServicePrincipalId $SP.Id -ErrorAction SilentlyContinue
if ($Owners -and $Owners.Count -gt 0) {
continue
}
# -------------------------
# Get App Role Assignments
# -------------------------
$Assignments = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $SP.Id -All -ErrorAction SilentlyContinue
if (-not $Assignments) {
continue
}
$MatchedPermissions = @()
foreach ($Assignment in $Assignments) {
# Cache resource SP
if (-not $ResourceSPCache.ContainsKey($Assignment.ResourceId)) {
$ResourceSPCache[$Assignment.ResourceId] =
Get-MgServicePrincipal -ServicePrincipalId $Assignment.ResourceId -Property AppRoles
}
$ResourceSP = $ResourceSPCache[$Assignment.ResourceId]
$Role = $ResourceSP.AppRoles | Where-Object {
$_.Id -eq $Assignment.AppRoleId
}
if ($Role -and $HighRiskPermissions -contains $Role.Value) {
$MatchedPermissions += $Role.Value
}
}
# Remove duplicates
$MatchedPermissions = $MatchedPermissions | Sort-Object -Unique
if ($MatchedPermissions.Count -gt 0) {
# Console output (minimal)
Write-Host "$($SP.DisplayName) | $($SP.AppId)" -ForegroundColor Red
# Export object
$Results += [PSCustomObject]@{
DisplayName = $SP.DisplayName
ServicePrincipalId = $SP.Id
AppId = $SP.AppId
OwnerStatus = "No Owner Assigned"
HighRiskPermissions = ($MatchedPermissions -join ", ")
PermissionCount = $MatchedPermissions.Count
RiskLevel = "Critical"
AppType = "Custom / Non-Microsoft"
}
}
}
# Export results
$ExportPath = "D:\SP_HighRisk_NoOwners_Report.csv"
$Results | Export-Csv $ExportPath -NoTypeInformation
Write-Host "Critical risk report exported to $ExportPath" -ForegroundColor Cyan
| Step | Description |
|---|---|
| Connect to Graph | Authenticates using Application.Read.All and Directory.Read.All |
| Define High-Risk Permissions | Stores a predefined list of sensitive permissions |
| Fetch Service Principals | Retrieves all service principals |
| Exclude Microsoft Apps | Skips Microsoft-managed service principals using tags |
| Check Owners | Filters only service principals with no owners |
| Get App Role Assignments | Retrieves assigned API permissions |
| Cache Resource SPs | Improves performance by caching resource service principals |
| Resolve Permission Names | Matches AppRoleId to permission names |
| Identify High-Risk Matches | Filters only high-risk permissions |
| Build Report | Stores details including permission count and risk level |
| Export Results | Exports critical-risk identities to CSV |
| Enhancement | Description |
|---|---|
| Include Sign-In Logs | Identify whether the SP is actively used |
| Add Owner Remediation | Automatically assign owners |
| Include Created Date | Track how old the identity is |
| Risk Prioritization | Rank based on number of high-risk permissions |
| Alert Integration | Send alerts for critical findings |
| Question | Answer |
|---|---|
| Why is this scenario critical? | High permissions + no owner = no accountability and high risk |
| What are high-risk permissions? | Permissions that allow broad access or modification of directory data |
| Why exclude Microsoft apps? | They are system-managed and generally safe |
| Can a service principal have multiple high-risk permissions? | Yes, increasing the risk level |
| Should these be deleted immediately? | No, validate usage before taking action |
| Use Case | Description |
|---|---|
| Security Audit | Identify critical-risk service principals |
| Governance Review | Ensure ownership and permission control |
| Risk Detection | Detect orphaned high-privilege identities |
| Compliance Reporting | Meet regulatory and audit requirements |
| Incident Response | Quickly identify potential attack vectors |
| Error | Cause | Solution |
|---|---|---|
| Insufficient privileges | Missing Graph permissions | Use Application.Read.All and Directory.Read.All |
| Cmdlet not recognized | Graph module not installed | Install using Install-Module Microsoft.Graph |
| Access token expired | Session timeout | Reconnect using Connect-MgGraph |
| Slow execution | Large tenant or API calls | Use caching (already implemented) |
Service principals with high-risk permissions and no owners represent one of the most dangerous security gaps in Microsoft Entra ID. These identities combine elevated access with zero accountability, making them prime targets for misuse.
This Microsoft Graph PowerShell script provides a powerful way to identify such critical-risk identities and export them for immediate review. By regularly auditing and remediating these service principals, administrators can:
Incorporating this script into your security workflows ensures better visibility, control, and protection across your Entra environment.
© Created and Maintained by LEARNIT WELL SOLUTIONS. All Rights Reserved.