Generating demo Access Packages for AAD Entitlement Management through the Microsoft Graph

Some times it can be handy to be able to generate some demo content, and have some reference PowerShell for working with stuff. Here is my script for creating 5 access packages with different properties:

  • A visible package available for any external user
  • A hidden package available for any external user, requiring the user to know the url
  • A package available to external users in connected organizations
  • A package available for members of an internal group
  • A package available to any internal user, with manager approval and self review

To use the script:

  1. Sign into the Microsoft Graph Explorer and add the scope ‘EntitlementManagement.ReadWrite.All’
  2. Run the script
  3. Paste the url in your browser, including the #access_token
#
# Configuration
#

$accessPackageCatalogName = "Demo"
$accessPackages = @(
    @{displayName = "Visible to anyone"}
    @{displayName = "Visible to connected organizations"}
    @{displayName = "Hidden, available for anyone through url"}
    @{displayName = "Only available to a random internal group"}
    @{displayName = "Available to any internal user"}
)

#
# Should not need to edit below this line
#



#
# Get access token
#


$graphExplorerUrl = Read-host -Prompt "Paste graph explorer url with #access_token in it"
$accessToken = $graphExplorerUrl -split "[#?&]" -like "access_token*" -replace "access_token="

$restParams = @{
    Headers = @{
        Authorization = "Bearer $accessToken"
    }
}


#
# Constants
#

$graphBase = "https://graph.microsoft.com/beta"
$endpoints = @{
    accessPackageCatalogs = "{0}/identityGovernance/entitlementManagement/accessPackageCatalogs" -f $graphBase
    accessPackages = "{0}/identityGovernance/entitlementManagement/accessPackages" -f $graphBase
    users = "{0}/users" -f $graphBase
    groups = "{0}/groups" -f $graphBase
    accessPackageAssignmentPolicies = "{0}/identityGovernance/entitlementManagement/accessPackageAssignmentPolicies" -f $graphBase
    me = "{0}/me" -f $graphBase
}


$VerbosePreference = "SilentlyContinue"

#
# STEP 1 - Ensure access package catalog is created
#
Write-Verbose "STEP 1 - Ensure access package catalog is created" -Verbose

$accessPackageCatalog = Invoke-RestMethod $endpoints.accessPackageCatalogs @restParams | 
    Select-Object -ExpandProperty Value | 
    Where-Object displayName -eq $accessPackageCatalogName

if(!$accessPackageCatalog) {
    Write-Verbose "Creating access package catalog '$accessPackageCatalogName'" -Verbose
    $body = @{
        displayName = $accessPackageCatalogName
        description = "Demo access package catalog created with script by Marius Solbakken Mellum"
        isExternallyVisible = $true
    }
    $accessPackageCatalog = Invoke-RestMethod $endpoints.accessPackageCatalogs @restParams -Method Post -Body $body
} else {
    Write-Verbose "Access package catalog '$accessPackageCatalogName' already created" -Verbose
}


#
# STEP 2 - Ensure access packages are created
#
Write-Verbose "STEP 2 - Ensure access packages are created" -Verbose

# Get all access packages in this catalog
$currentAccessPackages = Invoke-RestMethod $endpoints.accessPackages @restParams | 
    Select-Object -ExpandProperty Value | 
    Where-Object catalogId -eq $accessPackageCatalog.id 

# Add some attributes
$accessPackages[2]["isHidden"] = $true # Hide access package number 3
$accessPackages | ForEach-Object {
    $_["catalogId"] = $accessPackageCatalog.id
    $_["description"] = "Demo access package created with script by Marius Solbakken Mellum"
}

# Create access packages
$createdAccessPackages = $accessPackages | 
    ForEach-Object {
        if($_.displayName -notin $currentAccessPackages.displayName) {
            Write-Verbose "Creating access package '$($_.displayName)'" -Verbose
            Invoke-RestMethod $endpoints.accessPackages @restParams -Method Post -Body $_
        } else {
            Write-Verbose "Access package '$($_.displayName)' already created" -Verbose
        }
    }

$currentAccessPackages = @($currentAccessPackages; $createdAccessPackages)

#
# STEP 3 - Get some random groups and users form the tenant
#
Write-Verbose "STEP 3 - Get some random groups and users form the tenant" -Verbose

$User = Invoke-RestMethod $endpoints.me @restParams

$RandomGroup = Invoke-RestMethod ('{0}?$top=999' -f $endpoints.groups) @restParams | 
    Select-Object -ExpandProperty Value | 
    Get-Random -Count 1

#
# STEP 4 - Ensure access package request policies exist
#
Write-Verbose "STEP 4 - Ensure access package request policies exist" -Verbose

$policies = Invoke-RestMethod $endpoints.accessPackageAssignmentPolicies @restParams |
    Select-Object -ExpandProperty value

# If policy for package 0 ('Visible to anyone') is not already created
$_ap = ($currentAccessPackages | Where-Object displayName -eq $accessPackages[0].displayName)
if($policies | ? accessPackageId -eq $_ap.Id) {
    Write-Verbose "Policy for access package '$($_ap.displayName)' already created" -Verbose
} else {
    Write-Verbose "Creating policy for access package '$($_ap.displayName)'" -Verbose
    $body = @{
        accessPackageId = $_ap.Id
        displayName = "Policy allowing anyone to request package"
        description = "Anyone can request this package, no B2B guest account required"
        durationInDays = 365
        canExtend = $true
        requestorSettings = @{
            acceptRequests = $true 
            scopeType = "AllExternalSubjects"
            allowedRequestors = @()
        }
        accessReviewSettings = $null
        requestApprovalSettings = @{
            isApprovalRequired = $true
            isApprovalRequiredForExtension = $true
            isRequestorJustificationRequired = $true 
            approvalMode = "SingleStage"
            approvalStages = @(
                @{
                    approvalStageTimeOutInDays = 14
                    isApproverJustificationRequired = $true 
                    isEscalationEnabled = $false 
                    escalationTimeInMinutes = 0
                    escalationApprovers = @()
                    primaryApprovers = @(
                        @{
                            "@odata.type" = "#microsoft.graph.singleUser"
                            id = $User.id
                            description = $User.displayName
                            isBackup = $false
                        }
                    )
                }
            )
        }
    }
    Invoke-RestMethod $endpoints.accessPackageAssignmentPolicies @restParams -Method Post -Body ($body | ConvertTo-Json -Depth 10) -ContentType "application/json" | Out-Null 
}



$policies = Invoke-RestMethod $endpoints.accessPackageAssignmentPolicies @restParams |
    Select-Object -ExpandProperty value



# If policy for package 1 ('Visible to connected organizations') is not already created
$_ap = ($currentAccessPackages | Where-Object displayName -eq $accessPackages[1].displayName)
if($policies | ? accessPackageId -eq $_ap.Id) {
    Write-Verbose "Policy for access package '$($_ap.displayName)' already created" -Verbose
} else {
    Write-Verbose "Creating policy for access package '$($_ap.displayName)'" -Verbose
    $body = @{
        accessPackageId = $_ap.Id
        displayName = "Policy allowing connected organizations request package"
        description = "Any member of a connected organization can request this package, no B2B guest account required. Approval by internal sponsor."
        durationInDays = 365
        canExtend = $true
        requestorSettings = @{
            acceptRequests = $true
            scopeType = "AllExistingConnectedOrganizationSubjects"
            allowedRequestors = @()
        }
        accessReviewSettings = $null
        requestApprovalSettings = @{
            isApprovalRequired = $true
            isApprovalRequiredForExtension = $true
            isRequestorJustificationRequired = $true 
            approvalMode = "SingleStage"
            approvalStages = @(
                @{
                    approvalStageTimeOutInDays = 14
                    isApproverJustificationRequired = $true 
                    isEscalationEnabled = $false 
                    escalationTimeInMinutes = 0
                    escalationApprovers = @()
                    primaryApprovers = @(
                        @{
                            "@odata.type" = "#microsoft.graph.externalSponsors"
                            isBackup = $false
                        }
                        @{
                            "@odata.type" = "#microsoft.graph.singleUser"
                            id = $User.id
                            description = $User.displayName
                            isBackup = $true
                        }
                    )
                }
            )
        }
    }
    Invoke-RestMethod $endpoints.accessPackageAssignmentPolicies @restParams -Method Post -Body ($body | ConvertTo-Json -Depth 10) -ContentType "application/json" | Out-Null 
}



# If policy for package 2 ('Hidden, available for anyone through url') is not already created
$_ap = ($currentAccessPackages | Where-Object displayName -eq $accessPackages[2].displayName)
if($policies | ? accessPackageId -eq $_ap.Id) {
    Write-Verbose "Policy for access package '$($_ap.displayName)' already created" -Verbose
} else {
    Write-Verbose "Creating policy for access package '$($_ap.displayName)'" -Verbose
    $body = @{
        accessPackageId = $_ap.Id
        displayName = "Policy allowing anyone to request package"
        description = "Anyone can request this package, no B2B guest account required - but they must know the url"
        durationInDays = 365
        canExtend = $true
        requestorSettings = @{
            acceptRequests = $true
            scopeType = "AllExternalSubjects"
            allowedRequestors = @()
        }
        accessReviewSettings = $null
        requestApprovalSettings = @{
            isApprovalRequired = $true
            isApprovalRequiredForExtension = $true
            isRequestorJustificationRequired = $true 
            approvalMode = "SingleStage"
            approvalStages = @(
                @{
                    approvalStageTimeOutInDays = 14
                    isApproverJustificationRequired = $true 
                    isEscalationEnabled = $false 
                    escalationTimeInMinutes = 0
                    escalationApprovers = @()
                    primaryApprovers = @(
                        @{
                            "@odata.type" = "#microsoft.graph.singleUser"
                            id = $User.id
                            description = $User.displayName
                            isBackup = $false
                        }
                    )
                }
            )
        }
    }
    Invoke-RestMethod $endpoints.accessPackageAssignmentPolicies @restParams -Method Post -Body ($body | ConvertTo-Json -Depth 10) -ContentType "application/json" | Out-Null 
}



# If policy for package 3 ('Only available to a random internal group') is not already created
$_ap = ($currentAccessPackages | Where-Object displayName -eq $accessPackages[3].displayName)
if($policies | ? accessPackageId -eq $_ap.Id) {
    Write-Verbose "Policy for access package '$($_ap.displayName)' already created" -Verbose
} else {
    Write-Verbose "Creating policy for access package '$($_ap.displayName)'" -Verbose
    $body = @{
        accessPackageId = $_ap.Id
        displayName = "Policy allowing members of a random internal group to request package"
        description = "Policy allowing members of a random internal group to request package"
        durationInDays = 365
        canExtend = $true
        requestorSettings = @{
            acceptRequests = $true
            scopeType = "SpecificDirectorySubjects"
            allowedRequestors = @(
                @{
                    "@odata.type" = "#microsoft.graph.groupMembers"
                    isBackup = $false
                    description = $RandomGroup.displayName
                    id = $RandomGroup.id
                }
            )
        }
        accessReviewSettings = $null
        requestApprovalSettings = @{
            isApprovalRequired = $true
            isApprovalRequiredForExtension = $true
            isRequestorJustificationRequired = $true 
            approvalMode = "SingleStage"
            approvalStages = @(
                @{
                    approvalStageTimeOutInDays = 14
                    isApproverJustificationRequired = $true 
                    isEscalationEnabled = $false 
                    escalationTimeInMinutes = 0
                    escalationApprovers = @()
                    primaryApprovers = @(
                        @{
                            "@odata.type" = "#microsoft.graph.singleUser"
                            id = $User.id
                            description = $User.displayName
                            isBackup = $false
                        }
                    )
                }
            )
        }
    }
    Invoke-RestMethod $endpoints.accessPackageAssignmentPolicies @restParams -Method Post -Body ($body | ConvertTo-Json -Depth 10) -ContentType "application/json" | Out-Null 
}



# If policy for package 4 ('Available to any internal user') is not already created
$_ap = ($currentAccessPackages | Where-Object displayName -eq $accessPackages[4].displayName)
if($policies | ? accessPackageId -eq $_ap.Id) {
    Write-Verbose "Policy for access package '$($_ap.displayName)' already created" -Verbose
} else {
    Write-Verbose "Creating policy for access package '$($_ap.displayName)'" -Verbose
    $body = @{
        accessPackageId = $_ap.Id
        displayName = "Policy allowing any internal user to request package with manager approval"
        description = "Policy allowing any internal user to request package with manager approval. Quarterly access review."
        durationInDays = 365
        canExtend = $true
        requestorSettings = @{
            acceptRequests = $true
            scopeType = "AllExistingDirectoryMemberUsers"
            allowedRequestors = @()
        }
        accessReviewSettings = @{
            isEnabled = $true
            recurrenceType = "quarterly"
            reviewerType = "Self"
            startDateTime = (get-date).ToString("yyyy-MM-ddTHH:mm:ss.000Z")
            durationInDays = 25
            reviewers = @()
        }
        requestApprovalSettings = @{
            isApprovalRequired = $true
            isApprovalRequiredForExtension = $true
            isRequestorJustificationRequired = $true 
            approvalMode = "SingleStage"
            approvalStages = @(
                @{
                    approvalStageTimeOutInDays = 14
                    isApproverJustificationRequired = $true 
                    isEscalationEnabled = $false 
                    escalationTimeInMinutes = 0
                    escalationApprovers = @()
                    primaryApprovers = @(
                        @{
                            "@odata.type" = "#microsoft.graph.requestorManager"
                            isBackup = $false
                        }
                        @{
                            "@odata.type" = "#microsoft.graph.singleUser"
                            id = $User.id
                            description = $User.displayName
                            isBackup = $true
                        }
                    )
                }
            )
        }
    }
    Invoke-RestMethod $endpoints.accessPackageAssignmentPolicies @restParams -Method Post -Body ($body | ConvertTo-Json -Depth 10) -ContentType "application/json" | Out-Null 
}

Write-Verbose "That's it - go to http://myaccess.microsoft.com/@$($user.userPrincipalName.Split("@")[1]) as any internal or external user" -Verbose

You will now get these access packages created:

Including policies:

There are no resource roles linked to the access package right now. I’ll add some sample for that later 🙂

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 )

Facebook photo

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

Connecting to %s