Microsoft Entra ID · PowerShell 7.6
Delegated permission grants (user and admin consent)
Which apps can call which APIs on behalf of signed-in users, with which scopes, and was that consented for all users by an admin or by individual users?
Script
#Requires -Modules Microsoft.Graph.Authentication, Microsoft.Graph.Identity.SignIns, Microsoft.Graph.Applications, Microsoft.Graph.Users
# Delegated permission grants: which apps can act for users, on which APIs, with which scopes. Read-only.
Connect-MgGraph -Scopes 'Directory.Read.All' -NoWelcome
$servicePrincipals = @{}
$users = @{}
function Get-CachedServicePrincipalName {
param([string]$Id)
if (-not $servicePrincipals.ContainsKey($Id)) {
$servicePrincipals[$Id] = (Get-MgServicePrincipal -ServicePrincipalId $Id -Property 'displayName').DisplayName
}
return $servicePrincipals[$Id]
}
function Get-CachedUserPrincipalName {
param([string]$Id)
if (-not $users.ContainsKey($Id)) {
$user = Get-MgUser -UserId $Id -Property 'userPrincipalName' -ErrorAction SilentlyContinue
$users[$Id] = if ($user) { $user.UserPrincipalName } else { $Id }
}
return $users[$Id]
}
Get-MgOauth2PermissionGrant -All |
ForEach-Object {
[pscustomobject]@{
Application = Get-CachedServicePrincipalName -Id $_.ClientId
Api = Get-CachedServicePrincipalName -Id $_.ResourceId
ConsentType = $_.ConsentType
User = if ($_.ConsentType -eq 'Principal') { Get-CachedUserPrincipalName -Id $_.PrincipalId } else { 'All users' }
Scopes = "$($_.Scope)".Trim()
}
} |
Sort-Object Application, Api, ConsentType, User
Checked automatically against Microsoft's published cmdlet reference. KBY hasn't run it in your tenant; run it in a test environment first.
Reading the output
- Directory.Read.All is the least privileged permission for listing oAuth2PermissionGrants. It also covers reading the service principal and user names the script looks up.
- ConsentType AllPrincipals means an administrator consented for every user; Principal means one user consented for themselves, and User shows who.
- Scopes is the space-separated scope string from the grant, for example "openid User.Read Mail.Read".
- Makes one lookup per distinct app, API and user, cached, so large tenants with many user consents take a while.
Sources
The Microsoft reference page for every cmdlet the script calls, plus the API and product pages behind each permission and caveat.
Connect-MgGraph (Microsoft.Graph.Authentication)Get-MgOauth2PermissionGrant (Microsoft.Graph.Identity.SignIns)Get-MgServicePrincipal (Microsoft.Graph.Applications)Get-MgUser (Microsoft.Graph.Users)List oAuth2PermissionGrants: permissionsoAuth2PermissionGrant resource type: clientId, consentType, principalId, resourceId, scopeGet servicePrincipal: permissionsGet user: permissionsForEach-Object (Microsoft.PowerShell.Core)Sort-Object (Microsoft.PowerShell.Utility)