Invoke-HoldRemovalAction Cmdlet Cleans up Old eDiscovery Holds
MVP Vasil Michev likes to spend time poking around the innards of Microsoft 365. In a recent blog post, he covers a new cmdlet in the Security and Compliance set called Invoke-HoldRemovalAction. As defined by the documentation, the purpose of the cmdlet is “to view and remove holds on mailboxes and SharePoint sites. You can also see holds that were previously removed by using this cmdlet.”
In a nutshell, you can use this cmdlet to:
- Find and remove eDiscovery holds set on mailboxes.
- Find and remove Microsoft 365 holds set on SharePoint Online sites.
Vasil’s blog covers many of the gory details. Here I focus on discovering what holds exist. Before we start, let me note that this cmdlet is not fast. Don’t expect sparkling performance when using it in scripts.
Investigating Obsolete eDiscovery Holds on Exchange Online Mailboxes
Obsolete holds on mailboxes can prevent the permanent removal of a mailbox. Vasil’s blog explains that the holds returned by the cmdlet include litigation holds. The cmdlet also returns an indication when a delay hold applies to a mailbox.
Invoke-HoldRemovalAction -Action GetHolds -ExchangeLocation Rene.Artois DelayHold
Delay holds retain mailbox content for 30 days following the removal of a hold to ensure that data loss doesn’t inadvertently occur. The interesting thing is that even new mailboxes proclaim that they have delay hold set. I can remove delay hold by running the Set-Mailbox cmdlet, but I want to understand why the cmdlet reports this status.
When processing Exchange Online mailboxes, the Invoke-HoldRemovalAction cmdlet does not report Microsoft 365 retention holds placed by retention policies. We know this by comparing the set of holds reported by the Get-ExoMailbox cmdlet and those reported by Invoke-HoldRemovalAction. As you can see, the set reported by Get-ExoMailbox includes holds with identifiers prefixed with values like mbx, grp, and skp. These are Microsoft 365 retention holds for mailboxes, group mailboxes, and Skype for Business. The holds with Uni prefix are for holds imposed by eDiscovery cases.
Get-ExoMailbox -Identity Kim.Akers -Properties InPlaceHolds | Select-Object -ExpandProperty InPlaceHolds UniHcbbf9e00-9ed2-41d8-8157-eddd02be1dca UniH5995a91a-09f2-42a9-978c-1f11917e90dd mbx21788b2cd3e949b293317ab17b8b87a2:1 mbxa56b16072af24fa5bd96b2bf206e3816:1 skp748f77b020124e6e8304e66021fb297b:3 mbx748f77b020124e6e8304e66021fb297b:3 UniHec6163be-6ed6-4b16-afe8-1b2165b9359f UniH84dea76f-c845-4101-b066-a8b10c13c210 Invoke-HoldRemovalAction -Action GetHolds -ExchangeLocation Kim.Akers UniHcbbf9e00-9ed2-41d8-8157-eddd02be1dca UniH5995a91a-09f2-42a9-978c-1f11917e90dd UniHec6163be-6ed6-4b16-afe8-1b2165b9359f UniH84dea76f-c845-4101-b066-a8b10c13c210
Understanding what kinds of holds might exist on mailboxes, we can write a script to analyze mailboxes and see what’s reported. My version of a test script for both Exchange Online and SharePoint Online is available from GitHub. When the script analyzes a mailbox, it highlights any holds it cannot resolve. For example, this output tells us that two discovery holds cannot be associated with an eDiscovery case:
Analyzing holds on mailbox Ben Owens (DCPG) (3/34) Unable to identify hold identifier 47f67751-1036-4621-80d6-d25837adf813 Unable to identify hold identifier ec6163be-6ed6-4b16-afe8-1b2165b9359f Delay hold set
Unresolved holds are candidates to be removed using a command like:
Invoke-HoldRemovalAction -Action RemoveHold -ExchangeLocation Ben.Owens -HoldId ‘UniH47f67751-1036-4621-80d6-d25837adf813’ -Force
Although the hold release appears to be immediate, Microsoft warns that it can take a few hours before the hold is released from the mailbox.
Hold Removal Logging
Hold removal actions are logged. The details of removals can be found by running the cmdlet with the GetHoldRemovals action. For example, to find the details logged for the hold removal shown above, this code finds the full set of logged removals and applies a filter to find the specific removal:
[array]$RemovalActions = Invoke-HoldRemovalAction -Action GetHoldRemovals $RemovalActions | Where-Object {$_.Action -eq 'RemoveHold' -and $_.ExchangeLocation -eq 'Ben.Owens'} | Format-List TenantId : a662313f-14fc-43a2-9a7a-d2e27f4f3478 Identity : 92e84592-9f04-412c-ab7b-87955b777b99 Action : RemoveHold User : Tony.Redmond@office365itpros.com HoldId : UniH47f67751-1036-4621-80d6-d25837adf813 ExchangeLocation : Ben.Owens SharePointLocation : CreatedTime : 07/11/2023 15:44:20 Sequence : 20231107154420.7619713Z IsValid : True ObjectState : New
Investigating Obsolete eDiscovery Holds on SharePoint Online Sites
While the Invoke-HoldRemovalAction cmdlet eschews Microsoft 365 retention holds when dealing with mailboxes, the situation is quite different for SharePoint Online sites. SharePoint Server didn’t have the same kind of developed retention setup of the kind introduced with Exchange Server 2010, so the problem of dealing with orphaned or obsolete holds doesn’t really exist.
What does happen with SharePoint Online is sites that a retention policy can block deletion (Figure 1). This is goodness if the site is active and it’s important to keep the information held in the site; it can be frustrating if you’ve deleted the underlying Microsoft 365 group or team and SharePoint fails to remove the site. If you’re really sure that it is safe to remove the site, you can run the cmdlet to remove the holds.
![SharePoint Online site deletion is controlled by a retention policy](https://s40823.pcdn.co/wp-content/uploads/2023/11/Site-with-retention-policy-1024x679.jpg)
For instance, we can discover what holds are blocking the removal of the site shown in Figure 1 by running the cmdlet to return the hold identifiers and then using the Get-RetentionCompliancePolicy cmdlet to return the policy name:
Invoke-HoldRemovalAction -Action GetHolds -SharePointLocation "https://office365itpros.sharepoint.com/sites/contractworkinggroup" 08de2cc7-b361-48bc-9faf-9b5a31933cd6 d4cefc1c-2a11-4d4c-9cb4-85f5cb5df021 (Get-RetentionCompliancePolicy -Identity 08de2cc7-b361-48bc-9faf-9b5a31933cd6).Name GDPR Site Policy (Get-RetentionCompliancePolicy -Identity d4cefc1c-2a11-4d4c-9cb4-85f5cb5df021).Name Process GDPR Information in SharePoint Sites
If it’s deemed OK to remove a retention policy, run the cmdlet again, this time using the RemoveHold parameter:
Invoke-HoldRemovalAction -Action RemoveHold -SharePointLocation "https://office365itpros.sharepoint.com/sites/contractworkinggroup" -HoldId 08de2cc7-b361-48bc-9faf-9b5a31933cd6 -Force WARNING: Hold '08de2cc7-b361-48bc-9faf-9b5a31933cd6' was removed. It may take up to 240 minutes to take effect.
Interestingly, both retention policies use adaptive scopes, which evaluate sites against preset criteria to decide if they come within the scope of a retention policy. As it turns out, if you remove a hold applied by a policy with an adaptive scope, Purview reapplies the hold the next time it evaluates sites against the policy criteria.
If you see a hold name like CustodianHold-4eafeb2c-2654-4bc0-bcb1-6cedd436ff12, you know it’s a hold applied by an eDiscovery case.
Finding SharePoint Sites with Holds
Equipped with the knowledge of using the Invoke-HoldRemovalAction cmdlet with SharePoint sites, we can write some code to find sites with holds set. Before running this code, which finds holds applying to sites connected to Microsoft 365 groups, sign into SharePoint Online using the SharePoint Online management module. Make sure that you’re connected to Exchange Online and the compliance endpoint too and then run:
[array]$Sites = Get-SpoSite -Limit All -Template "Group#0" $SPOReport = [System.Collections.Generic.List[Object]]::new() [Int]$i = 0 ForEach ($Site in $Sites) { $i++ Write-Host ("Processing site {0} {1}/{2}" -f $Site.Title, $i, $Sites.Count) [array]$HoldData = Invoke-HoldRemovalAction -Action GetHolds -SharePointLocation $Site.Url If ($HoldData) { Write-Host ("Found holds on site {0} {1}" -f $Site.Title, $Site.Url) -ForegroundColor Red ForEach ($Hold in $HoldData) { $HoldInfo = Get-RetentionCompliancePolicy -Identity $Hold If ($HoldInfo.IsAdaptivePolicy -eq $True) { $AdaptiveFlag = "Adaptive Policy" } Else { $AdaptiveFlag = "Static Policy" } Write-Host ("Microsoft 365 Retention Policy set is {0} ({1})" -f $HoldInfo.Name, $AdaptiveFlag) -ForegroundColor Yellow $Reportline = [PsCustomObject]@{ Site = $Site.Url Title = $Site.Title HoldId = $Hold Hold = $HoldInfo.name 'Policy Scope' = $AdaptiveFlag } $SPOReport.Add($ReportLine) } } }
The result is a report containing records like this:
Site : https://office365itpros.sharepoint.com/sites/pltestgroup Title : PL Test Group HoldId : 9292f3a7-ddf2-4401-9ecf-72be0f78974b Hold: Preservation Lock - Mailboxes and Sites Policy Scope: Static Policy
The report should be enough to help you understand what holds exist on SharePoint Online sites.
Script Available for Testing
The script mentioned above (downloadable from GitHub) contains the code for checking holds against Exchange Online mailboxes and SharePoint Online sites.
If you discover more information about how the cmdlet works or what it does, please respond in a comment. Meantime, I am off to talk to some Microsoft people to see if I can learn more about those pesky delay hold indicators.
If the hold has multiple source mailboxes, does it remove the hold ONLY from the mailbox specified in the cmdlet?
The Real Person!
The Real Person!
Yes. That’s why you specify the target mailbox.