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 🙂