Need a script to generate a report of all access package assignments in your tenants? Look no further.
The following scripts gets an access token from the Microsoft Graph Explorer, and queries your tenants for all access packages and users assigned to them.
<#
.Synopsis
Get graph explorer access token by sending the user to sign in and asking to copy url
#>
function Get-GraphExplorerAccessToken
{
[CmdletBinding()]
[Alias()]
[OutputType([string])]
Param
(
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[String[]] $Scopes = "User.Read.All"
)
$url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?nonce=graph_explorer&prompt=select_account&client_id=de8bc8b5-d9f9-48b1-a8ad-b748da725064&response_type=token&redirect_uri=https%3A%2F%2Fdeveloper.microsoft.com%2Fen-us%2Fgraph%2Fgraph-explorer&scope={0}" -f ($Scopes -join "+")
$url | Set-Clipboard
Write-Verbose "Please do the following:"
Write-Verbose ""
Write-Verbose "1. Go to the url on the clipboard"
Write-Verbose "2. Sign in with an account that has the required access"
Write-Verbose "3. Accept any consent (remember, this is the Microsoft Graph Explorer app, not a funky one)"
Write-Verbose "4. When back at the Graph Explorer, copy the url in the address bar (should now contain #access_token=...)"
# Loop and wait to find access token on the clipboard
$accessToken = $null
do {
$clipboardContent = Get-Clipboard
if($clipboardContent -like "*#access_token=*") {
$accessToken = ($clipboardContent -split "[#&]" | Where{$_ -like "access_token=*"}) -replace "access_token="
# Create base64 value of correct length (multiple of 4)
$base64 = $accessToken.Split(".")[1]
while(($base64.Length % 4) -ne 0) {
$base64 += "="
}
$accessTokenDecoded = [System.Text.Encoding]::UTF8.GetString(([System.Convert]::FromBase64String($base64))) | ConvertFrom-Json
$scopesInToken = $accessTokenDecoded.scp -split " "
$Scopes |
Where{$_ -notin $scopesInToken} |
Foreach{
Write-Warning "Token does not contain scope $($_)"
}
} else {
sleep -Milliseconds 100
}
} while(!$accessToken)
Write-Verbose "Access token found!"
return $accessToken
}
# Get access token and build rest param (parameters for Invoke-RestMethod)
$token = Get-GraphExplorerAccessToken -Verbose -Scopes "User.Read.All","EntitlementManagement.ReadWrite.All"
$restParams = @{Headers = @{Authorization = "Bearer $token"}}
# Endpoints
$graphBase = "https://graph.microsoft.com/beta"
$endpoints = @{
accessPackageCatalogs = "{0}/identityGovernance/entitlementManagement/accessPackageCatalogs" -f $graphBase
accessPackages = "{0}/identityGovernance/entitlementManagement/accessPackages?`$top=999" -f $graphBase
accessPackageAssignments = "{0}/identityGovernance/entitlementManagement/accessPackageAssignments?`$top=999" -f $graphBase
users = "{0}/users" -f $graphBase
groups = "{0}/groups" -f $graphBase
accessPackageAssignmentPolicies = "{0}/identityGovernance/entitlementManagement/accessPackageAssignmentPolicies?`$top" -f $graphBase
me = "{0}/me" -f $graphBase
directoryObjects = "{0}/directoryObjects" -f $graphBase
}
# Get all access packages (TODO: Add paging support ot support more than 999 access packages)
Write-Verbose "Getting all access packages..." -Verbose
$AccessPackages = Invoke-RestMethod $endpoints.accessPackages @restParams
if(($AccessPackages.value|measure).Count -gt 0) {
$AccessPackagesMap = $AccessPackages.value | Group-Object -AsHashTable -Property id -AsString
Write-Verbose "Found $($AccessPackagesMap.Count) access packages" -Verbose
} else {
Write-Error "No access packages found!" -ErrorAction Stop
}
# Get all access package assignment policies
Write-Verbose "Getting all access package assignment policies..." -Verbose
$AccessPackageAssignmentPolicies = Invoke-RestMethod $endpoints.accessPackageAssignmentPolicies @restParams
if(($AccessPackageAssignmentPolicies.value|measure).Count -gt 0) {
$AccessPackageAssignmentPoliciesMap = $AccessPackageAssignmentPolicies.value | Group-Object -AsHashTable -Property id -AsString
Write-Verbose "Found $($AccessPackageAssignmentPoliciesMap.Count) access package assignment policies" -Verbose
} else {
Write-Error "No access package assignment policies found!" -ErrorAction Stop
}
# Get all access package assignments
Write-Verbose "Getting all access package assignments..." -Verbose
$url = "https://graph.microsoft.com/beta/identityGovernance/entitlementManagement/accessPackageAssignments?`$expand=target"
$AccessPackageAssignments = New-Object System.Collections.ArrayList
while($url) {
$_Result = Invoke-RestMethod $url @restParams
$url = $_Result.'@odata.nextLink'
if($_Result.value) {
$AccessPackageAssignments.AddRange($_Result.value) | Out-Null
}
}
Write-Verbose "Found $($AccessPackageAssignments.Count) access package assignments" -Verbose
# Process into objects
Write-Verbose "Processing access package assignments" -Verbose
$Report = $AccessPackageAssignments | ForEach-Object {
[PSCustomObject] @{
AccessPackageId = $_.AccessPackageId
AccessPackageName = $AccessPackagesMap[$_.AccessPackageId].DisplayName
AssignmentPolicyId = $_.AssignmentPolicyId
AssignmentPolicy = $AccessPackageAssignmentPoliciesMap[$_.AssignmentPolicyId].DisplayName
AssignmentStatus = $_.AssignmentStatus
AssignmentState = $_.AssignmentState
IsExtended = $_.IsExtended
ExpiredDateTime = $_.ExpiredDateTime
Target = $_.Target.ObjectId
TargetDisplayName = $_.Target.DisplayName
TargetUPN = $_.Target.PrincipalName
}
}
$Report | Out-GridView
Install-Module ExportExcel -Scope CurrentUser
$Report | Export-Excel -Path ~\Desktop\report.xlsx -AutoSize -AutoFilter
This was really helpful thank you! One last piece I’m struggling with (after attempting to modify this script to include it) is being able to see what roles are assigned to the access package. The endpoint path for roles within an AP seems to be different. How might you include the roles assigned to the access package?
Hi, to report on the assigned roles per access package, you need to query the following API endpoint: https://docs.microsoft.com/en-us/graph/api/accesspackage-post-accesspackageresourcerolescopes?view=graph-rest-beta&tabs=http