Generating report of all Access Package assignments in Azure AD Entitlement Management

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

2 thoughts on “Generating report of all Access Package assignments in Azure AD Entitlement Management

  1. 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?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s