Skip to content

Instantly share code, notes, and snippets.

@JustinGrote
Last active November 6, 2024 10:21
Show Gist options
  • Save JustinGrote/656a01c1e047df940d577698ba62e3f2 to your computer and use it in GitHub Desktop.
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
#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
}
}
}
}
}
@JustinGrote
Copy link
Author

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.
image

@smcallister594
Copy link

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