SharePoint Online: Delete Version History using PowerShell

The versioning feature in SharePoint Online allows you to access previous copies of a document or list items. By default, versioning is enabled on your SharePoint Online document libraries. When a user makes edits, SharePoint automatically creates a new version with metadata such as created by, Timestamps, etc.

How to Delete Version History in SharePoint Online?

In this blog post, we’ll look at how to delete version history in SharePoint Online? This can come in handy if you need to clear out older versions of files that are taking up storage space or simply want to clean up previous versions. We’ll also show you how to delete version history in SharePoint Online quickly and easily using PowerShell.

To delete all previous versions of a document in SharePoint, follow these steps:

  • Navigate to your SharePoint Library, select the file and then click on “Version History” from the ribbon menu.sharepoint online delete version history using powershell
  • From the version history page, click on the “Delete all versions” link, confirm if prompted!sharepoint online delete versions

SharePoint Online: Delete Version History using PowerShell

You may delete all versions of a document or leave the latest ‘N’ number of versions and then delete the rest. Here are my PowerShell scripts to delete versions in SharePoint Online:

Case 1: Delete All Versions for a particular file:

This PowerShell script deletes all versions in SharePoint Online.

#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
  
#Config Parameters
$SiteURL= "https://crescent.sharepoint.com/sites/sales/"
$FileURL="/Sites/Sales/ProjectDocuments/ProjectMetrics.xlsx"

#Setup Credentials to connect
$Cred = Get-Credential
$Cred = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.UserName,$Cred.Password)

Try {
    #Setup the context
    $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
    $Ctx.Credentials = $Cred

    #Get the File
    $File = $Ctx.Web.GetFileByServerRelativeUrl($FileURL)

    #Get all versions of the file
    $Versions = $File.Versions
    $Ctx.Load($Versions)
    $Ctx.ExecuteQuery()

    Write-host -f Yellow "Total Number of Versions Found :" $Versions.count

    #Delete all versions of the file
    $Versions.DeleteAll()
    $Ctx.ExecuteQuery()

    Write-host -f Green "All versions Deleted for given File!"   
}
Catch {
    write-host -f Red "Error deleting versions!" $_.Exception.Message
}

Case 2: Delete All Versions for All Files in a Library:

This PowerShell script clears all previous Major versions from all files in a given document library.

#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
    
#Config Parameters
$SiteURL= "https://crescent.sharepoint.com/sites/Marketing"
$LibraryName="Branding"
$BatchSize = 500
  
#Setup Credentials to connect
$Cred = Get-Credential
$Cred = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.UserName,$Cred.Password)
  
Try {
    #Setup the context
    $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
    $Ctx.Credentials = $Cred
  
    #Get the web and Library
    $Web=$Ctx.Web
    $List=$web.Lists.GetByTitle($LibraryName)
   
    $Query = New-Object Microsoft.SharePoint.Client.CamlQuery
    $Query.ViewXml = "<View Scope='RecursiveAll'><Query><OrderBy><FieldRef Name='ID' Ascending='TRUE'/></OrderBy></Query><RowLimit Paged='TRUE'>$BatchSize</RowLimit></View>" 
    Do {
        #Get items from the list in batches
        $ListItems = $List.GetItems($Query)
        $Ctx.Load($ListItems)
        $Ctx.ExecuteQuery()
        $Query.ListItemCollectionPosition = $ListItems.ListItemCollectionPosition
    
        #Loop through each file in the library
        Foreach($Item in $ListItems | Where {$_.FileSystemObjectType -eq "File"})
        {
            #Get all versions of the file
            $Ctx.Load($Item.File)
            $Ctx.Load($Item.File.Versions)
            $Ctx.ExecuteQuery()
            Write-host "Processing Item:" $Item.file.Name
  
            #Delete all versions of the file
            If($Item.File.Versions.count -gt 0)
            {
                $Item.File.Versions.DeleteAll()
                $Ctx.ExecuteQuery()
                Write-host -f Green "`tAll Versions deleted on "$Item.file.Name
            }
        }
    } While ($Query.ListItemCollectionPosition -ne $null)
}
Catch {
    write-host -f Red "Error Deleting version History!" $_.Exception.Message
}

Case 3: Leave Last Five versions and Delete the Rest in the Library:

This time, let’s keep only the last “N” number of versions and trim the rest.

#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
  
#Config Parameters
$SiteURL= "https://crescent.sharepoint.com/sites/sales/"
$LibraryName="Project Documents"
$VersionsToKeep=5
$BatchSize = 500

#Setup Credentials to connect
$Cred = Get-Credential
$Cred = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.UserName,$Cred.Password)

Try {
    #Setup the context
    $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
    $Ctx.Credentials = $Cred
  
    #Get the web and Library
    $Web=$Ctx.Web
    $List=$web.Lists.GetByTitle($LibraryName)
 
    #Get all items from the library
    $Query = New-Object Microsoft.SharePoint.Client.CamlQuery
    $Query.ViewXml = "<View Scope='RecursiveAll'><Query><OrderBy><FieldRef Name='ID' Ascending='TRUE'/></OrderBy></Query><RowLimit Paged='TRUE'>$BatchSize</RowLimit></View>" 
    Do {
        #Get items from the list in batches
        $ListItems = $List.GetItems($Query)
        $Ctx.Load($ListItems)
        $Ctx.ExecuteQuery()
        $Query.ListItemCollectionPosition = $ListItems.ListItemCollectionPosition
 
        #Loop through each file in the library
        Foreach($Item in $ListItems | Where {$_.FileSystemObjectType -eq "File"})
        {
            #Get all versions of the file
            $Versions = $Item.File.Versions
            $Ctx.Load($Versions)
            $Ctx.Load($Item.File) #To get File Name
            $Ctx.ExecuteQuery()

            Write-host -f Yellow "Total Number of Versions Found in '$($Item.File.Name )' : $($Versions.count)"

            #Check if number of versions are more than limit
            While($Item.File.Versions.Count -gt $VersionsToKeep)
            {
                $Versions[0].DeleteObject()
                $Ctx.ExecuteQuery()
                Write-host -f Green "`tDeleted Version:" $Versions[0].VersionLabel
    
                #Reload versions
                $Ctx.Load($Item.File.Versions)
                $Ctx.ExecuteQuery()
            }
        }
    } While ($Query.ListItemCollectionPosition -ne $null)
}
Catch {
    write-host -f Red "Error deleting versions!" $_.Exception.Message
}

Trim Excess Versions for All Libraries in a Site

Let’s keep the last “N” number of versions and delete the rest, for all files from all document libraries of a SharePoint Online site:

#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"

Function Cleanup-SPOVersions
{
    param
    (
        [Parameter(Mandatory=$true)] [string] $SiteURL,
        [parameter(Mandatory=$false)][int]$VersionsToKeep = 100
    )
  
    #Config Parameters
    $BatchSize = 500

    #Exclude certain libraries
    $ExcludedLibraries = @("Form Templates", "Preservation Hold Library", "Site Assets","Site Pages", "Images",
                                "Site Collection Documents", "Site Collection Images","Style Library")
 
    #Setup Credentials to connect
    $Cred = Get-Credential
 
    Try {
        #Setup the context
        $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
        $Ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.UserName,$Cred.Password)
   
        #Get the web and Libraries
        $Web=$Ctx.Web
        $Ctx.Load($Web)
        $Ctx.Load($Web.Lists)
        $Ctx.executeQuery()

        #Get all document libraries in sharepoint online site
        $DocLibraries = $Web.Lists | Where {$_.BaseType -eq "DocumentLibrary" -and $_.Hidden -eq $False -and $_.Title -notin $ExcludedLibraries}

        #Loop through each document library and Get the Title
        Foreach ($Library in $DocLibraries)
        { 
            Write-host "Processing Document Library:"$Library.Title -f Yellow
            #Get all items from the library
            $Query = New-Object Microsoft.SharePoint.Client.CamlQuery
            $Query.ViewXml = "<View Scope='RecursiveAll'><Query><OrderBy><FieldRef Name='ID' Ascending='TRUE'/></OrderBy></Query><RowLimit Paged='TRUE'>$BatchSize</RowLimit></View>"
            Do {
                #Get items from the library in batches
                $ListItems = $Library.GetItems($Query)
                $Ctx.Load($ListItems)
                $Ctx.ExecuteQuery()
                $Query.ListItemCollectionPosition = $ListItems.ListItemCollectionPosition
  
                #Loop through each file in the library
                Foreach($Item in $ListItems | Where {$_.FileSystemObjectType -eq "File"})
                {
                    #Get all versions of the file
                    $Versions = $Item.File.Versions
                    $Ctx.Load($Versions)
                    $Ctx.Load($Item.File) #To get File Name
                    $Ctx.ExecuteQuery()

                    #Calculate if versions to be cleaned for the file
                    $VersionsToDelete = $Versions.Count - $VersionsToKeep
                    If($VersionsToDelete -gt 0)
                    { 
                        Write-host -f Yellow "`tTotal Number of Versions Found in '$($Item.File.ServerRelativeUrl)' : $($Versions.count)"
 
                        #Check if number of versions are more than limit
                        While($Item.File.Versions.Count -gt $VersionsToKeep)
                        {
                            $Versions[0].DeleteObject()
                            $Ctx.ExecuteQuery()
                            Write-host -f Green "`tDeleted Version:" $Versions[0].VersionLabel
     
                            #Reload versions
                            $Ctx.Load($Item.File.Versions)
                            $Ctx.ExecuteQuery()
                        }
                    }
                }
            } While ($Query.ListItemCollectionPosition -ne $null)
        }
    }
    Catch {
        write-host -f Red "Error deleting versions!" $_.Exception.Message
    }
}

#Call the function to cleanup versions
Cleanup-SPOVersions -SiteURL "https://crescent.sharepoint.com/sites/marketing" -VersionsToKeep 10

If you want to keep the last five versions and delete the rest for a particular file, use:

    #Get the File
    $File = $Ctx.Web.GetFileByServerRelativeUrl($FileURL)

    #Get all versions of the file
    $Versions = $File.Versions
    $Ctx.Load($Versions)
    $Ctx.ExecuteQuery()
    $Versionscount = $Versions.count

    Write-host -f Yellow "Total Number of Versions Found :" $Versionscount

    #Check if number of versions are more than limit
    While($File.Versions.Count -gt $VersionsToKeep)
    {
        write-host "Deleting Version:" $Versions[0].VersionLabel
        $Versions[0].DeleteObject()
        $Ctx.ExecuteQuery()
    
        #Reload versions
        $Ctx.Load($File.Versions)
        $Ctx.ExecuteQuery()
    }

To delete a particular version by its Label, use: $Versions.DeleteByLabel($LabelID) 

You can also delete versions in SharePoint Online using PnP PowerShell: SharePoint Online: Delete Version History using PnP PowerShell

Salaudeen Rajack

Salaudeen Rajack is a SharePoint Architect with Two decades of SharePoint Experience. He loves sharing his knowledge and experiences with the SharePoint community, through his real-world articles!

42 thoughts on “SharePoint Online: Delete Version History using PowerShell

  • Hi Dan, Any update yet? I have the same isssue.

    Reply
  • Hi Salaudeen Rajak, I just used you ps script to delete the version history, many thanks already, it worked like a charm!.
    But i have another question: is it possible to prevent sharepoint creating versions for specific extensions please?

    Reply
    • Hi Kelly,

      You can create a workflow that deletes the version based on the File extension.

      Reply
  • How would you implement Case #3 but with a specific folder instead of a whole library name? trying to add the specific folder path in the $LibraryName variable doesnt seem to work

    Reply
  • Hello,

    I have a client that was having storage issues. I discovered that they had set versioining to 500, reduced this. I then went to clean up the versioning history. Ran the below script, which worked well, however i inevitably get throttled with a 429 error.
    Id like to add the snytax to avoid the throttle, but all i can find is one that includes lists and not libriares (as you can see in my below script. Can someone help tell me what syntax i should add and where abouts in my script? Many thanks!

    #Load SharePoint CSOM Assemblies
    Add-Type -Path “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll”
    Add-Type -Path “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll”

    #Config Parameters
    $SiteURL= “https://XXXXX.sharepoint.com/chcfiles”
    $LibraryName=”operations”
    $VersionsToKeep=9

    #Setup Credentials to connect
    $Cred = Get-Credential
    $Cred = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.UserName,$Cred.Password)

    Try {
    #Setup the context
    $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
    $Ctx.Credentials = $Cred

    #Get the web and Library
    $Web=$Ctx.Web
    $List=$web.Lists.GetByTitle($LibraryName)

    #Get all items from the library
    $Query = New-Object Microsoft.SharePoint.Client.CamlQuery
    $Query.ViewXml = “$BatchSize”
    Do {
    #Get items from the list in batches
    $ListItems = $List.GetItems($Query)
    $Ctx.Load($ListItems)
    $Ctx.ExecuteQuery()
    $Query.ListItemCollectionPosition = $ListItems.ListItemCollectionPosition

    #Loop through each file in the library
    Foreach($Item in $ListItems | Where {$_.FileSystemObjectType -eq “File”})
    {
    #Get all versions of the file
    $Versions = $Item.File.Versions
    $Ctx.Load($Versions)
    $Ctx.Load($Item.File) #To get File Name
    $Ctx.ExecuteQuery()

    Write-host -f Yellow “Total Number of Versions Found in ‘$($Item.File.Name )’ : $($Versions.count)”

    #Check if number of versions are more than limit
    While($Item.File.Versions.Count -gt $VersionsToKeep)
    {
    $Versions[0].DeleteObject()
    $Ctx.ExecuteQuery()
    Write-host -f Green “`tDeleted Version:” $Versions[0].VersionLabel

    #Reload versions
    $Ctx.Load($Item.File.Versions)
    $Ctx.ExecuteQuery()
    }
    }
    } While ($Query.ListItemCollectionPosition -ne $null)
    }
    Catch {
    write-host -f Red “Error deleting versions!” $_.Exception.Message
    }

    Reply
  • Thanks for this. Trying to run Case 2 but get the following error after a couple of minutes. Any suggestions?
    Error Deleting version History! Exception calling “ExecuteQuery” with “0” argument(s): “Operation is not valid due to the current state of the object.”

    Reply
  • Hello Salaudeen
    First of all, many thanks for all your SPO scripts. They’re so usefull !
    In order to avoid throttling (error 429), I’m trying to implement the “Batch Execute CSOM PowerShell Scripts to Avoid 429 Resource Throttling Issue in SharePoint Online” method in these scripts (Case #3: delete 5 versions history).
    But I don’t know where the batch process should be added
    Could you please advise ?
    Best

    Reply
  • Ciao Salaudeen , thanks for your help! Unfortunately both with Powershell and with PnP I get the error “(401) Not authorized” when I execute these commands, unless I set the user who executes the command as “Admin” or “Additional Admin” on the site. Is there the possibility for a full Sharepoint admin to reduce the versions on all tenant sites (in a simple way, without having to set Admin on 400+ sites)?

    Reply
  • HI, did it’s possible to have a new SPO connection script plz (i use MFA and he refuse my access).

    Reply
  • is there a way to run these script being a full Sharepoint admin but not an owner of the Teams/Sharepoint?
    I would like tu run a script from an admin that cycle between every sharepoint site and clean the versioning

    Reply
    • Post is updated with a script to clean up all libraries in a site. You can call the function with the relevant site URL.

      Reply
  • Thank you. It appears to be working now as intended. Thank you for taking the time to freely share your knowledge on this subject! 🙂

    Reply
  • I am getting the error “The attempted operation is prohibited because it exceeds the list view threshold” how do I work around this

    Reply
  • Hi Salaudeen,
    Is it possible to make a script that delete version history on files that have not been used for the last 90 days ?

    Reply
  • Hi Salaudeen,
    I am getting the following error. could you please assist why is that?
    Error deleting versions! Exception calling “ExecuteQuery” with “0” argument(s): “Cannot contact site at the specified URL URL/Test/. There is no Web named “URL/_vti_bin/sites.asmx”.”

    Reply
  • I installed the SharePoint Online Powershell Module as instructed above, but get the following error when running the Case 1 script:

    New-Object : Cannot find type [Microsoft.SharePoint.Client.SharePointOnlineCredentials]: verify that the assembly containing this type is
    loaded.
    At line:7 char:9
    + $Cred = New-Object Microsoft.SharePoint.Client.SharePointOnlineCreden …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
    + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

    Error deleting versions! Cannot find type [Microsoft.SharePoint.Client.ClientContext]: verify that the assembly containing this type is loaded.

    Reply
  • Hello,

    I got the errors:
    “Add-Type : Cannot bind parameter ‘Path’ to the target. Exception setting “Path”: “Cannot find path ‘C:\Program Files\Common Files\Microsoft
    Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll’ because it does not exist.”

    “Add-Type : Cannot bind parameter ‘Path’ to the target. Exception setting “Path”: “Cannot find path ‘C:\Program Files\Common Files\Microsoft
    Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll’ because it does not exist.”

    “New-Object : Cannot find type [Microsoft.SharePoint.Client.SharePointOnlineCredentials]: verify that the assembly containing this type is loaded”

    Sorry, I’m not familiar with this commands. Also other scripts that pointed at does .dll files, I was able to run without problem eventhough I got the same error message.

    Reply
  • how can i use case2 to keep last 5 versions?

    Reply
  • $file.versions.count is not displaying the correct count. Its showing one less than the actual count. In the above screenshot there are 11 versions but $file.versions.count is showing 10. Can you please help?

    Reply
    • Yes, That’s right! Because the current version of the file is not actually considered as a “Version”.

      Reply
  • This would be a huge help – but I’m getting an error. When I merge this script with the Do/While for large lists I am receiving an error Error deleting versions! Cannot find an overload for “Load” and the argument count: “1”. Any suggestions?

    Reply
  • Hello. Thank you for this, it has helped enormously. I have some libraries that exceed the 5000 file limit and am unsure how to integrate the batch process into Case 2. Is that something you could help with please? Thanks

    Reply
  • Can you please share the script to keep top 5 versions and delete all else, to run it in PowerShell usinf PnP ?

    Reply
  • I am getting an Error that I do not know how to fix……..could someone help: — Error deleting versions! 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

    Reply
      • Hello I’ve got the same error with ExecuteQuery. Any idea how to fix it ?

        Reply
    • Salaudeen, Thanks for your quick. This is very helpful but I think many of us need help as our users keep adding files to multiple folders. Is possible to add a script targeting a specific folder and set it up as part of the variables. I believer We have admins here with multiple folders as me with more than 1000 items and set up to 2 or 3 versions that we are trying to keep “LEAN”……..if Possible We will Appreciate the help.

      Reply
    • Simple! Just append this line after line#25 in the case 3:
      $Query.FolderServerRelativeUrl=”/Project Docs/Active” (or whatever sub-folder path)

      Reply
    • This comment has been removed by the author.

      Reply
  • Does Case 2 cover all files in all folders? and is it possible to have it filtered to limit rows to 5000 on large sites ?
    Thanks !

    Reply
  • Hi Salaudeen,
    These are really great and helpful scripts.
    I’m using the script to delete all but 5 versions for all files in a library, but the script fails if the library has any sub-folders. Any ideas what could be added so that the sub-folders are ignored? Thanks!

    Reply

Leave a Reply