Disposition review: read items and change them in bulk with PowerShell

Disposition review is the queue after retention ends, when the label has KeepAndDelete + reviewers. PowerShell can read and export the queue without the UI 50,000-row cap. Approving, extending or relabelling in bulk is not a cmdlet — that is the Purview UI (multi-select). If a SharePoint list is filled by the end-of-retention flow, you can bulk-update that list with PnP. Create the labels in File Plan record labels.

Connect

Get-ReviewItems needs the Mailbox Import Export role, not assigned to any group by default. Add it to Records Management. PowerShell 7, not ISE. Security & Compliance PowerShell: Connect-IPPSSession, not Connect-ExchangeOnline. See Connect-IPPSSession.

PowerShell
# PowerShell 7. Role: Records Management + Mailbox Import Export
# (Mailbox Import Export is not on any role group by default — add it.)
Connect-IPPSSession -UserPrincipalName [email protected]

Get-ComplianceTag | Where-Object { $_.RetentionAction -eq 'KeepAndDelete' } |
  Format-Table Name, ImmutableId, IsRecordLabel, ReviewerEmail, AutoApprovalPeriod

Pending items for one label

TargetLabelId is the ImmutableId, not the display name. Without -IncludeHeaders the CSV columns are unusable. Each page is a CSV fragment in ExportItems.

PowerShell
$tag = Get-ComplianceTag -Identity "IB-Vergunning-20j"
$id  = $tag.ImmutableId   # Get-ReviewItems wants this, not the name

$page = Get-ReviewItems -TargetLabelId $id -IncludeHeaders $true
$rows = $page.ExportItems | ConvertFrom-Csv -Header $page.Headers
$rows | Select-Object Subject, Location, ReviewAction, Comment |
  Format-Table -AutoSize

Page everything to CSV

UI export stops at 50,000. This loop does not. Throttling: pause on large queues. Then filter in Excel/Power BI on Location, ReviewAction, Comment. This is your evidence pile for the auditor.

PowerShell
$id = (Get-ComplianceTag -Identity "IB-Vergunning-20j").ImmutableId
$page = Get-ReviewItems -TargetLabelId $id -IncludeHeaders $true
$csv  = $page.ExportItems
while (-not [string]::IsNullOrEmpty($page.PaginationCookie)) {
    $page = Get-ReviewItems -TargetLabelId $id -IncludeHeaders $true `
              -PagingCookie $page.PaginationCookie
    $csv += $page.ExportItems
}
$csv | ConvertFrom-Csv -Header $page.Headers |
  Export-Csv .\pending-afvoer.csv -NoTypeInformation -Encoding UTF8
PowerShell
# Already disposed (proof of destruction)
$page = Get-ReviewItems -TargetLabelId $id -IncludeHeaders $true -Disposed $true
$page.ExportItems | ConvertFrom-Csv -Header $page.Headers |
  Select-Object Subject, Location, DeletedBy, DeletedDate |
  Export-Csv .\afgevoerd.csv -NoTypeInformation -Encoding UTF8

Bulk changes

  • Purview queue: Disposition → label → Pending → select the filtered set → Approve disposal / Relabel / Extend / Add reviewer. There is no Approve-ReviewItem.
  • Your list (Power Automate): that is your working pile. PnP Set-PnPListItem in a foreach. That does not approve the Microsoft queue — still do the UI afterwards. Flow: File Plan + flow.
  • The label itself: reviewers and auto-approval are Set-ComplianceTag. That hits new items; pending items keep the old reviewer until you change them in the UI.
  • After Approve disposal: up to 15 days until permanent delete. Audit: Approved disposal, Extended retention period, Relabeled item.
PowerShell
# There is no Approve-ReviewItem cmdlet. Native bulk approve = Purview portal.
# If your Power Automate flow wrote items to a SharePoint list, bulk-update THAT list:

Connect-PnPOnline -Url "https://voorbeeld.sharepoint.com/sites/Informatiebeheer" -Interactive -ClientId <id>
$items = Get-PnPListItem -List "IM-afvoer" -PageSize 2000 |
  Where-Object { $_["Status"] -eq "Pending" -and $_["Label"] -eq "IB-Vergunning-20j" }

foreach ($i in $items) {
    # Example: mark reviewed in YOUR list (does not approve the Purview queue).
    Set-PnPListItem -List "IM-afvoer" -Identity $i.Id -Values @{
        Status     = "Goedgekeurd IM-overleg 2026-09-14"
        ReviewedBy = "[email protected]"
        ReviewedOn = (Get-Date)
    }
}
# Then approve the same set in Purview Disposition UI (multi-select → Approve disposal).
PowerShell
# Change the LABEL (reviewers, auto-approval) — not the items in the queue.
Set-ComplianceTag -Identity "IB-Vergunning-20j" `
  -ReviewerEmail "[email protected];[email protected]" `
  -AutoApprovalPeriod 30

Enable-ComplianceTagStorage -RecordsManagementSecurityGroupEmail "[email protected]"

Enable-ComplianceTagStorage -RecordsManagementSecurityGroupEmail once, or the records admin will not see every item. Changing the group later: PowerShell again, not the portal. Too much review = a queue that never empties. Auto-approval + replacement label (daisy-chain) are the escape, not more reviewers.

Frequent errors and how to fix them

These are the messages people type into search when the module will not start. The exact wording is here so this page can be found.

ComplianceTagForNextStage / replacement label not found
Daisy-chain: create the last label first, then the one that points at it. Nested sensitivity is the opposite (parent first).
A parameter cannot be found that matches parameter name 'TargetLabelId'
Get-ReviewItems wants ImmutableId, not the name. Get-ComplianceTag | Select ImmutableId. Mailbox Import Export role is often missing.
Flow not listed on the retention label
Flow is not in the Default environment, or the premium connector has no licence. Attach with Set-ComplianceTag -FlowId.
ReviewerEmail / invalid recipient
Disposition review needs a mail-enabled security group, not a Microsoft 365 Group and not a guest. Max 200 reviewers per tenant.
EnableMIPLabels is False / Groups and sites options missing
Container labels: set EnableMIPLabels to True via Microsoft Graph (directory setting) before you publish Groups and sites.
ParentId / label not found (New-Label nested)
Sensitivity: parent first, child second. Retention daisy-chain is the opposite (last first).

Questions about this, or need help setting it up? We are happy to think along.

Message on LinkedIn →