In this post, I am going to show you how to install PnP PowerShell and connect to SharePoint Online, and how to check the connection actually worked before you start running anything against it.
Everything below was run on PowerShell 7.5.8 with PnP.PowerShell 2.12.0. The output is what my machine printed, not what the documentation says it should print.
Install the module
Install-Module PnP.PowerShell -Scope CurrentUser
-Scope CurrentUser installs it under your profile so you do not need to run PowerShell as administrator.
If you have had PnP installed for a while you may get this instead of an install:
WARNING: Version '2.12.0' of module 'PnP.PowerShell' is already installed at
'C:\Program Files\WindowsPowerShell\Modules\PnP.PowerShell\2.12.0'. To install
version '3.3.0', run Install-Module and add the -Force parameter.
That is not an error. It found an older copy installed for all users and stopped rather than silently putting a second one next to it. Add -Force if you do want the newer version side by side.
Check what you have with:
Get-Module PnP.PowerShell -ListAvailable | Select-Object Name, Version, ModuleBase
Name Version ModuleBase
---- ------- ----------
PnP.PowerShell 2.12.0 C:\Program Files\WindowsPowerShell\Modules\PnP.PowerShell
Worth doing before anything else, because the syntax of several cmdlets changed between 2.x and 3.x and half the guides you will find online do not say which one they were written for.
You need an app registration first
This is the part that catches everyone coming back to an old script. You cannot just connect any more:
Connect-PnPOnline -Url "https://contoso.sharepoint.com/sites/Demo" -Interactive
WARNING: Connecting with -Interactive used the PnP Management Shell multi-tenant
App Id for authentication. As of September 9th, 2024 this option is not available
anymore.
Specified method is not supported.
The red line is the one everybody pastes into Google. The answer is in the yellow warning just above it, which most people scroll straight past.
Register your own app once per tenant:
Register-PnPEntraIDAppForInteractiveLogin `
-ApplicationName "PnP.PowerShell" `
-Tenant contoso.onmicrosoft.com `
-Interactive
On 2.x you must include -Interactive or you get an unhelpful “Parameter set cannot be resolved”. I wrote that up separately in Connect-PnPOnline: Specified method is not supported.
Copy the Client ID it gives you at the end. You need it every single time from now on.
Connect
Connect-PnPOnline `
-Url "https://contoso.sharepoint.com/sites/Demo" `
-ClientId "b1539324-4fa0-4bf0-8c45-949d49fbe112" `
-Interactive
Your browser opens, you sign in, and the command finishes with no output at all. That is normal – PnP tells you nothing when it works.
Check it actually connected
Because it says nothing on success, get into the habit of confirming:
Get-PnPWeb | Select-Object Title, Url, Id
Title : PowerShell Demo
Url : https://contoso.sharepoint.com/sites/Demo
Id : cfa3cc52-ce17-4c52-a7b8-89ae0c4f66e6
And if you want to see what you are connected as:
Get-PnPConnection | Select-Object Url, ConnectionType, ClientId
Url : https://contoso.sharepoint.com/sites/demo
ConnectionType : O365
ClientId : a0748433-4fa0-4bf0-8c45-949d49fbe112
The first thing everyone runs, and the first surprise
Get-PnPList | Select-Object Title, ItemCount, Hidden
On a site where I had made exactly four lists, that returned nineteen:
Title ItemCount Hidden
----- --------- ------
appdata 0 True
Composed Looks 18 True
Departments 4 False
Documents 0 False
Master Page Gallery 175 True
Project Documents 9 False
Projects 25 False
Site Pages 1 False
Style Library 0 False
TaxonomyHiddenList 0 True
Theme Gallery 41 True
User Information List 9 True
Web Part Gallery 6 True
Most of those are system lists SharePoint creates for itself. You almost always want:
Get-PnPList | Where-Object { -not $_.Hidden } | Select-Object Title, ItemCount
The same thing happens with folders. Listing a document library gives you a Forms folder you did not create:
Name Type
---- ----
Archive Folder
Contracts Folder
Forms Folder
Reports Folder
Forms holds the library’s form pages and is in every library on every site. If you are looping through folders to do something, filter it out or your script will try to process it.
Disconnect when you are done
Disconnect-PnPOnline
Not strictly required, but if you are switching between tenants in one session it saves you a confusing half hour wondering why your commands are hitting the wrong site ?