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
Figure 1: SharePoint Online site deletion is controlled by a retention policy

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.

TEC Talk Series 2024

Check out these free, 60-minute live training webinars modeled after the acclaimed, in-person sessions that The Experts Conference (TEC) is known for – with practical, no-fluff insight led by the industry’s leading Microsoft 365 experts.

About the Author

Tony Redmond

Tony Redmond has written thousands of articles about Microsoft technology since 1996. He is the lead author for the Office 365 for IT Pros eBook, the only book covering Office 365 that is updated monthly to keep pace with change in the cloud. Apart from contributing to Practical365.com, Tony also writes at Office365itpros.com to support the development of the eBook. He has been a Microsoft MVP since 2004.

Comments

  1. Josh

    If the hold has multiple source mailboxes, does it remove the hold ONLY from the mailbox specified in the cmdlet?

Leave a Reply