SharePointVault ~/scripts/lists-and-libraries ⌕ Search 404 pages, error messages, scripts…Ctrl K ★ Star
M↓ README.mdPS Get-ListInventory.ps1
sharepoint-vault›scripts›lists-and-libraries›Get-ListInventory.ps1historyedit on GitHub
● SCRIPTlists-and-libraries

Get-ListInventory.ps1

Full inventory of every list and library on a site: template, item count, versioning policy, content types, unique permissions and last activity.

⬇ downloadview on GitHub ↗170 lines · 6.8 KB
<#
.SYNOPSIS
    Full inventory of every list and library on a site: template, item count,
    versioning policy, content types, unique permissions and last activity.

.DESCRIPTION
    The report you build a migration plan, a versioning cleanup or a
    governance review on. One row per list, with the settings that actually
    cost money or cause incidents.

    READ-ONLY: this script makes no changes.

    Versioning is reported honestly, because SharePoint encodes "unlimited"
    as zero: MajorVersionLimit = 0 means *unlimited versions* when versioning
    is ON, and means nothing at all when versioning is OFF. A report that
    prints "0" in both cases invites somebody to conclude that the library
    keeps no versions, when in fact it keeps all of them forever.

    Module note: uses PnP.PowerShell - the official SharePoint Online
    Management Shell cannot enumerate lists or read their settings.

.PARAMETER SiteUrl
    One or more full site URLs to scan.

.PARAMETER ClientId
    Client ID of your own Entra ID app registration used by PnP.PowerShell.
    https://pnp.github.io/powershell/articles/registerapplication.html

.PARAMETER IncludeHidden
    Also report hidden lists (the plumbing SharePoint creates for itself).

.PARAMETER OutputPath
    CSV file to create. Defaults to a timestamped file in the current directory.

.EXAMPLE
    .\Get-ListInventory.ps1 -SiteUrl https://contoso.sharepoint.com/sites/intranet -ClientId 00000000-0000-0000-0000-000000000000

    Scanning https://contoso.sharepoint.com/sites/intranet ...
      30 list(s)/librar(ies) inventoried.

    Done. 30 row(s) written to .\ListInventory_20260729-143912.csv
    1 librar(ies)/list(s) keep UNLIMITED versions - the usual storage culprit.

    Two rows, showing why the raw numbers are never printed on their own:
    Title,Kind,ItemCount,VersioningEnabled,MajorVersionLimit,VersionPolicy
    Documents,Library,842,True,500,Keep 500 major versions
    Site Pages,Library,7,True,0,"Unlimited major versions, drafts kept for ALL
    major versions"

    Both zeroes on the second row mean "unlimited", not "none".

.EXAMPLE
    .\Get-ListInventory.ps1 -SiteUrl (Get-Content .\sites.txt) -ClientId 00000000-0000-0000-0000-000000000000 -IncludeHidden

.NOTES
    Requires : PnP.PowerShell 2.x or newer (Install-Module PnP.PowerShell)
    Auth     : Interactive (browser) sign-in per site.
    Samples  : scripts/sample-outputs.md - what this prints, from a real run
    Source   : https://github.com/EasyPortal365/sharepoint-vault
#>
#Requires -Modules PnP.PowerShell

[CmdletBinding()]
param(
    [Parameter(Mandatory = $true)]
    [string[]]$SiteUrl,

    [Parameter(Mandatory = $true)]
    [string]$ClientId,

    [switch]$IncludeHidden,

    [string]$OutputPath = ".\ListInventory_$(Get-Date -Format 'yyyyMMdd-HHmmss').csv"
)

$ErrorActionPreference = 'Stop'
$report = New-Object System.Collections.Generic.List[object]

$includes = @(
    'Hidden', 'ItemCount', 'BaseTemplate', 'BaseType', 'Created',
    'LastItemUserModifiedDate', 'EnableVersioning', 'EnableMinorVersions',
    'MajorVersionLimit', 'MajorWithMinorVersionsLimit', 'ContentTypesEnabled',
    'HasUniqueRoleAssignments', 'EnableAttachments', 'EnableFolderCreation',
    'DefaultViewUrl', 'ForceCheckout', 'NoCrawl'
)

foreach ($url in $SiteUrl) {
    Write-Host "Scanning $url ..." -ForegroundColor Cyan

    try {
        Connect-PnPOnline -Url $url -Interactive -ClientId $ClientId

        $lists = @(Get-PnPList -Includes $includes)
        if (-not $IncludeHidden) {
            $lists = @($lists | Where-Object { -not $_.Hidden })
        }

        foreach ($list in $lists) {
            # "0" is unlimited when versioning is on, and meaningless when it is off.
            $versionPolicy = 'Versioning OFF'
            if ($list.EnableVersioning) {
                if ($list.MajorVersionLimit -eq 0) {
                    $versionPolicy = 'Unlimited major versions'
                }
                else {
                    $versionPolicy = ('Keep {0} major versions' -f $list.MajorVersionLimit)
                }
                if ($list.EnableMinorVersions) {
                    # Zero is "unlimited" here too - printing the raw 0 reads as
                    # "keeps no drafts", the exact opposite of what it means.
                    if ($list.MajorWithMinorVersionsLimit -eq 0) {
                        $versionPolicy += ', drafts kept for ALL major versions'
                    }
                    else {
                        $versionPolicy += (', drafts for {0} major versions' -f $list.MajorWithMinorVersionsLimit)
                    }
                }
            }

            $report.Add([pscustomobject]@{
                SiteUrl            = $url
                Title              = $list.Title
                Url                = $list.DefaultViewUrl
                Kind               = if ($list.BaseType -eq 'DocumentLibrary') { 'Library' } else { 'List' }
                BaseTemplate       = $list.BaseTemplate
                ItemCount          = $list.ItemCount
                OverViewThreshold  = ($list.ItemCount -ge 5000)
                VersioningEnabled  = $list.EnableVersioning
                MinorVersions      = $list.EnableMinorVersions
                MajorVersionLimit  = $list.MajorVersionLimit
                VersionPolicy      = $versionPolicy
                ContentTypesOn     = $list.ContentTypesEnabled
                UniquePermissions  = $list.HasUniqueRoleAssignments
                ForceCheckout      = $list.ForceCheckout
                FolderCreation     = $list.EnableFolderCreation
                ExcludedFromSearch = $list.NoCrawl
                Hidden             = $list.Hidden
                Created            = $list.Created
                LastItemModified   = $list.LastItemUserModifiedDate
            })
        }

        Write-Host ("  {0} list(s)/librar(ies) inventoried." -f $lists.Count) -ForegroundColor Green
    }
    catch {
        Write-Warning "Failed to scan ${url}: $($_.Exception.Message)"
    }
}

Write-Host ''
if ($report.Count -gt 0) {
    $report | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
    Write-Host ("Done. {0} row(s) written to {1}" -f $report.Count, (Resolve-Path $OutputPath)) -ForegroundColor Green

    $unlimited = @($report | Where-Object { $_.VersionPolicy -eq 'Unlimited major versions' })
    if ($unlimited.Count -gt 0) {
        Write-Host ("{0} librar(ies)/list(s) keep UNLIMITED versions - the usual storage culprit." -f $unlimited.Count) -ForegroundColor Yellow
    }

    $big = @($report | Where-Object { $_.OverViewThreshold })
    if ($big.Count -gt 0) {
        Write-Host ("{0} are past the 5,000-item view threshold." -f $big.Count) -ForegroundColor Yellow
    }
}
else {
    Write-Host 'Done. Nothing to export.' -ForegroundColor Green
}

try { Disconnect-PnPOnline } catch { }
Was this helpful?ask or add your experience on GitHub →
Saved you an hour? A star on GitHub helps other SharePoint people find the vault.★ Star on GitHub
last updated: 2026-09-23historyedit on GitHubview source