Entra / Microsoft 365 · Applications
Report delegated permissions
A script to demonstrate how to report delegated permissions held by Entra ID user accounts.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -NoWelcome -Scopes Directory.Read.All
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
Connect-MgGraph -NoWelcome -Scopes Directory.Read.All# Find licensed users[array]$Users = Get-MgUser -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" `-ConsistencyLevel eventual -CountVariable Records -All -Sort displayNameWrite-Host ("{0} licensed user accounts found" -f $Users.count)If (!($Users)) {Write-Host "No licensed users found - exiting!"; break}# Define file name for CSV output$CSVOutputFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\DelegatedPermissions.csv"# Scopes to ignore for reporting purposes[array]$IgnoredScopes = "openid", "profile", "offline_access"# Create hash tables for lookup of client and resource names$ClientIds = @{}$ResourceIds = @{}$Report = [System.Collections.Generic.List[Object]]::new()ForEach ($User in $Users) {Write-Host ("Checking delegated permissions for {0}" -f $User.UserPrincipalName)[array]$Permissions = Get-MgUserOauth2PermissionGrant -UserId $User.Id -AllForEach ($Permission in $Permissions) {# Try to look up client and resource names in the hash tables. If we find an entry, use the# display name, else run Get-MgServicePrincipal to find the display name and store it$ClientDisplayName = $ClientIds[$Permission.ClientId]If ($null -eq $ClientDisplayName) {$ClientDisplayName = (Get-MgServicePrincipal -ServicePrincipalId $Permission.ClientId).displayName$ClientIds.Add($Permission.ClientId, $ClientDisplayName)}$ResourceDisplayName = $ResourceIds[$Permission.ResourceId]If ($null -eq $ResourceDisplayName) {$ResourceDisplayName = (Get-MgServicePrincipal -ServicePrincipalId $Permission.ResourceId).displayName$ResourceIds.Add($Permission.ResourceId, $ResourceDisplayName)}# Find the set of assigned scopes, ignoring some of the common scopes[array]$Scopes = $Permission.scope.Split(" ")[array]$FoundScopes = $nullForEach ($Scope in $Scopes) {If ($Scope -in $IgnoredScopes -or [string]::isNullOrWhiteSpace($Scope)) {Continue}$FoundScopes += $Scope}# Generate the output$ReportLine = [PSCustomObject][Ordered]@{'Consent type' = $Permission.consentTypeUserPrincipalName = $User.UserPrincipalNameClient = $ClientDisplayNameResource = $ResourceDisplayNameScopes = $FoundScopes -join ", "ClientId = $Permission.ClientId}$Report.Add($ReportLine)}}# Now handle the AppPrincipals delegated permissionsWrite-Host "Checking delegated permissions for all user accounts (AllPrincipals)"[array]$AllPermissions = Get-MgOauth2PermissionGrant -filter "consentType eq 'AllPrincipals'" -AllWrite-Host ("{0} delegated permissions found for all user accounts" -f $AllPermissions.count)[int]$i = 0ForEach ($AllPermission in $AllPermissions) {$i++# Try to look up client and resource names in the hash tables. If we find an entry, use the# display name, else run Get-MgServicePrincipal to find the display name and store it$ClientDisplayName = $ClientIds[$AllPermission.ClientId]If ($null -eq $ClientDisplayName) {$ClientDisplayName = (Get-MgServicePrincipal -ServicePrincipalId $AllPermission.ClientId).displayName$ClientIds.Add($AllPermission.ClientId, $ClientDisplayName)}Write-Host ("Checking permission for client {0} ({1}/{2})" -f $ClientDisplayName, $i, $AllPermissions.count)$ResourceDisplayName = $ResourceIds[$AllPermission.ResourceId]If ($null -eq $ResourceDisplayName) {$ResourceDisplayName = (Get-MgServicePrincipal -ServicePrincipalId $AllPermission.ResourceId).displayName$ResourceIds.Add($AllPermission.ResourceId, $ResourceDisplayName)}# Find the set of assigned scopes, ignoring some of the common scopes[array]$Scopes = $AllPermission.scope.Split(" ")[array]$FoundScopes = $nullForEach ($Scope in $Scopes) {If ($Scope -in $IgnoredScopes -or [string]::isNullOrWhiteSpace($Scope)) {Continue}$FoundScopes += $Scope}# Generate the output$ReportLine = [PSCustomObject][Ordered]@{'Consent type' = $AllPermission.consentTypeUserPrincipalName = "All User Accounts"Client = $ClientDisplayNameResource = $ResourceDisplayNameScopes = $FoundScopes -join ", "ClientId = $AllPermission.ClientId}$Report.Add($ReportLine)}$Report | Out-GridView -Title "Delegated Permissions Report"$Report | Export-Csv -Path $CSVOutputFile -NoTypeInformation -Encoding UTF8Write-Host ("CSV output file written to {0}" -f $CSVOutputFile)
Attribution
Author
Office365itpros