By default, users in your organisation can authorise applications to access data about themselves in Microsoft Entra. Not only that, by default, users can also authorise applications to act on their behalf, such as create Microsoft Teams and Send emails as said user.
In 99% of scenarios, this requirement from an application is not required unless the application was created for a very custom workflow, in which admins should still be configured to grant the consent manually (or, again, through a specific workflow).
As an administrator, you should limit what permissions users can self-grant applications access to. This should still be carefully reviewed and aligned with the application’s requirements. While that is covered in separate posts of mine, in this post, I will show you how you can generate reports of user-consented permissions to applications in your tenant and then export this to a CSV.
Requirements
This tutorial utilises the Microsoft Graph PowerShell SDK. If you don’t have it installed, or it has been a while since you installed it, check out my post to guide you through installing and updating the modules. How to install the Microsoft Graph PowerShell SDK.
You will also need access to a Global Administrator account in your tenant. This is so you can grant the initiate tenant-wide permissions to the Microsoft Graph Command Line Tools application.
The user-consented permissions report script
You can copy the below script and modify it to your liking, or you can run it how it is and generate a report.
The script utilises batching requests to improve the performance of checking permissions on almost 500 service principals in Microsoft Entra. It will then filter out any permission grants that are consented to at the user level.
As this user grant information does not contain the user’s username or application name, the script will loop through each object ID to obtain the meaningful name and add them to a report named $final.
You will then be prompted to choose where the report will be exported and saved.
Find this script on my public GitHub repository.
<#
.SYNOPSIS
Generates a report of user-consented permissions to application in Microsoft Entra
.LINK
https://ourcloudnetwork.com
https://www.linkedin.com/in/danielbradley2/
.NOTES
Version: 0.1
Author: Daniel Bradley
Creation Date: Monday, January 8th 2024, 6:54:33 am
File: ApplicationUserConsentReport.ps1
Copyright (c) 2024 Your Company
HISTORY:
2024-01-08 File Generated
.INPUTS
.OUTPUTS
.COMPONENT
Required Modules:
Microsoft.Graph.Applications
Microsoft.Graph.Users
.EXAMPLE
.\ApplicationUserConsentReport.ps1
.LICENSE
Use this code free of charge at your own risk.
Never deploy code into production if you do not know what it does.
#>
#Connect to graph
Connect-MgGraph -Scopes Application.Read.All, user.read.all
#Define output file name
$filename = "User_Permissions_Grant_Report.csv"
#Function will launch Windows explorer to select a folder
Function SelectFolder {
$global:folderselection = New-Object System.Windows.Forms.OpenFileDialog -Property @{
InitialDirectory = [Environment]::GetFolderPath('Desktop')
CheckFileExists = 0
ValidateNames = 0
FileName = $filename
}
$folderselection.ShowDialog() | Out-Null
}
#Gets all service principals with limited info
Write-host "Gathering service principals.." -ForegroundColor Yellow
$AllServicePrincipals = Get-MgServicePrincipal -All | Select Id, appid, DisplayName
#Send batch requests to gather all user grants, due to large number of service principals
Write-host "Indentifying user grants..." -ForegroundColor Yellow
$Report = @()
for($i=0;$i -lt $AllServicePrincipals.count;$i+=20){
$batch = @{}
$batch['requests'] = ($AllServicePrincipals[$i..($i+19)] | select @{n='id';e={$_.id}},@{n='method';e={'GET'}},`
@{n='url';e={"/servicePrincipals/$($_.id)/oauth2PermissionGrants"}})
$response = invoke-mggraphrequest -Method POST -URI "https://graph.microsoft.com/v1.0/`$batch" -body ($batch | convertto-json) -OutputType PSObject
$Report += $response.responses
}
$UserGrants = $Report.body.value | Where {$_.ConsentType -eq "Principal"}
#Uses loop function to gather additional information such as the userprincipalname. It also correlates the service principal name.
Write-host "correlating information..." -ForegroundColor Yellow
$final = [System.Collections.Generic.List[Object]]::new()
Foreach ($Grant in $UserGrants){
$user = $null
$app = $null
$user = Get-MgUser -UserId $Grant.principalId | Select UserPrincipalName
$app = $AllServicePrincipals | Where {$_.id -eq $($grant.clientid)}
$obj = [PSCustomObject][ordered]@{
"Application" = $app.DisplayName
"user" = $user.UserPrincipalName
"permission" = ($grant.scope.Trim() -replace " ",", ")
}
$final.Add($obj)
}
#Run select folder function
Write-host "Please select a folder `n" -ForegroundColor Green
Start-Sleep -Seconds 2
SelectFolder
#Export file
$final | Export-CSV -Path $folderselection.FileName -NoTypeInformation
Write-host "File saved! Click Enter to close... `n" -ForegroundColor Green
Write-host " Script by >> Daniel Bradley " -ForegroundColor cyan -BackgroundColor black
Write-host " Visit >> ourcloudnetwork.com " -ForegroundColor cyan -BackgroundColor black
Write-host " Connect >> https://www.linkedin.com/in/danielbradley2/ " -ForegroundColor cyan -BackgroundColor black
Write-host "`n"
Pause
Once the script has run, you will receive a report like the below.
Wrapping up
It is important to take back control of applications in your environment. Even after you have prevented users from creating applications in your tenant and restricted application user consent, there are likely some permissions lurking on the applications, which this script will help you find!