Last active
November 6, 2024 10:21
-
-
Save JustinGrote/656a01c1e047df940d577698ba62e3f2 to your computer and use it in GitHub Desktop.
Get a list of application and delegated permissions for a service principal, similar to what the Azure Portal shows
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#requires -version 7 -module Microsoft.Graph.Applications | |
using namespace Microsoft.Graph.PowerShell.Models | |
using namespace System.Collections.Generic | |
enum MicrosoftGraphServicePrincipalType { | |
Application | |
Delegated | |
} | |
class MgServicePrincipalPermission { | |
[MicrosoftGraphServicePrincipal]$ServicePrincipal | |
[string]$Id | |
[MicrosoftGraphServicePrincipalType]$Type | |
[string]$User | |
[string]$ResourceName | |
[string]$Permission | |
[string]$PermissionDisplayName | |
[string]$Description | |
[Nullable[DateTime]]$CreatedDateTime | |
} | |
Update-TypeData -TypeName 'MgServicePrincipalPermission' -DefaultDisplayPropertySet 'ServicePrincipal', 'ResourceName', 'User', 'Permission' -Force | Out-Null | |
function Get-MgServicePrincipalPermission { | |
<# | |
.SYNOPSIS | |
Retrieves the permissions assigned to a service principal, providing a similar output to what is on the Azure Portal Screen. | |
.EXAMPLE | |
Get-MgServicePrincipal -Search 'displayname:MyServicePrincipal' -Con eventual -Cou count | Get-MgServicePrincipalPermission | |
#> | |
[CmdletBinding(DefaultParameterSetName = 'Object')] | |
param( | |
[Parameter(ParameterSetName = 'Id', Position = 0, Mandatory, ValueFromPipelineByPropertyName)] | |
[Alias('Id')] | |
[string]$ServicePrincipalId, | |
[Parameter(ParameterSetName = 'Object', Position = 0, Mandatory, ValueFromPipeline)] | |
[Microsoft.Graph.PowerShell.Models.MicrosoftGraphServicePrincipal]$ServicePrincipal | |
) | |
begin { | |
#We use this to cache app info for permission lookups | |
[Dictionary[string, MicrosoftGraphServicePrincipal]]$spCache = @{} | |
} | |
process { | |
$ErrorActionPreference = 'Stop' | |
$ServicePrincipal ??= Get-MgServicePrincipal -ServicePrincipalId $ServicePrincipalId | |
#Add a ToString to the serviceprincipal so it summarizes correctly in the formatting | |
$ServicePrincipal | Add-Member -MemberType ScriptMethod -Name ToString -Value { $this.DisplayName } -Force | |
#When using Mandatory above, it becomes an empty string instead of null, so null conditional cannot be used. | |
if ([string]::IsNullOrEmpty($ServicePrincipalId)) { | |
$ServicePrincipalId = $ServicePrincipal.Id | |
} | |
$appPermissions = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ServicePrincipalId | |
| Where-Object DeletedDateTime -EQ $null | |
foreach ($app in $appPermissions) { | |
$spCache[$app.ResourceId] ??= Get-MgServicePrincipal -ServicePrincipalId $app.ResourceId | |
[MicrosoftGraphAppRole]$role = $spCache[$app.ResourceId].AppRoles | |
| Where-Object Id -EQ $app.AppRoleId | |
if (-not $Role) { throw "No matching permission found for AppRoleID $($app.AppRoleId). This is a bug" } | |
[MgServicePrincipalPermission]@{ | |
ServicePrincipal = $ServicePrincipal | |
Id = $app.Id | |
Type = 'Application' | |
User = '[Application]' | |
ResourceName = $app.ResourceDisplayName | |
Permission = $role.Value | |
PermissionDisplayName = $role.DisplayName | |
Description = $role.Description | |
CreatedDateTime = $app.CreatedDateTime | |
} | |
} | |
$delegatedPermissions = Get-MgServicePrincipalOauth2PermissionGrant -ServicePrincipalId $ServicePrincipalId | |
foreach ($permission in $delegatedPermissions) { | |
if (-not $spCache[$permission.ResourceId]) { | |
$spCache[$permission.ResourceId] = Get-MgServicePrincipal -ServicePrincipalId $permission.ResourceId | |
| Add-Member -MemberType ScriptMethod -Name ToString -Value { $this.DisplayName } -Force -PassThru | |
} | |
$resource = $spCache[$permission.ResourceId] | |
foreach ($scope in $permission.Scope.split(' ')) { | |
$role = $resource.AppRoles | Where-Object Value -EQ $scope | |
[MgServicePrincipalPermission]@{ | |
ServicePrincipal = $ServicePrincipal | |
Id = $scope.Id | |
Type = 'Delegated' | |
User = $permission.ConsentType -eq 'AllPrincipals' ? 'All' : $permission.PrincipalId | |
ResourceName = $resource.DisplayName | |
Permission = $scope | |
PermissionDisplayName = $role.DisplayName | |
Description = $role.Description | |
CreatedDateTime = $null | |
} | |
} | |
} | |
} | |
} |
Thanks @JustinGrote :)) Saved me reinventing the wheel here. Modified it a bit to list all the permissions in a gridview so I can select and delete Application permissions.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I've updated the script to include the original service principal as a reference while still reporting the name, and use a custom type for the permissions for better formatting.