Customers should really start paying attention to their Azure AD application consents, which can be daunting. There are obviously tools for this, even provided by Microsoft through Cloud App Security (CAS), but you know – why not simply dump out everything in Excel and parse through it?
This blog post contains a simple PowerShell script that does exactly this.
Start by signing into the Graph Explorer as a global admin, and when you are there, copy the url. Then start the below PowerShell, which will monitor the clipboard in order to find a graph explorer url with an access token, and then use that to access the Microsoft Graph. Of course, you can do this different ways, but this is very handy for quick and dirty scripting.
function Get-AccessTokenFromGraphExplorerUrlOnClipboard
{
[CmdletBinding()]
[Alias()]
Param
()
Process
{
$first = $true
do {
if(!$first) {
Sleep -Seconds 1
}
$first = $false
Write-Verbose "Trying to get Graph Explorer URL from clipboard"
$url = Get-Clipboard
if($url -ne $null -and $url.StartsWith("https://developer.microsoft.com/en-us/graph/graph-explorer#access_token=")) {
$token = $url -split "[=&]" | Select -Index 1
}
} while($token -eq $null -or !$token.StartsWith("ey"))
$token
}
}
$token = Get-AccessTokenFromGraphExplorerUrlOnClipboard -Verbose
$headers = @{Authorization = "Bearer $token"}
$VerbosePreference = "Continue"
# Get all Oauth2 permission grants from the tenant
$oauth2PermissionGrants = @()
$url = "https://graph.microsoft.com/beta/oauth2PermissionGrants?`$top=999"
do {
$result = Invoke-RestMethod $url -Headers $headers -Verbose
$oauth2PermissionGrants += $result.value
$url = $result.'@odata.nextLink'
} while ($url)
$oauth2PermissionGrants | Out-GridView
# Fetch all related directory objects (applications, servicePrincipals, users etc)
$directoryObjects = @{}
$oauth2PermissionGrants | ForEach-Object {
$oauth2PermissionGrant = $_ # $oauth2PermissionGrant = $oauth2PermissionGrants[0]
if($oauth2PermissionGrant.clientId -and !$directoryObjects.ContainsKey($oauth2PermissionGrant.clientId)) {
$directoryObjects[$oauth2PermissionGrant.clientId] = Invoke-RestMethod "https://graph.microsoft.com/beta/directoryObjects/$($oauth2PermissionGrant.clientId)" -Headers $headers
}
if($oauth2PermissionGrant.principalId -and !$directoryObjects.ContainsKey($oauth2PermissionGrant.principalId)) {
$directoryObjects[$oauth2PermissionGrant.principalId] = Invoke-RestMethod "https://graph.microsoft.com/beta/directoryObjects/$($oauth2PermissionGrant.principalId)" -Headers $headers
}
if($oauth2PermissionGrant.resourceId -and !$directoryObjects.ContainsKey($oauth2PermissionGrant.resourceId)) {
$directoryObjects[$oauth2PermissionGrant.resourceId] = Invoke-RestMethod "https://graph.microsoft.com/beta/directoryObjects/$($oauth2PermissionGrant.resourceId)" -Headers $headers
}
}
$oauth2PermissionGrantsParsed = $oauth2PermissionGrants | ForEach-Object {
$oauth2PermissionGrant = $_ # $oauth2PermissionGrant = $oauth2PermissionGrants[0]
$obj = [ordered] @{
clientId = $oauth2PermissionGrant.clientId
clientDisplayName = $null
clientType = $null
consentType = $oauth2PermissionGrant.consentType
expiryTime = $oauth2PermissionGrant.expiryTime
startTime = $oauth2PermissionGrant.startTime
resourceId = $oauth2PermissionGrant.resourceId
resourceType = $null
resourceDisplayName = $null
principalId = $oauth2PermissionGrant.principalId
principalDisplayName = $null
principalType = $null
scope = $oauth2PermissionGrant.scope
}
if($oauth2PermissionGrant.clientId) {
$obj["clientDisplayName"] = $directoryObjects[$oauth2PermissionGrant.clientId].displayName
$obj["clientType"] = $directoryObjects[$oauth2PermissionGrant.clientId].'@odata.type'
}
if($oauth2PermissionGrant.resourceId) {
$obj["resourceDisplayName"] = $directoryObjects[$oauth2PermissionGrant.resourceId].displayName
$obj["resourceType"] = $directoryObjects[$oauth2PermissionGrant.resourceId].'@odata.type'
}
if($oauth2PermissionGrant.principalId) {
$obj["principalDisplayName"] = $directoryObjects[$oauth2PermissionGrant.principalId].displayName
$obj["principalType"] = $directoryObjects[$oauth2PermissionGrant.principalId].'@odata.type'
}
[PSCustomObject] $obj
}
# Create a one row per scope version
$attributes = "clientId", "clientDisplayName", "clientType", "consentType", "expiryTime", "startTime", "resourceId", "resourceType", "resourceDisplayName", "principalId", "principalDisplayName", "principalType"
$oauth2PermissionGrantsParsedSingle = $oauth2PermissionGrantsParsed | ForEach-Object {
$obj = $_
$obj.scope -split " " | ForEach-Object {
$obj2 = [ordered] @{scope = $_}
$attributes | ForEach-Object {$obj2[$_] = $obj.$_}
[PSCustomObject] $obj2
}
}
# Output excel file
# If error on Export-Excel missing: Install-Module -Scope CurrentUser ImportExcel
$file = "~\Desktop\OAuth2 $([guid]::NewGuid()).xlsx"
$oauth2PermissionGrantsParsed | Export-Excel -Path $file -WorksheetName "Combined" -AutoSize -AutoFilter
$oauth2PermissionGrantsParsedSingle | Export-Excel -Path $file -WorksheetName "Single" -AutoSize -AutoFilter
ii $file