Get-MgAllAppsByPermission.ps1

<#PSScriptInfo
 
.VERSION 0.6
 
.GUID 84a7dc33-b92a-4911-a0a3-f319bb65a059
 
.AUTHOR Daniel Bradley
 
.TAGS Microsoft Graph PowerShell Application Permissions
 
.RELEASENOTES
[Version 0.0.2] - Initial Release.
[Version 0.0.3] - Fixed dependant module install.
[Version 0.0.4] - Add ScopeType and orderd results.
[Version 0.0.5] - Removed placeholder permission.
[Version 0.0.6] - Added error handling.
#>


<#
 
.DESCRIPTION
 Finds all delegated and application permissions for applications in Entra by permission scope.
.SYNOPSIS
 Loops through all applications and filters for the defined permissions.
.EXAMPLE
    Get-MgAllAppsByPermission -Scope Mail.Send
 
#>
 

[CmdletBinding()]
param (
    [switch]$Version,
    [string]$Scope
)

# Script information
$CurrentVersion = '0.6'

if ($Version.IsPresent) {
    $CurrentVersion
    exit 0
}

#-------------Dependencies-------------#

$module = Import-Module microsoft.graph.authentication -PassThru -ErrorAction Ignore
if (-not $module) {
    Write-Host "Installing module microsoft.graph.authentication" -ForegroundColor Yellow
    Install-Module microsoft.graph.authentication -Scope CurrentUser -Force
}
Import-Module microsoft.graph.authentication

$module = Import-Module microsoft.graph.applications -PassThru -ErrorAction Ignore
if (-not $module) {
    Write-Host "Installing module microsoft.graph.applications" -ForegroundColor Yellow
    Install-Module microsoft.graph.applications -Scope CurrentUser -Force
}
Import-Module microsoft.graph.applications

$module = Import-Module Microsoft.Graph.Identity.SignIns -PassThru -ErrorAction Ignore
if (-not $module) {
    Write-Host "Installing module microsoft.graph.Identity.SignIns" -ForegroundColor Yellow
    Install-Module Microsoft.Graph.Identity.SignIns -Scope CurrentUser -Force
}
Import-Module Microsoft.Graph.Identity.SignIns

#-------------End Dependencies-------------#

#----------------Functions----------------#

function Get-AllAppsByPermission {

    <#
        .SYNOPSIS
        Retrieves all apps in Microsoft Entra by defined permission.
  
        .DESCRIPTION
        This function queries both delegated permissions and application (app roles) permissions on applications in Microsoft Entra. Then filters locally for only those with the defined permission.
  
        .EXAMPLE
        Get-AllAppsByPermission -Scope Mail.Send
  
        This example will find all applications with Mail.Send permission consented.
 
        .NOTES
        Credit to Luca Spolidoro @ Microsoft for sharing some core functionality of this script
    #>


    [CmdletBinding()]
    Param(
        $Permission
    )

    #Check current context
    If((Get-MgContext).Scopes -notcontains "Application.Read.All"){
        Write-host "Connecting to Microsoft Graph..."
        Connect-MgGraph -Scopes Application.Read.All, DelegatedPermissionGrant.Read.All
    }

    #The the Graph Service Principal ID
    $graphSp = Get-MgServicePrincipalByAppId -AppId "00000003-0000-0000-c000-000000000000" -Property "id,appRoles"

    #Get all OAuth2 delegated Graph permissions for all apps
    $delegatedPermissions = Get-MgOauth2PermissionGrant -All -Top 999 -CountVariable CountVar -Property "clientId,scope" -Filter "ConsentType eq 'AllPrincipals' and resourceId eq '$($graphSp.Id)'" -Headers @{ 'ConsistencyLevel' = 'Eventual' } | Select-Object @{Name='ServicePrincipalId';Expression={$_.ClientId}}, @{Name='ScopeType';Expression={"Delegated"}}, @{Name='Scope'; Expression={$_.Scope -split ' ' -ne '' }}

    #Get all possible available app roles
    $graphAppRoles = $graphSp | Select-Object -ExpandProperty AppRoles | Select-Object Id, Value | Group-Object -Property Id -AsHashTable

    #Get all app role assignments for all apps
    $spRoleAssignments = Get-MgServicePrincipal -All -PageSize 999 -ExpandProperty "appRoleAssignments" -Property "id,appId,displayName" | Where-Object { ($_.AppRoleAssignments | Where-Object { $_.ResourceId -eq $graphSp.Id }).Count -gt 0 } | Select-Object Id, AppId, DisplayName, @{Name="AppRoleId"; Expression={ ($_.AppRoleAssignments).AppRoleId }}

    #Expand permissions for all app role assignments
    $appPermissions = $spRoleAssignments | ForEach-Object { [PSCustomObject]@{ ServicePrincipalId = $_.Id; ScopeType="Application"; Scope = @($graphAppRoles[$_.AppRoleId].Value)}}

    #Filter all permissions by selected permission
    $filteredPermissions = $delegatedPermissions + $appPermissions | Where-Object Scope -CEQ $Permission

    #Build a filter collection of AppIds from the above
    $filterString = "id in('" + (($filteredPermissions | Select-Object -ExpandProperty ServicePrincipalId) -join "','") + "')"

    #Work backwards and find all apps by app ID from the permissions list above.
    $filteredAppsWithName = Get-MgServicePrincipal -Filter $filterString -Select "id,appId,displayName" -erroraction SilentlyContinue | Select-Object Id, AppId, DisplayName

    $Report = [System.Collections.Generic.List[Object]]::new()
    ForEach ($app in $filteredAppsWithName){
        $obj = [PSCustomObject][ordered]@{
            "DisplayName" = $app.DisplayName
            "AppId" = $app.AppId
            "ID" = $app.Id
            "ScopeType" = $filteredPermissions | Where {$_.ServicePrincipalId -eq $app.Id} | Select -ExpandProperty ScopeType
        }
        $report.Add($obj)
    }

    $Report | Sort -Descending ScopeType

    Write-Host "Found $($Report.count) apps with permission: $Permission `n" -ForegroundColor Cyan

    If ($Report.count -eq 0){
        Write-Host "Permissions are case senstive, please ensure letter capitalisation is correct `n" -ForegroundColor Yellow
    }

    $return
}

#----------------End Functions----------------#

Get-AllAppsByPermission -Permission $Scope