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

Set a Max File-Version Limit on All SharePoint Online Sites

Cap major version history on every document library across all SharePoint Online sites with PnP PowerShell and app-only authentication, to stop old versions from silently eating your tenant storage.

by Emanuel De Almeida

TL;DR

  • Applies a major-version limit to every visible document library across all SharePoint Online sites.
  • Runs app-only with PnP PowerShell and a certificate, so it works unattended.
  • Skips Style Library, Site Assets and libraries that do not have versioning enabled.
  • Caps the version history that quietly consumes tenant storage.
  • Set $Major_Versions to the limit you want (the script uses 25 by default).

What does this script do?

It connects to the SharePoint admin center, lists every site collection, and walks each visible document library, setting the maximum number of major versions SharePoint keeps per file. By default SharePoint can keep up to 500 versions per document, and every major version stores a full copy of the file, so a handful of large, frequently edited documents can balloon a site quota. Capping the limit keeps storage predictable.

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.
  • A client certificate uploaded to that app, with its thumbprint on the machine that runs the script.

Install the PnP module if you do not already have it:

shell
Install-Module PnP.PowerShell -Scope CurrentUser

How do you run it?

Set the connection variables and $Major_Versions, then run it. It changes settings on every site, so test on a pilot site first:

powershell
.\Set-MaxFileVersions.ps1

Progress is written to C:\Temp\Log. The change is non-destructive: lowering the limit does not delete existing versions, it only caps new ones; use the companion clean-up script if you also need to reclaim storage now.

Which libraries does it touch?

Only visible document libraries (base template 101). It deliberately skips Style Library and Site Assets, and any library where versioning is turned off, so it never force-enables versioning where you chose to leave it off.

FAQ

Does lowering the limit delete old versions?

No. Setting a lower major-version limit only affects versions created from then on. Existing versions stay until they age out or you remove them explicitly.

Why app-only instead of an interactive login?

App-only with a certificate lets the script run as a scheduled task with no user present and no stored password, which is what you want for a tenant-wide policy job.

The script

powershell-set-max-file-versions-all-sharepoint-sites.ps1
#Requires -Version 5.1
#Requires -Modules PnP.PowerShell
<#
.SYNOPSIS
    Sets a maximum major-version limit on document libraries across every
    SharePoint Online site.
.DESCRIPTION
    Connects to the SharePoint admin center with app-only (certificate)
    authentication, enumerates every site collection, and applies a major-
    version limit to each visible document library (skipping Style Library,
    Site Assets and libraries that do not have versioning enabled). This caps
    the version history that quietly consumes SharePoint storage.

    Fill in your tenant, admin URL, application (client) id and certificate
    thumbprint below, and set $Major_Versions. The app registration needs
    SharePoint app-only permissions (Sites.FullControl.All) to change list
    settings tenant-wide.

    This script CHANGES settings on every site. Test it on a pilot site first.
.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>'

# Maximum number of major versions to keep per document library
$Major_Versions = 25

# 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
}

# Make sure the log folder exists
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' -Type 'Information'
    $Sites = Get-PnPTenantSite
    Write-Log -Message "Retrieved $($Sites.Count) sites" -Type 'Information'
    Write-Log -Message "Applying the max-version policy ($Major_Versions) to all sites" -Type 'Information'

    Foreach ($Site in $Sites) {
        $Site_Connection = Connect-PnPOnline -Url $Site.Url -Tenant $Tenant -ClientId $Application_ID -Thumbprint $Certificate_Thumb_Print -ReturnConnection
        $Document_Libraries = Get-PnPList -Connection $Site_Connection |
            Where-Object { $_.BaseTemplate -eq 101 -and $_.Hidden -eq $false }

        Foreach ($Library in $Document_Libraries) {
            if ($Library.Title -in @('Style Library', 'Site Assets') -or -not $Library.EnableVersioning) {
                Continue
            }
            Write-Log -Message "Setting $Major_Versions major versions on '$($Library.Title)' in $($Site.Url)" -Type 'Information'
            Set-PnPList -Identity $Library -MajorVersions $Major_Versions -Connection $Site_Connection
        }
    }
    Write-Log -Message 'Version policy applied to all sites' -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'
}

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

#PowerShell#microsoft-365#sharepoint#Automation

Related topics