SharePoint site storage and library version settings report
List every SharePoint site with its storage used, plus each document library's versioning settings and major version limit, in one CSV. Use it to decide how many versions to migrate and which libraries will take longest.
What you need
Modules and tools
Admin roles
- SharePoint Administrator (to list all sites from the admin URL)
- Read access to each site you want to report on (a SharePoint Administrator is not automatically a member of every site)
Runs on
- PowerShell 7.4 or later on Windows, macOS or Linux (PnP.PowerShell 3.x requirement)
Options you can set
| Parameter | What it does |
|---|---|
-AdminUrl | Required. Your SharePoint admin URL, for example https://contoso-admin.sharepoint.com. |
-ClientId | Required. The Application (client) ID of your PnP app registration in Entra ID. |
-SiteUrlFilter | Optional. Only report sites whose URL contains this text, for example /sites/finance. Default: all sites. |
-IncludeHiddenLists | Optional switch. Also report hidden document libraries. |
-OutputPath | Optional. Where to save the CSV. Default: SharePointVersionReport_yyyyMMdd_HHmm.csv in the current folder. |
Step by step
- 1
Step 1
Install PowerShell 7.4 or later, then open it and run
Install-Module PnP.PowerShell -Scope CurrentUser. - 2
Step 2
If your organisation does not already have a PnP app registration, have an admin who can register apps and grant admin consent in Entra ID run
Register-PnPEntraIDAppForInteractiveLogin -ApplicationName "PnP PowerShell" -Tenant contoso.onmicrosoft.comand note the client ID it returns. - 3
Step 3
Save the script as
Get-CMSharePointVersionReport.ps1in a working folder. - 4
Step 4
Run
./Get-CMSharePointVersionReport.ps1 -AdminUrl https://contoso-admin.sharepoint.com -ClientId 00000000-0000-0000-0000-000000000000and sign in. You may be asked to sign in again for some sites. - 5
Step 5
Open the CSV. Sort by MajorVersionLimit and SiteStorageUsageCurrent to find the libraries that will carry the most version history.
What you get: A UTF-8 CSV with one row per document library: SiteUrl, SiteTitle, SiteTemplate, SiteStorageUsageCurrent, SiteStorageQuota, LibraryTitle, LibraryUrl, ItemCount, EnableVersioning, MajorVersionLimit, EnableMinorVersions, MajorWithMinorVersionsLimit and Error. A site that cannot be opened gets one row with the Error column filled.
Get-CMSharePointVersionReport.ps1
<#
.SYNOPSIS
Get-CMSharePointVersionReport.ps1
.DESCRIPTION
Reports storage used per SharePoint site and the versioning settings of every
document library (versioning on or off, major version limit, minor versions).
Read-only: it changes nothing in your tenant.
Why PnP.PowerShell: SharePoint Online Management Shell (Get-SPOSite) reports site
storage, but it has no cmdlet that lists the document libraries in a site.
PnP.PowerShell's Get-PnPList does, so this script uses PnP for both parts.
Provided by cloud-migrations.au as is; test before use.
Read the script before you run it and try it in a test tenant first.
.NOTES
Requirements
- PowerShell 7.4 or later (required by PnP.PowerShell 3.x)
- Install-Module PnP.PowerShell -Scope CurrentUser
- An Entra ID app registration for PnP (Register-PnPEntraIDAppForInteractiveLogin)
- SharePoint Administrator, plus read access to the sites you report on
Commands used (documented by the PnP PowerShell project):
Connect-PnPOnline -Url -Interactive -ClientId, Get-PnPTenantSite, Get-PnPList -Includes
List properties (EnableVersioning, MajorVersionLimit, EnableMinorVersions,
MajorWithMinorVersionsLimit, BaseTemplate, Hidden) are SharePoint List properties
documented by Microsoft. BaseTemplate 101 is a document library.
.EXAMPLE
./Get-CMSharePointVersionReport.ps1 -AdminUrl https://contoso-admin.sharepoint.com -ClientId 00000000-0000-0000-0000-000000000000
.EXAMPLE
./Get-CMSharePointVersionReport.ps1 -AdminUrl https://contoso-admin.sharepoint.com -ClientId 00000000-0000-0000-0000-000000000000 -SiteUrlFilter /sites/finance
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)][string]$AdminUrl,
[Parameter(Mandatory = $true)][string]$ClientId,
[string]$SiteUrlFilter = '',
[switch]$IncludeHiddenLists,
[string]$OutputPath = (Join-Path -Path (Get-Location) -ChildPath ('SharePointVersionReport_{0}.csv' -f (Get-Date -Format 'yyyyMMdd_HHmm')))
)
$ErrorActionPreference = 'Stop'
# Copy the site columns into a new ordered row
function Copy-CMRow {
param($Source)
$copy = [ordered]@{}
foreach ($k in $Source.Keys) { $copy[$k] = $Source[$k] }
return $copy
}
# 1. Connect to the admin URL and list sites (Get-PnPTenantSite leaves out OneDrive sites by default)
try {
Write-Host "Connecting to $AdminUrl ..."
$adminConn = Connect-PnPOnline -Url $AdminUrl -Interactive -ClientId $ClientId -ReturnConnection
Write-Host 'Reading the list of sites...'
$sites = @(Get-PnPTenantSite -Connection $adminConn)
}
catch {
Write-Error "Could not connect or list sites: $($_.Exception.Message)"
return
}
if ($SiteUrlFilter) { $sites = @($sites | Where-Object { $_.Url -like "*$SiteUrlFilter*" }) }
Write-Host ("Found {0} sites to check." -f $sites.Count)
$results = New-Object System.Collections.Generic.List[object]
$i = 0
foreach ($site in $sites) {
$i++
Write-Progress -Activity 'Reading library version settings' -Status ("{0} of {1}: {2}" -f $i, $sites.Count, $site.Url) -PercentComplete (($i / [math]::Max($sites.Count, 1)) * 100)
$base = [ordered]@{
SiteUrl = $site.Url
SiteTitle = $site.Title
SiteTemplate = $site.Template
SiteStorageUsageCurrent = $site.StorageUsageCurrent
SiteStorageQuota = $site.StorageQuota
}
try {
$siteConn = Connect-PnPOnline -Url $site.Url -Interactive -ClientId $ClientId -ReturnConnection
$lists = Get-PnPList -Connection $siteConn -Includes EnableVersioning, MajorVersionLimit, EnableMinorVersions, MajorWithMinorVersionsLimit, BaseTemplate, Hidden, ItemCount, RootFolder
$libraries = @($lists | Where-Object { $_.BaseTemplate -eq 101 -and ($IncludeHiddenLists -or -not $_.Hidden) })
foreach ($lib in $libraries) {
$row = Copy-CMRow $base
$row.LibraryTitle = $lib.Title
$row.LibraryUrl = $lib.RootFolder.ServerRelativeUrl
$row.ItemCount = $lib.ItemCount
$row.EnableVersioning = $lib.EnableVersioning
$row.MajorVersionLimit = $lib.MajorVersionLimit
$row.EnableMinorVersions = $lib.EnableMinorVersions
$row.MajorWithMinorVersionsLimit = $lib.MajorWithMinorVersionsLimit
$row.Error = ''
$results.Add([pscustomobject]$row)
}
}
catch {
$row = Copy-CMRow $base
$row.LibraryTitle = ''; $row.LibraryUrl = ''; $row.ItemCount = ''
$row.EnableVersioning = ''; $row.MajorVersionLimit = ''; $row.EnableMinorVersions = ''
$row.MajorWithMinorVersionsLimit = ''
$row.Error = $_.Exception.Message
$results.Add([pscustomobject]$row)
}
}
Write-Progress -Activity 'Reading library version settings' -Completed
# 2. Export
try {
$results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
$failed = @($results | Where-Object { $_.Error }).Count
Write-Host ("Done. {0} rows written, {1} sites could not be read." -f $results.Count, $failed)
Write-Host "Report saved to: $((Resolve-Path -Path $OutputPath).Path)"
}
catch {
Write-Error "Could not write the CSV to $OutputPath : $($_.Exception.Message)"
}Commands documented here
Every command in the script is documented on these vendor pages. Test in a non-production tenant or environment first.