A version-storage audit almost always ends with one surprise: a single, boring file — Sales dashboard.xlsx, export.csv, status.pptx — with more versions than the rest of the library combined. Nobody edited it 3,000 times. Something did.
Find the files
Connect-PnPOnline -Url https://contoso.sharepoint.com/sites/Sales -Interactive
$lib = 'Documents'
Get-PnPListItem -List $lib -PageSize 2000 -Fields FileRef,File_x0020_Size |
Where-Object FileSystemObjectType -eq 'File' |
ForEach-Object {
$v = Get-PnPFileVersion -Url $_['FileRef']
[pscustomobject]@{ Path = $_['FileRef']; Versions = $v.Count; VersionsMB = [math]::Round(($v | Measure-Object Size -Sum).Sum/1MB,0) }
} |
Sort-Object Versions -Descending | Select-Object -First 20 | Format-Table -AutoSize
It is one call per file, so on a 100,000-file library run it per folder or overnight. The top five are what you came for.
Identify the writer
Open the file → Version history. Look at Modified By and the spacing of the timestamps:
- Same account, every 15 / 30 / 60 minutes, around the clock — a scheduled flow, a Power BI dataflow, a script, or an Excel data connection with refresh on open in a workbook someone leaves open. The account is usually a service account or the flow owner.
- Same account, seconds apart, in bursts — a sync client conflict loop: two devices disagreeing and re-uploading. Often shows as
filename-DESKTOP-ABC123.xlsxconflict copies nearby. - Many people, every few minutes during office hours — Office autosave on a genuinely shared file. Legitimate, but a hard version limit or automatic versioning will keep it bounded.
System Account— a workflow or a retention/label action rewriting metadata.
The audit log confirms it: FileModified events on that path, filtered by user agent (OneDrive, Power Automate, Microsoft.Data.Mashup).
Fix the writer
- Flow: add a change check (compare hash or Modified with the source) before writing; write only when different.
- Excel data connection: move the refresh to a Power BI dataset, or turn off refresh data when opening the file on a file that lives in SharePoint.
- Sync loop: sign the client out, delete the local conflict copies, re-add as a shortcut rather than a full sync.
- Scheduled re-upload of the same export: overwrite is fine, but set that library's version limit low (PowerShell allows below 100) or put exports in a library with versioning off.
Then reclaim the space
For one file: Version history → Delete All Versions (keeps the current one). In PowerShell:
Remove-PnPFileVersion -Url '/sites/Sales/Shared Documents/Sales dashboard.xlsx' -All -Force
For a library where several files are affected, a version trim with a count limit does it in one job; see how to trim existing versions. Either way it is permanent, so look first.
The pattern is the point
The reason to rank files by versions rather than just set a limit is that the limit hides the cause. A library with a 100-version cap and a flow saving a file every ten minutes stays at 100 versions of that file forever — 100 × 40 MB, refreshed daily, still counting against the quota. Ranking by historical versions per file, which TrimVersions365's free analysis shows for every library on the site, is how you find the writer.