Table of Contents
Different Cmdlets and Methods Exist to Fetch SharePoint Sites with PowerShell
If you’ve used the Microsoft Graph PowerShell SDK to work with SharePoint Online and OneDrive for Business, you know that two cmdlets are available to retrieve sites. I cover the use of the two cmdlets in this article, but in summary:
- Get-MgSite is based on the Get Site API and works in both delegated and app-only mode. In delegated mode, the cmdlet can retrieve sites that the signed-in account owns or belongs to. In app-only mode, the cmdlet can retrieve all sites in the tenant when the -All parameter is used.
- Get-MgAllSite is based on the getAllSites API, which is designed exclusively for app-only mode access to all sites in the tenant. Unlike Get-MgSite, this cmdlet supports server-side filtering to find specific sets of sites. Get-MgAllSite supports multi-geo tenants and can find sites from all geographies.
If you’re in a single-geo tenant and want to retrieve all sites, either cmdlet works when run in app-only mode.
Filtering is Important for Performance
Retrieving every site in a tenant isn’t always the best approach, especially when performance matters. Even my relatively small tenant contains 628 sites, including SharePoint Online team sites connected to Microsoft 365 groups, hub sites, communication sites, sites connected to Teams private and shared channels, and personal OneDrive for Business sites. The SharePoint admin center (Figure 1) includes views and search to make it easier to find the right sites. Not all the views are available as Graph filters.

Fetching every site and then discarding the sites that aren’t relevant adds unnecessary overhead and can slow scripts to a crawl, particularly in large tenants. This is where server-side filtering is essential. Although Get-MgSite supports search, it doesn’t support filtering. Get-MgAllSite is therefore the better choice, assuming that its available filters can solve the automation problem you’re trying to address.
Get-MgAllSite supports server-side filtering against the displayName, createdDateTime, and isPersonalSite properties. The cmdlet also supports the startsWith operator when filtering site display names. All of which is great if you want to find or exclude OneDrive for Business sites, or sites created before or after a specific time, or sites named in a particular way. Unfortunately, the limited set of filtering capabilities often cannot deal with common scenarios, such as:
- Find sites connected to Microsoft 365 groups.
- Find sites connected to Teams.
- Find sites connected to Teams private or shared channels.
- Find sites created using a specific site template.
Because the getAllSites API does not expose the necessary properties, neither server-side filtering nor client-side filtering of the output returned by a query can identify these site types. I recently ran into the problem when attempting to discover how many files were moved to Microsoft 365 Archive after adding an archive action to retention labels. Only some sites were configured with default retention labels with an archive action, so I wanted to target those sites.
Use Other Cmdlets to Get to Sites
The solution is to use other cmdlets to find site-associated objects and then navigate from objects to linked sites. For example, to find sites connected to Microsoft 365 groups, start by running the Get-MgGroup cmdlet to find all Microsoft 365 groups:
[array]$Groups = Get-MgGroup -Filter "groupTypes/any(c:c eq 'unified')" -All
The Get-MgGroup cmdlet supports many properties for groups, so its filtering is more extensive than for sites. After finding the target set of groups, use the Get-MgGroupSite cmdlet to find the linked site. It’s important to include “root” as the input site identifier because this tells SharePoint Online to search for the root site for the group:
$Site = Get-MgGroupSite -GroupId $Group.Id -SiteId "root"
The $Site variable now contains the all-important site identifier (in $Site.Id), which can be used with other site cmdlets. For example, to find the default document library for the site, run the Get-MgSiteDefaultDrive cmdlet:
$Drive = Get-MgSiteDefaultDrive -SiteId $SiteId
The same approach works with the Get-MgTeam cmdlet to find team-connected sites. In this example, we find all teams and retrieve the site for the first team in the set (the team identifier has the same value as the group identifier):
[array]$Teams = Get-MgTeam -All $Team = $Teams[0] $Site = Get-MgGroupSite -GroupId $Team.Id -SiteId "root"
More work is needed to find sites connected to Teams channels. Private and shared channels store files in separate SharePoint sites with access limited to channel members. The Graph does not expose a direct site identifier for channel sites, but it does expose the document library used by a channel (visible through the Shared tab for the channel). Because the drive object includes the document library URL, it’s possible to derive a site identifier from that URL. This example uses the team (group) identifier to check each channel to see if it is shared or private, and if so, retrieve its details, including the site identifier:
$Channels = Get-MgTeamChannel -TeamId $TeamId
ForEach ($Channel in $Channels) {
If ($Channel.MembershipType -in @("private","shared")) {
$Folder = Get-MgTeamChannelFileFolder -TeamId $TeamId -ChannelId $Channel.Id
$Drive = Get-MgDrive -DriveId $Folder.ParentReference.DriveId
$SiteUrl = $Drive.WebUrl -replace "/Shared%20Documents.*$"
$Uri = [System.Uri]$SiteUrl
$SiteId = "{0}:{1}:" -f $Uri.Host, $Uri.AbsolutePath
[PSCustomObject]@{
Channel = $Channel.DisplayName
Type = $Channel.MembershipType
SiteId = $SiteId
}
}
}
Finding sites created using a specific template requires using the Get-SPOSite cmdlet. This command fetches all sites created using the template for Microsoft 365 groups:
[array]$Sites = Get-SPOSite -Template "Group#0" -Limit $All
This is an example of when the SharePoint Online module is useful because it exposes many other site properties than are available through the Graph APIs.
Understanding How the Graph Works
Understanding the Graph relationships between objects is often more important than finding a specific API to solve a problem. Once you know how sites relate to groups, teams, channels, drives, and document libraries, you can often work around apparent limitations such as the lack of property filtering in Get-MgAllSite.
Microsoft might sometimes be criticized for providing multiple ways to accomplish a task, but sometimes that variety is very useful!
Need help to write and manage PowerShell scripts for Microsoft 365, including Azure Automation runbooks? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Microsoft 365 for IT Pros eBook bundle.