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:
- Sign into the Microsoft Graph Explorer and add the scope ‘EntitlementManagement.ReadWrite.All’
- Run the script
- 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 🙂