List Every List and Library in a SharePoint Site with PowerShell

You want a quick inventory of a SharePoint site: what lists and libraries are in it, how many items each holds, how big they are. It sounds like one command. It is, but the obvious version lies to you in two ways, and the way most people get sizes is the slow way.

Run against a real site on PnP.PowerShell 2.12.0.

Get-PnPList returns far more than you asked for

$all = Get-PnPList
$all.Count
Total lists returned : 19
Hidden               : 11
Visible              : 8

Nineteen, on a site where I created four things. The rest are the machinery SharePoint runs on: Solution Gallery, TaxonomyHiddenList, Theme Gallery, the user information list, and so on. Eleven of the nineteen are hidden.

So the first job is filtering. You want visible lists, and you want to keep it to actual lists and libraries rather than galleries and form-template stores. Two base templates cover that: 100 is a list, 101 is a document library.

Get-PnPList |
    Where-Object { -not $_.Hidden -and $_.BaseTemplate -in @(100, 101) } |
    Select-Object Title,
        @{n='Kind'; e={ if ($_.BaseTemplate -eq 101) { 'Library' } else { 'List' } }},
        ItemCount,
        @{n='LastChanged'; e={ $_.LastItemUserModifiedDate }} |
    Sort-Object Kind, Title
Title             Kind    ItemCount LastChanged
-----             ----    --------- -----------
Agent Knowledge   Library         4 22/07/2026
Project Documents Library        14 22/07/2026
Departments       List            4 18/07/2026
Projects          List           25 22/07/2026

LastItemUserModifiedDate is worth having in there. It is the last time a person changed something, which is how you spot the libraries nobody has touched in a year.

ItemCount is not the number of files

Look at Project Documents above, ItemCount 14. But there are not fourteen documents in it. I checked:

Project Documents ItemCount   : 14
Actual files                  : 8
Folders (also in ItemCount)   : 6

ItemCount counts folders as items. A library with eight files across six folders reports fourteen. If you are telling someone “this library has 14 documents”, you are wrong by however many folders it has.

For the real file count you have to ask, and filter by object type:

@(Get-PnPListItem -List 'Project Documents' -PageSize 500 |
    Where-Object { $_.FileSystemObjectType -eq 'File' }).Count

Same trap applies to a list with folders in it, which is rarer but does happen.

Size: the fast way and the slow way

This is where an inventory script quietly becomes an overnight job, so it is worth understanding the two approaches before you pick one.

The fast way: ask the site for its own number

Every site collection already knows how much storage it uses. One call gets it, but the call lives in the admin centre:

Connect-PnPOnline -Url "https://contoso-admin.sharepoint.com" -ClientId "..." -Interactive
$site = Get-PnPTenantSite -Identity "https://contoso.sharepoint.com/sites/Demo"
$site.StorageUsageCurrent
Fast: 1 MB in 805ms

One request, under a second, and it does not care whether the site has ten files or ten million. The catch is that it is a whole-site figure and it needs SharePoint admin. You cannot break it down per library this way, and you cannot run it as an ordinary site member.

The slow way: add up every file

If you need it per library, or you do not have admin, you sum the file sizes yourself:

$libraries = Get-PnPList | Where-Object { -not $_.Hidden -and $_.BaseTemplate -eq 101 }

foreach ($lib in $libraries) {
    $bytes = 0
    Get-PnPListItem -List $lib -PageSize 500 |
        Where-Object { $_.FileSystemObjectType -eq 'File' } |
        ForEach-Object { $bytes += [long]$_.FieldValues['File_x0020_Size'] }

    [pscustomobject]@{
        Library = $lib.Title
        SizeMB  = [math]::Round($bytes / 1MB, 2)
    }
}
Library           SizeMB
-------           ------
Agent Knowledge     0.07
Project Documents   0.07
...

Took : 3.7s for 5 libraries

Note the field name, File_x0020_Size, with the encoded space. That is the internal name, and it will not work as “File Size”. I wrote up why that happens in finding a list’s internal column names.

The gap is unbounded, not four times

On my tiny demo site the fast way took 805ms and the slow way 3.7 seconds, roughly four times slower. That ratio is not the point, because it does not hold.

The fast way is one call regardless of size. The slow way is one call per item. On five near-empty libraries that is nothing. On a library of fifty thousand documents it is fifty thousand round trips, and now you are measuring in minutes per library rather than seconds per site. The gap grows with every file you add.

So the rule is simple. If you have admin and you only need whole-site totals, use StorageUsageCurrent and be done in a second. Only drop to summing files when you genuinely need a per-library breakdown, and when you do, expect it to be slow and scope it to one site at a time.

The version I keep

Get-PnPList |
    Where-Object { -not $_.Hidden -and $_.BaseTemplate -in @(100, 101) } |
    Select-Object Title,
        @{n='Kind';  e={ if ($_.BaseTemplate -eq 101) { 'Library' } else { 'List' } }},
        @{n='Items'; e={ $_.ItemCount }},
        @{n='LastChanged'; e={ $_.LastItemUserModifiedDate }} |
    Sort-Object Kind, Title |
    Format-Table -AutoSize

Fast, honest about what it is showing, and no admin needed. It counts folders in the item total, so if that matters for what you are doing, add the FileSystemObjectType filter, and know that you are trading speed for accuracy 🙂