SharePoint Online: Delete Version History using PnP PowerShell

Requirement: Delete version history in SharePoint Online.

How to Delete Versions in SharePoint Online?

If you’ve been managing SharePoint Online for any time, you know that keeping your environment clean and organized is essential. One such way to clean it up is by deleting older versions of documents and files, as old versions can take up a lot of storage space. This blog post will show you how to remove older versions of documents in SharePoint Online using PowerShell and automate the cleanup process.

To delete previous versions of a file in SharePoint Online, follow these steps:

  1. Navigate to your SharePoint Online document library, select the file and then click on “Version History” from the toolbar. (or Select “Version History” from the context menu of the document)
  2. On the version history page, You can choose “Delete” from the context menu of the particular version. To delete all versions of a document or list item, click the “Delete All Versions” link on the Version History page and then confirm the prompt.
    sharepoint online delete versions powershell

Delete All Versions of a File using PnP PowerShell

We can delete all versions of a file in the SharePoint Online document library using PowerShell.

#Parameters
$SiteURL = "https://crescent.sharepoint.com/sites/marketing"
$FileURL= "/sites/marketing/Branding/Invoice.xlsx"
 
#Connect to PnP Online
Connect-PnPOnline -Url $SiteURL -Interactive
 
#Get all versions of the File
$Versions = Get-PnPFileVersion -Url $FileURL

#Delete all versions of the File
$Versions.DeleteAll()
Invoke-PnPQuery

SharePoint Online: Delete Version History using PnP PowerShell

Let’s delete all versions of all items in a list or library using PowerShell.

#Config Variables
$SiteURL = "https://Crescent.sharepoint.com/sites/marketing"
$ListName="Documents"

#Connect to PnP Online
Connect-PnPOnline -Url $SiteURL -Credentials (Get-Credential)

#Get the Context
$Ctx= Get-PnPContext

#Get All Items from the List - Exclude 'Folder' List Items
$ListItems = Get-PnPListItem -List $ListName -PageSize 2000 | Where {$_.FileSystemObjectType -eq "File"}

ForEach ($Item in $ListItems)
{
    #Get File Versions
    $File = $Item.File
    $Versions = $File.Versions
    $Ctx.Load($File)
    $Ctx.Load($Versions)
    $Ctx.ExecuteQuery()
    Write-host -f Yellow "Scanning File:"$File.Name
    
    If($Versions.Count -gt 0)
    {
        #Delete all versions
        $Versions.DeleteAll()
        $Ctx.ExecuteQuery()
        Write-Host -f Green "Deleted All Previous Versions of the File:"$File.Name
    }
}

We can clear all previous versions of all the files in a SharePoint Online document library using PnP PowerShell as:

#Parameters
$SiteURL = "https://crescent.sharepoint.com/sites/IC"
$LibraryName = "Energy"
 
Try {
    #Connect to PnP Online
    Connect-PnPOnline -Url $SiteURL -Interactive

    #Get the Library
    $Library = Get-PnPList -Identity $LibraryName 
    $global:counter=0

    #Get All Items from the List - Get 'Files
    $ListItems = Get-PnPListItem -List $Library -Fields FileLeafRef,FileRef -PageSize 2000 -ScriptBlock { Param($items) $global:counter += $items.Count; Write-Progress `
                    -PercentComplete ($global:Counter / ($Library.ItemCount) * 100) -Activity "Getting Files of '$($Library.Title)'" `
                       -Status "Processing Files $global:Counter of $($Library.ItemCount)";} | Where {($_.FileSystemObjectType -eq "File")} 
    Write-Progress -Activity "Completed Retrieving Files!" -Completed

    #Loop through each file
    $global:counter=1
    ForEach ($Item in $ListItems)
    {
        #Get File and File Versions
        $Versions = Get-PnPFileVersion -Url $Item.FieldValues.FileRef
        Write-host -f Yellow "Scanning File ($global:counter of $($Library.ItemCount)) : $($Item.FieldValues.FileRef)"
      
        If($Versions.Count -gt 0)
        {
            #Delete all versions
            $Versions.DeleteAll()
            Invoke-PnPQuery
            Write-Host -f Green "`tDeleted All Previous Versions of the File!" #"$Item.FieldValues.FileRef
        }
        $global:counter++
    }
}
Catch {
    write-host -f Red "Error Cleaning up Version History!" $_.Exception.Message
}

PowerShell to Clear Previous Versions Folder by Folder

Here is a different approach to clear previous versions of all files on libraries with less than 5000 items.

#Parameters
$SiteURL = "https://crescent.sharepoint.com/sites/marketing"
$LibraryName ="Documents"

#Function to Clear all File versions in a Folder
Function Cleanup-Versions([Microsoft.SharePoint.Client.Folder]$Folder)
{
    Write-host "Processing Folder:"$Folder.ServerRelativeUrl -f Yellow
    #Get the Site Relative URL of the folder
    $FolderSiteRelativeURL = $Folder.ServerRelativeURL.Replace($Web.ServerRelativeURL,[string]::Empty)

    #Get All Files from the folder    
    $Files = Get-PnPFolderItem -FolderSiteRelativeUrl $FolderSiteRelativeURL -ItemType File
    
    #Iterate through each file
    ForEach ($File in $Files)
    {
        #Get File Versions
        $Versions = Get-PnPProperty -ClientObject $File -Property Versions 
        Write-host -f Yellow "`tScanning File:"$File.Name
     
        If($Versions.Count -gt 0)
        {
            #Delete all versions
            $Versions.DeleteAll()
            Invoke-PnPQuery
            Write-Host -f Green "`t`tDeleted All Previous Versions of the File:"$File.Name
        }
    }

    #Get Sub-folders from the folder - Exclude "Forms" and Hidden folders
    $SubFolders = Get-PnPFolderItem -FolderSiteRelativeUrl $FolderSiteRelativeURL -ItemType Folder | Where {($_.Name -ne "Forms") -and (-Not($_.Name.StartsWith("_")))}
    Foreach($SubFolder in $SubFolders)
    {
        #Call the function recursively
        Cleanup-Versions -Folder $SubFolder     
    }
}
 
#Connect to PnP Online
Connect-PnPOnline -Url $SiteURL -Interactive
$Web = Get-PnPWeb

#Get the Root Folder of the Library
$RootFolder = Get-PnPList -Identity $LibraryName -Includes RootFolder | Select -ExpandProperty RootFolder

#Call the function with Root Folder of the Library
Cleanup-Versions -Folder $RootFolder

You can clean up previous versions of all files from a folder by replacing Line#45 with “$RootFolder = Get-PnPFolder -Url $FolderServerRelativeURL”.

Keep the Last Five Versions and Delete the Rest:

Going through the version history page and deleting it one by one is cumbersome. Here is the PnP PowerShell to keep the last ‘N’ versions and delete all others.

#Parameters
$SiteURL = "https://crescent.sharepoint.com/sites/Funds"
$ListName = "Documents"
$VersionsToKeep = 5
 
#Connect to PnP Online
Connect-PnPOnline -Url $SiteURL -UseWebLogin

#Get the Document Library
$List = Get-PnPList -Identity $ListName
 
#Get the Context
$Ctx= Get-PnPContext

$global:counter=0 
#Get All Items from the List - Get 'Files
$ListItems = Get-PnPListItem -List $ListName -Fields FileLeafRef -PageSize 2000 -ScriptBlock { Param($items) $global:counter += $items.Count; Write-Progress `
                -PercentComplete ($global:Counter / ($List.ItemCount) * 100) -Activity "Getting Files of '$($List.Title)'" `
                    -Status "Processing Files $global:Counter of $($List.ItemCount)";} | Where {($_.FileSystemObjectType -eq "File")}
Write-Progress -Activity "Completed Retrieving Files!" -Completed

$TotalFiles = $ListItems.count
$Counter = 1 
ForEach ($Item in $ListItems)
{
    #Get File Versions
    $File = $Item.File
    $Versions = $File.Versions
    $Ctx.Load($File)
    $Ctx.Load($Versions)
    $Ctx.ExecuteQuery()
 
    Write-host -f Yellow "Scanning File ($Counter of $TotalFiles):"$Item.FieldValues.FileRef
    $VersionsCount = $Versions.Count
    $VersionsToDelete = $VersionsCount - $VersionsToKeep
    If($VersionsToDelete -gt 0)
    {
        write-host -f Cyan "`t Total Number of Versions of the File:" $VersionsCount
        #Delete versions
        For($i=0; $i -lt $VersionsToDelete; $i++)
        {
            write-host -f Cyan "`t Deleting Version:" $Versions[0].VersionLabel
            $Versions[0].DeleteObject()
        }
        $Ctx.ExecuteQuery()
        Write-Host -f Green "`t Version History is cleaned for the File:"$File.Name
    }
    $Counter++
}

How about cleaning up the version history for all document libraries on a site?

#Config Parameters
$SiteURL = "https://crescent.sharepoint.com/sites/marketing"
$VersionsToKeep = 5

Try { 
    #Connect to PnP Online
    Connect-PnPOnline -Url $SiteURL -Interactive

    #Get the Context
    $Ctx= Get-PnPContext

    #Exclude certain libraries
    $ExcludedLists = @("Form Templates", "Preservation Hold Library","Site Assets", "Pages", "Site Pages", "Images",
                            "Site Collection Documents", "Site Collection Images","Style Library")

    #Get All document libraries
    $DocumentLibraries = Get-PnPList | Where-Object {$_.BaseType -eq "DocumentLibrary" -and $_.Title -notin $ExcludedLists -and $_.Hidden -eq $false}

    #Iterate through each document library
    ForEach($Library in $DocumentLibraries)
    {
        Write-host "Processing Document Library:"$Library.Title -f Magenta

        #Get All Items from the List - Exclude 'Folder' List Items
        $ListItems = Get-PnPListItem -List $Library -PageSize 2000 | Where {$_.FileSystemObjectType -eq "File"}

        #Loop through each file
        ForEach ($Item in $ListItems)
        {
            #Get File Versions
            $File = $Item.File
            $Versions = $File.Versions
            $Ctx.Load($File)
            $Ctx.Load($Versions)
            $Ctx.ExecuteQuery()
 
            Write-host -f Yellow "`tScanning File:"$File.Name
            $VersionsCount = $Versions.Count
            $VersionsToDelete = $VersionsCount - $VersionsToKeep
            If($VersionsToDelete -gt 0)
            {
                write-host -f Cyan "`t Total Number of Versions of the File:" $VersionsCount
                #Delete versions
                For($i=0; $i -lt $VersionsToDelete; $i++)
                {
                    $Versions[0].DeleteObject()
                    Write-host -f Cyan "`t`t Deleted Version:" $Versions[0].VersionLabel
                }
                $Ctx.ExecuteQuery()
                Write-Host -f Green "`t Version History is cleaned for the File:"$File.Name
            }
        }
    }
}
Catch {
    write-host -f Red "Error Cleaning up Version History!" $_.Exception.Message
}

You can apply this cleanup to all files within a specific folder or sub-folder by:

$ListItems = Get-PnPListItem -List $ListName -FolderServerRelativeUrl $FolderServerRelativeURL -PageSize 2000

This script leaves the recent ‘N’ number of versions and deletes all previous versions of the documents. To clean up older versions in SharePoint Online using PowerShell CSOM, refer: SharePoint Online: Delete Version History using PowerShell CSOM

Don’t forget to set a limit for the number of versions under:  List/Library Settings >> Versioning settings to prevent any further versions growing beyond your limit.

Salaudeen Rajack

Salaudeen Rajack - SharePoint Expert with Two decades of SharePoint Experience. Love to Share my knowledge and experience with the SharePoint community, through real-time articles!

51 thoughts on “SharePoint Online: Delete Version History using PnP PowerShell

  • Hi Salaudeen,

    thanks for this code. In the code for delete all versions for all document libraries in a site, I’m trying to apply filter to only non-Microsoft files – so I tried putting in the check using $File.Name with the extension using notlike switch , but it does not seem to filter, it still deleting the Microsoft Office file versions. Any idea ? thanks.

    #Get File Versions
    $File = $Item.File
    $Versions = $File.Versions
    $Ctx.Load($File)
    $Ctx.Load($Versions)
    $Ctx.ExecuteQuery()

    if (($File.Name -notlike “.docx”) -or ($File.Name -notlike “.xlsx”) -or ($File.Name -notlike “.pptx”))
    {
    Write-host -f Yellow “`tScanning File:”$File.Name
    $VersionsCount = $Versions.Count
    $VersionsToDelete = $VersionsCount – $VersionsToKeep
    If($VersionsToDelete -gt 0)
    {
    write-host -f Cyan “`t Total Number of Versions of the File:” $VersionsCount
    #Delete versions
    For($i=0; $i -lt $VersionsToDelete; $i++)
    {
    $Versions[0].DeleteObject()
    Write-host -f Cyan “`t`t Deleted Version:” $Versions[0].VersionLabel
    }
    $Ctx.ExecuteQuery()
    Write-Host -f Green “`t Version History is cleaned for the File:”$File.Name
    }
    }

    Reply
    • Never mind found a solution using the following –
      $ListItems = Get-PnPListItem -List $Library -PageSize 2000 | Where {$_.FileSystemObjectType -eq “File” -and $_.FieldValues.File_x0020_Type -notin (“docx”,”xlsx”,”pptx”)}

      Reply
  • Hi Saludeen, I’m concerned about the performance overhead from having to loop through every file in every document library to remove the older versions of documents, when reducing from the default of 500 down to 10. In another article (https://www.sharepointdiary.com/2011/01/limit-versioning-and-delete-old-versions-in-sharepoint.html) you cover how to force an empty update on a list, like so

    #Perform a empty update – without creating new version, so that versioning changes will take effect
    $item.SystemUpdate($false)

    Is there a PNPonline equivalent approach that can be used, or do we need to iterate through every document in the library?

    Reply
  • This is fantastic, thank you! How can this be adjusted to run on sites with more than 999 files?

    Reply
  • Hi There,

    I see that more of the users are having this issue with the too many requests. “Exception calling “ExecuteQuery” with “0” argument(s):”

    I’ve read that you had 2 solutions for this:
    1. Try: $Ctx.RequestTimeout = [System.Threading.Timeout]::Infinite or call $Ctx.ExecuteQuery() after each Delete operation!

    2. You can mitigate by calling $Ctx.ExecuteQuery right after the operation such as: $Versions[0].DeleteObject()

    I am not that type of scripting guy, but when executing the second solution, it will return the same error.
    For the first solution i have no idea where to add this request timeout.

    Could you help me out?

    Thanks,
    Kind regards,
    Bart Maatje

    Reply
  • Would you be able to add line to write out to text file to tell what file was touched as log?

    Reply
  • I have found that after a week since I deleted a large amount of version history files that the total reported size of our tenant in the admin center has not reduced, despite the fact that the site where I deleted the version history is showing a lot less storage usage in the admin centre. I may have to open a MS ticket

    Reply
    • Try this:

      • Empty both the recycle bins
      • Exclude the site from any retention hold policy for time being (Before start deleting the versions)
      Reply
  • Hi Salsudeen,

    Many thanks for this! It is helping us clear out many excess files we no longer require.

    I was wondering if it would be possible to target a single file rather than document library?

    Are you able to help?

    Kind Regards

    Richard

    Reply
  • Hi there, thanks a lot for the scripts. They worked fine for me on smaller document libraries. However on some big ones I receive error 429. I think this means I am being throttled. I tried waiting 4 days before trying again and it happens again. I read on https://docs.microsoft.com/en-us/sharepoint/dev/general-development/how-to-avoid-getting-throttled-or-blocked-in-sharepoint-online I should use ExecuteQueryWithIncrementalRetry instead of ExecuteQuery, but this method is not known in my libraries. I cannot quite figure out how I use this extension method in a static class and call it in the script. Any idea how and whether this will be the solution to my issue?

    Reply
  • I´m struggling with Point 2. Would it be possible to get help with it?
    The part with Sitecollection Admin i get but i cant get it work with the deletion of the version history.

    Reply
  • Hi , I am using Keep last 5 version script . I have large site with more then 100000 files I need to filter modified day Gr 365 days and exclude Jpg ,JPGE files. Can you please help

    Reply
    • Use:
      $ListItems = Get-PnPListItem -List “Branding” -PageSize 2000 | Where {$_.FileSystemObjectType -eq “File” -and $_.FieldValues.Modified -lt (Get-Date).AddDays(-365) -and $_.FieldValues.File_x0020_Type -in (“jpg”,”jpeg”)}

      Reply
  • Hi Salaudeen,

    Any chance the “Keep five versions” script can be updated to loop through all libraries in a site?

    Thanks!

    Reply
  • ‘Keep Last Five Versions and Delete the Rest’

    Hi Salaudeen,

    Can this script above be updated to cycle through all libraries in a site? Thanks!

    Reply
  • Hi Salaudeen, the script works perfect, but the deleted versions are not showing in the recycle bin or the second stage recycle bin and the storage has not been freed up. Where can I find the deleted items so that I can free up the space?

    Reply
    • The delete method permanently deletes the object without sending it to the recycle bin. Use RecycleByID or RecycleByLabel methods to send it to recycle bin. SharePoint Online stores only the Delta of different versions on specific file types, and it takes a while for storage recalculations.

      Reply
  • Hi Salaudeen
    Works perfectly 🙂
    I have a question
    Is it possible to add a session that looks when the files was last modified and delete all history version for files that haven’t been modified the last 90 days ?

    Reply
    • Use: $ListItems = Get-PnPListItem -List $ListName -PageSize 2000 | Where {$_.FileSystemObjectType -eq “File” -and $_.FieldValues.Modified -lt (Get-Date).AddDays(-90)}

      Reply
  • Hello Salaudeen,

    thank you very much for the script. It has already helped me a lot to delete the old file versions (document library) within a single site collection (SharePoint Online). For it to work, it was necessary to add an extra Site Administrator account to the site and use those credentials for the script.

    I have run the script (case 3; keep 5 versions) on a test basis for a single site.
    https://example.sharepoint.com/teams/AAA

    Is there a way (via loop) to delete versions through all sites in a tenant?
    /teams/xxx
    /sites/xxx
    Using a sharepoint admin account (or global admin) without adding a separate site admin account to each individual team site (collection)?

    Supplemental question:
    After deleting the old versions on the test site (../teams/AAA), the SharePoint Online Admin Center does not show any reduction in memory usage.
    Is there a time delay here?

    Thanks for your help
    Greetings Tom

    Reply
    • 1. Even if you are a Global Admin, You must add your account as site collection Admin to each site in SharePoint Online, As there is no Web Application Policies in SharePoint Online.
      2. You can wrap this script inside a function and call the function for each site in your tenant.
      3. Yes! There is a time delay in recalculating the disk space!

      Reply
  • Accomplished my above request and have managed to cleanup most of our files. One File has 8000 versions and we get this error when when trying to clean it up: The request message is too big. The server does not allow messages larger than 2097152 and it errors out. How can you get it to work on a large set of versions.

    Also we were running into the time out issue. It cleans ups files but not all of them. We ended up just rerunning it until it was down to 100 versions.

    Reply
    • Yes! Larger requests may end up with timeout and related issues. You can mitigate by calling $Ctx.ExecuteQuery right after the operation such as: $Versions[0].DeleteObject()

      Reply
  • What if you have a CSV file with the Site Names and Library Names that have files over the limit. Can you read the sites from a CSV file?

    Reply
  • Hi, I’m running ‘PowerShell to Clear Previous Versions by Folder by Folder’

    Worked fine in my test environment of one folder level, but doesn’t run in my live environments with up to 20 folders depth.

    Could you let me know how many folders deep the script runs and if it can be adapted to run for all files and folders in a document library.

    Other than that, great script. Something Microsoft should have done !!!

    Reply
  • Hi, I’m running ‘PowerShell to Clear Previous Versions by Folder by Folder’

    Worked fine in my test environment of one folder level, but doesn’t run in my live environments with up to 20 folders depth.

    Could you let me know how many folders deep the script runs and if it can be adapted to run for all files and folders in a document library.

    Other than that, great script. Something Microsoft should have done !!!

    Reply
  • I am using a modified version of Keep Last Five Versions and Delete the Rest Script and its fantastic, my only problem is when the versions are large >100 as an example I get a timeout on the $Ctx.ExecuteQuery()

    Deleting Version: 2.137

    Exception calling “ExecuteQuery” with “0” argument(s): “The operation has timed out”

    I attempted to extend the time out by $Ctx.RequestTimeOut = 10000 or $Ctx.RequestTimeOut = 0 but the error still happens. Is there a way to execute them as batches?

    Reply
    • Try: $Ctx.RequestTimeout = [System.Threading.Timeout]::Infinite or call $Ctx.ExecuteQuery() after each Delete operation!

      Reply
    • Thanks very much for those options, I going to push my luck here.

      The request timeout didn’t work, I still get the same error. I am invoking it directly after this line 22 $Ctx.ExecuteQuery().

      If I try the other approach to run $Ctx.ExecuteQuery() inside here
      {
      write-host -f Cyan “`t Deleting Version:” $Versions[0].VersionLabel
      $Versions[0].DeleteObject()
      $Ctx.ExecuteQuery()

      }
      It doesn’t find the array item I am trying to remove and states that $Versions[0] is null.

      Am I missing something?

      Reply
    • Exception calling “ExecuteQuery” with “0” argument(s): “Cannot invoke method or retrieve property from null object. Object returned by the following call stack is null. “””

      is the exact error.

      Reply
  • Worked perfectly. Thank you!

    Reply
  • This comment has been removed by the author.

    Reply
  • Hi, can you just delete all versions but drop them into the recycle bin? DeleteAll permanently deletes them.

    Reply
  • I love most all your articles; however, this is one that did not work for me…at all. I had to enumerate through each version and $Version.DeleteObject()

    Received errors every time I used $Version.DeleteAll() [Method invocation failed because [Microsoft.SharePoint.Client.ListItemVersion] does not contain a method named ‘DeleteAll’]

    Reply
    • Well, Versioning features were added to PowerShell CSOM’s later build. I would suggest to update the PnP PowerShell module once and give it a try!
      Update-Module SharePointPnPPowerShellOnline

      Reply
    • That’s what I thought, too. I updated the module, but still nothing.

      I think this one’s a no-go for me, my friend. I still very much love the site – use it weekly!

      Thank you.

      Reply
    • The Version Collection object has DeleteAll(), But individual version has only DeleteObject() method!

      Reply
  • This comment has been removed by the author.

    Reply
  • Thank you for these!

    When using “Keep Last Five Versions and Delete the Rest” is there a way to specify the exact sub folder in a library? For example: .sharepoint.com/sites/marketing/documents/proposals/2019

    Reply
    • Use this line to get all files from a given folder:
      $ListItems = Get-PnPListItem -List $ListName -FolderServerRelativeUrl $FolderServerRelativeURL -PageSize 2000 | Where {$_.FileSystemObjectType -eq “File”}

      Reply
  • Hi this works a treat now you have done a great job on this thank you so much

    Reply
  • Hi there

    I ran the script but I get this message now

    Get-PnPListItem : The attempted operation is prohibited because it exceeds the list view threshold enforced by the administrator.

    Regards

    Eric

    Reply
  • I am getting the following errors, any idea?
    Exception calling “ExecuteQuery” with “0” argument(s): “Cannot invoke method or retrieve property from null object.
    Object returned by the following call stack is null. “File
    “”
    At line:10 char:5
    + $Ctx.ExecuteQuery()
    + ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ServerException

    Reply
    • Hey, I´ve got exactly the same problem. When I echo the $ctx variable I can see that the values for Versions and displayname are Null. Any way to fix that? :/

      Reply
      • When the $Ctx is null, It indicates the connection was not successful to SharePoint!

        Reply

Leave a Reply

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