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