Clear the SharePoint Preservation Hold Library

Introduction: When SharePoint Retention Becomes a Storage Problem

If you manage Microsoft 365 long enough, you’ll eventually hit this problem:

“Why is our SharePoint storage still growing even though we deleted everything?”

In my case, the culprit was the Preservation Hold Library—a hidden SharePoint document library that silently ballooned due to a retention policy. Even after users deleted content and emptied recycle bins, storage usage kept climbing.

This article explains what the Preservation Hold Library is, why it grows, when it’s safe to clear, and—most importantly—how to permanently delete its contents using PowerShell, bypassing the recycle bin.

This is not theory. This is a field-tested solution for IT professionals responsible for SharePoint storage, compliance, and Microsoft 365 governance.


What Is the Preservation Hold Library (PHL)?

The Preservation Hold Library is a hidden system library created automatically when:

  • A retention policy applies to a SharePoint site
  • A retention label with “retain” behavior is used
  • Legal hold or eDiscovery retention is enforced

When users delete files under retention, SharePoint does not actually remove them. Instead:

  • The content is copied into the Preservation Hold Library
  • The file is retained until the retention period expires
  • The user believes the file is gone, but storage continues to be consumed

This design ensures compliance—but it comes with a cost.


Why the Preservation Hold Library Grows Out of Control

From real-world experience, this happens most often when:

  • Retention policies are removed or reconfigured
  • Sites are heavily used for collaboration or file churn
  • Migration projects bring in large volumes of temporary data
  • Retention labels were applied too broadly

The key problem:
SharePoint does not automatically clean up the Preservation Hold Library immediately after retention changes.

In some tenants, content can remain indefinitely unless explicitly removed.


Important Warning: This Is a Permanent Action

Before going any further, this must be crystal clear:

⚠️ Deleting content from the Preservation Hold Library permanently removes retained data and bypasses the recycle bin.

You should only proceed if:

  • The retention policy has been removed or expired
  • Legal, compliance, and records teams have approved the deletion
  • You understand the regulatory implications for your organization

In regulated industries (finance, health, government), this step should be documented and approved formally.


Why Microsoft Doesn’t Provide a “Clean Up” Button

A common question I get is:

“Why isn’t there a Microsoft-supported way to clean this up?”

The answer is risk.

Microsoft deliberately avoids providing UI-based deletion tools for retained data because:

  • Retention is often tied to legal obligations
  • Accidental deletion could invalidate legal holds
  • Automation could conflict with eDiscovery processes

That leaves experienced IT admins with PowerShell as the only viable option.


The Approach: Using PnP.PowerShell

The PnP.PowerShell module provides the necessary control to:

  • Access hidden SharePoint system libraries
  • Enumerate large numbers of list items
  • Permanently delete items without recycling

This script is designed to:

  • Process items in batches
  • Provide clear warnings
  • Require explicit confirmation
  • Permanently delete items (Recycle Bin bypassed)

PowerShell Script: Empty the Preservation Hold Library

Prerequisites

  • PowerShell 7+ recommended
  • PnP.PowerShell module
  • SharePoint Administrator or Site Collection Administrator permissions

Install the module if required:

Install-Module PnP.PowerShell -Scope CurrentUser

The Script

# Empty SharePoint Preservation Hold Library (Permanent Delete - Bypasses Recycle Bin)
# Requires PnP.PowerShell module: Install-Module PnP.PowerShell -Scope CurrentUser

param(
    [Parameter(Mandatory=$true)]
    [string]$SiteUrl,
    
    [int]$BatchSize = 500
)

# Connect to SharePoint (will prompt for authentication)
try {
    Connect-PnPOnline -Url $SiteUrl -Interactive -ErrorAction Stop
}
catch {
    Write-Host "Failed to connect to SharePoint: $_" -ForegroundColor Red
    exit 1
}

# Verify we have a valid connection
$ctx = Get-PnPContext
if ($null -eq $ctx) {
    Write-Host "No valid SharePoint connection. Exiting." -ForegroundColor Red
    exit 1
}

Write-Host "Connected successfully to $SiteUrl" -ForegroundColor Green

# The Preservation Hold Library has a fixed internal name
$libraryName = "PreservationHoldLibrary"

Write-Host "`nWARNING: This will PERMANENTLY delete items (bypassing recycle bin)!" -ForegroundColor Red
Write-Host "Site: $SiteUrl" -ForegroundColor Yellow
$confirm = Read-Host "Type 'YES' to continue"

if ($confirm -ne "YES") {
    Write-Host "Aborted." -ForegroundColor Yellow
    Disconnect-PnPOnline
    exit
}

Write-Host "`nFetching items from Preservation Hold Library..." -ForegroundColor Cyan

$itemCount = 0
do {
    # Get items in batches
    $items = Get-PnPListItem -List $libraryName -PageSize $BatchSize
    
    if ($items.Count -eq 0) {
        Write-Host "No more items to delete." -ForegroundColor Green
        break
    }
    
    Write-Host "Processing batch of $($items.Count) items..." -ForegroundColor Yellow
    
    foreach ($item in $items) {
        try {
            # -Recycle:$false = permanent delete, bypasses recycle bin
            Remove-PnPListItem -List $libraryName -Identity $item.Id -Force -Recycle:$false
            $itemCount++
            
            if ($itemCount % 100 -eq 0) {
                Write-Host "Permanently deleted $itemCount items so far..." -ForegroundColor Gray
            }
        }
        catch {
            Write-Host "Failed to delete item $($item.Id): $_" -ForegroundColor Red
        }
    }
    
} while ($items.Count -gt 0)

Write-Host "`nComplete! Permanently deleted $itemCount total items." -ForegroundColor Green

Disconnect-PnPOnline

How to Run the Script

  1. Save the script as: Clear-PreservationHoldLibrary.ps1
  2. Open PowerShell as your admin account
  3. Run the script: .\Clear-PreservationHoldLibrary.ps1 -SiteUrl https://tenant.sharepoint.com/sites/SiteName
  4. Type YES when prompted

Real-World Observations and Lessons Learned

Storage Doesn’t Drop Immediately

In my experience, SharePoint storage metrics can take 24–72 hours to reflect changes. Don’t panic if usage doesn’t drop instantly.

Batch Size Matters

If you hit throttling or timeouts:

  • Reduce batch size to 100–200
  • Run during off-peak hours

Always Document the Action

Treat this like deleting backups:

  • Record approval
  • Capture before/after storage metrics
  • Keep script output for audit purposes

When You Should NOT Use This Script

Do not use this script if:

  • Retention policies are still active
  • Legal hold or litigation is ongoing
  • You are unsure which data is retained
  • Compliance teams have not approved deletion

This script is a scalpel, not a broom.


Final Thoughts

The Preservation Hold Library is one of those SharePoint features that works exactly as designed, yet still catches IT teams off guard.

Understanding how retention actually behaves—and how to safely clean up after it—is a mark of a senior Microsoft 365 administrator, not a junior one.

Used responsibly, this script can:

  • Reclaim terabytes of storage
  • Reduce licensing costs
  • Restore confidence in your SharePoint governance model

Just remember:
Compliance first. Cleanup second.

Leave a Reply

Your email address will not be published. Required fields are marked *