SharePoint Online PowerShell
There are two modules for SharePoint Online and they do different jobs.
Documentation: Microsoft Learn
Overview
Microsoft.Online.SharePoint.PowerShell provides the SPO cmdlets and operates at tenant scope: sites, quotas, sharing settings, tenant properties. Connect-SPOService needs the admin URL, the one with -admin in it, which is a frequent first stumble.
PnP.PowerShell operates within a site: lists, libraries, items, pages, permissions. Connect-PnPOnline connects to a specific site URL. It is community-maintained, considerably broader than the SPO module, and what most site-level scripting uses.
So tenant-level work uses SPO cmdlets, site-level work uses PnP cmdlets, and a script that does both needs two connections.
Get-SPOSite defaults to a limited number of results, so pass -Limit All for a full inventory. The same pattern as Exchange, for the same reason.
Throttling is aggressive in SharePoint Online. PnP cmdlets handle retries better than raw API calls, but a loop over thousands of items will still be slowed. Batch where you can and expect to run large operations in stages.
For automation, register an app registration with certificate authentication. Interactive connection in a scheduled script does not work.
Inspect this example’s structure
Read the command and its supplied arguments. This example requires real PowerShell or its module; this diagram does not execute it.
[pscustomobject]@{
Admin = 'Connect-SPOService -Url https://contoso-admin.sharepoint.com'
Sites = 'Get-SPOSite -Limit All | Select-Object Url, StorageQuota, Owner'
PnP = 'Connect-PnPOnline -Url https://contoso.sharepoint.com/sites/hr -Interactive'
Lists = 'Get-PnPList | Select-Object Title, ItemCount'
} | Format-List- Stage 1[pscustomobject]@{
Positional values or no named parameters - Stage 2Format-List
Positional values or no named parameters
An example to inspect
Preview the example. Sign in for the full lesson and activities.
[pscustomobject]@{
Admin = 'Connect-SPOService -Url https://contoso-admin.sharepoint.com'
Sites = 'Get-SPOSite -Limit All | Select-Object Url, StorageQuota, Owner'
PnP = 'Connect-PnPOnline -Url https://contoso.sharepoint.com/sites/hr -Interactive'
Lists = 'Get-PnPList | Select-Object Title, ItemCount'
} | Format-ListWhat happens
This prints both connection patterns side by side. The Admin line shows the -admin URL that Connect-SPOService requires; the PnP line shows a site URL instead. Seeing them together is the fastest way to remember which module needs which, and that a script touching both scopes needs both connections.
Worth remembering
- SPO cmdlets are tenant scope; PnP cmdlets are site scope.
- Connect-SPOService needs the -admin URL, not the site URL.
- Get-SPOSite needs -Limit All for a complete list.
- Throttling is aggressive; batch large operations.
Primary documentation
Try it
Expected output · prints command text; does not execute these commands
Example reader
Inspect this page's example
Run supported commands against the practice data, or inspect the example without executing it.
Ready to inspect.
Prints both SharePoint connection patterns and their scopes.
Show reference output
Expected output · prints command text; does not execute these commands
Admin : Connect-SPOService -Url https://contoso-admin.sharepoint.com Sites : Get-SPOSite -Limit All | Select-Object Url, StorageQuota, Owner PnP : Connect-PnPOnline -Url https://contoso.sharepoint.com/sites/hr -Interactive Lists : Get-PnPList | Select-Object Title, ItemCount
Keep going
Next: Teams PowerShellCheck your understanding
A SharePoint command is available, but querying your tenant fails. What do you check?
Check the connected tenant admin URL, account permissions and the reported error. Installing a module does not establish a tenant connection.