NAVANEM
SharePoint Online[PowerShell]advanced6 min read · jun 14, 2026 · 02:17 utc

Delete All File Versions Across SharePoint Online to Reclaim Storage

Permanently delete the version history of every file in every document library across all SharePoint Online sites with PnP PowerShell, to reclaim storage. Destructive and irreversible, so use it with care.

by Emanuel De Almeida

TL;DR

  • Permanently deletes the entire version history of every file in every document library across all SharePoint Online sites.
  • Reclaims the storage that old versions consume.
  • Destructive and irreversible: deleted versions cannot be recovered.
  • Runs app-only with PnP PowerShell and a certificate.
  • Pair it with a max-version policy so the bloat does not just come back.

What does this script do?

It connects to the SharePoint admin center, enumerates every site, and for each file in every visible document library it loads the version history and calls Versions.DeleteAll, removing all previous versions while keeping the current file. On tenants that have run for years without a version cap, this can free a surprising amount of storage.

Before you run this

This is destructive and cannot be undone. There is no recycle bin for file versions removed this way. Run it only when you are sure you no longer need version history, test it on a single pilot site first, and confirm your backup or retention strategy covers anything you might still need. It is good practice to apply a sensible max-version limit first, then clean up the backlog.

What do you need before running it?

  • PowerShell 5.1 or later.
  • The PnP.PowerShell module.
  • An Entra ID app registration with SharePoint app-only permission Sites.FullControl.All, granted admin consent, and a client certificate.

Install the PnP module if needed:

shell
Install-Module PnP.PowerShell -Scope CurrentUser

How do you run it?

Fill in the connection variables, then run it (start with a pilot site):

powershell
.\Remove-AllFileVersions.ps1

A progress bar shows the library being processed, and a run log is written to C:\Temp\Log.

FAQ

Can I undo this?

No. Versions removed with DeleteAll are gone permanently. Only proceed if you are certain you do not need the history.

Is there a less drastic option?

Yes. If you only want to stop growth, apply a major-version limit instead, which caps future versions without deleting existing data. Use the clean-up only when you need to reclaim storage immediately.

The script

powershell-delete-all-file-versions-sharepoint-online.ps1
#Requires -Version 5.1
#Requires -Modules PnP.PowerShell
<#
.SYNOPSIS
    Deletes ALL previous file versions from every document library on every
    SharePoint Online site.
.DESCRIPTION
    Connects to the SharePoint admin center with app-only (certificate)
    authentication, enumerates every site, and for each file in every visible
    document library deletes the entire version history with CSOM
    (Versions.DeleteAll). This reclaims the storage consumed by old versions.

    WARNING: this is destructive and irreversible. Deleted versions cannot be
    recovered. Apply a sane max-version policy first, test on a pilot site, and
    confirm your backup strategy before running it tenant-wide.

    The app registration needs SharePoint app-only permissions
    (Sites.FullControl.All).
.NOTES
    Author : Emanuel De Almeida - https://www.navanem.com
    Version: 1.0
#>
If ([Net.SecurityProtocolType]::Tls12 -bor $False) {
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    Write-Host "`t Forced TLS 1.2 since it is not the server default"
}
$Global:ErrorActionPreference = 'Stop'

# ─── Connection variables (replace with your own) ───
$Tenant                  = '<your-tenant>.onmicrosoft.com'
$Admin_Url               = 'https://<your-tenant>-admin.sharepoint.com'
$Application_ID          = '<your-application-id>'
$Certificate_Thumb_Print = '<your-certificate-thumbprint>'

# Log helper
Function Write-Log {
    Param(
        [Parameter(Mandatory = $true)][String]$Message,
        [Parameter(Mandatory = $true)][String]$Type
    )
    $Date = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
    "$Date - $Type - $Message" |
        Out-File -FilePath "C:\Temp\Log\$(Get-Date -Format 'yyyy-MM-dd').log" -Append -Encoding UTF8
}

Function CheckFilePath {
    If (-not (Test-Path -Path 'C:\Temp\Log')) { New-Item 'C:\Temp\Log' -ItemType Directory | Out-Null }
}
CheckFilePath

Try {
    Write-Log -Message 'Connecting to SharePoint Online' -Type 'Information'
    Connect-PnPOnline -Url $Admin_Url -Tenant $Tenant -ClientId $Application_ID -Thumbprint $Certificate_Thumb_Print
    Write-Log -Message 'Connected' -Type 'Success'
} Catch {
    Write-Host "`n`t$($_.InvocationInfo.InvocationName) [Line:$($_.InvocationInfo.ScriptLineNumber)]: $($_.Exception.Message)" -ForegroundColor Yellow
    Write-Log -Message "$($_.InvocationInfo.InvocationName) [Line:$($_.InvocationInfo.ScriptLineNumber)]: $($_.Exception.Message)" -Type 'Error'
    Write-Log -Message 'Unable to connect to SharePoint Online' -Type 'Error'
    Break
}

Try {
    Write-Log -Message 'Getting all sites from SharePoint Online' -Type 'Information'
    $Sites = Get-PnPTenantSite
    Write-Log -Message "Sites found: $($Sites.Count)" -Type 'Information'

    Foreach ($Site in $Sites) {
        Connect-PnPOnline -Url $Site.Url -Tenant $Tenant -ClientId $Application_ID -Thumbprint $Certificate_Thumb_Print
        $Context = Get-PnPContext
        $Document_Libraries = Get-PnPList | Where-Object { $_.BaseType -eq 'DocumentLibrary' -and $_.Hidden -eq $false }
        $i = 1
        $Total = [math]::Max($Document_Libraries.Count, 1)
        Foreach ($Library in $Document_Libraries) {
            Write-Progress -Activity 'Cleaning versions' -Status "Library $i of $Total in $($Site.Url)" -PercentComplete (($i / $Total) * 100)
            $List_Items = Get-PnPListItem -List $Library -PageSize 2000 | Where-Object { $_.FileSystemObjectType -eq 'File' }
            Foreach ($Item in $List_Items) {
                $File = $Item.File
                $Versions = $File.Versions
                $Context.Load($File)
                $Context.Load($Versions)
                $Context.ExecuteQuery()
                If ($Versions.Count -gt 0) {
                    $Versions.DeleteAll()
                    Invoke-PnPQuery
                }
            }
            $i++
        }
    }
    Write-Log -Message 'All sites processed' -Type 'Success'
} Catch {
    Write-Host "`n`t$($_.InvocationInfo.InvocationName) [Line:$($_.InvocationInfo.ScriptLineNumber)]: $($_.Exception.Message)" -ForegroundColor Yellow
    Write-Log -Message "$($_.InvocationInfo.InvocationName) [Line:$($_.InvocationInfo.ScriptLineNumber)]: $($_.Exception.Message)" -Type 'Error'
    Write-Log -Message 'Unable to process all sites' -Type 'Error'
}

Review before running. Test in a non-production environment first.

#PowerShell#microsoft-365#sharepoint#Automation

Related topics