Get (or export) an existing site structure in a SharePoint Online tenant
Summary
The Get-SiteStructure
function is a PowerShell function that could be part of a larger script or module.
Its purpose is to provide a function that retrieves an existing structure of a SharePoint Online (SPO) tenant
and its content for further processing.
The function takes in several parameters:
$RootSiteUrl
: The URL of the root site to start with.$WithSiteContent
: A switch parameter that determines whether the site content (libraries) should be included.$AsObject
: A switch parameter that determines whether the result will be returned as an object to be processed later.
The main function consists of several sub-functions define the analysis or export process:
- initialize some variables,
- analyze whether to start with a home site or hub site,
- get informations about the selected root site,
- get all assigned sites (and hubs),
- and optionally retrieve also site content (document libraries and lists).
Finally, Get-SiteStructure
returns the site structure, optionally as an object that can be processed later:
Tenant
: The tenant's nameVersion
: Timestamp of the executionSharePoint
: High-level inforamtion about the tenant (for now, only theTenantId
)Structure
: The retrieved structure, consisting of sites and site content (optional)
Note
The function needs an exisiting connection to the SPO admin center; at least SharePoint administrator rights are needed.
Usage
First of all, connect to the SPO admin center:
$adminUrl = "your SPO tenant admin url"
Connect-PnPOnline -Url $adminUrl -Interactive
Then, use the function Get-SiteStructure
:
Get-SiteStructure -RootSiteUrl "https://<yourtenant>.sharepoint.com/sites/<your(hub)site>" -WithSiteContent
# store the output as a structured object in a variable
$result = Get-SiteStructure -RootSiteUrl "https://<yourtenant>.sharepoint.com/sites/<your(hub)site>" -WithSiteContent -AsObject
Further processing can be done on the $result
object along your needs, e.g.:
$result | ConvertTo-Json -Depth 4 > hub-structure.json
# Initial parameters to start (connect to SPO admin center)
$adminUrl = "your SPO tenant admin url"
Connect-PnPOnline -Url $adminUrl -Interactive
Function Get-SiteStructure {
param (
[Parameter(HelpMessage = "The root site url to start with", Mandatory = $true)]
[string] $RootSiteUrl,
[Parameter(HelpMessage = "defines whether the site content (libraries) should be included", Mandatory = $false)]
[switch] $WithSiteContent,
[Parameter(HelpMessage = "defines whether the result will be returned as an object to be processed later", Mandatory = $false)]
[switch] $AsObject
)
Function Initialize-Routine {
$Script:RootSiteUrl = $RootSiteUrl
$Script:Structure = @()
$Script:TenantInfo = Get-PnPTenantInfo
$Script:RetrieveSiteContent = $WithSiteContent
Clear-Host
}
Function Get-SiteInfo([string] $SiteUrl) {
$siteInfo = Get-PnPTenantSite -Identity $SiteUrl
return @{
Id = $siteInfo.IsHubSite ? $siteInfo.HubSiteId : $siteInfo.Id
Title = $siteInfo.Title
Url = $siteInfo.Url
Type = $siteInfo.Template -eq "SITEPAGEPUBLISHING#0" ? "Communication"
: $siteInfo.Template -eq "GROUP#0" ? "Team"
: $siteInfo.Template -eq "STS#3" ? "SPOTeam"
: "Other"
IsHubSite = $siteInfo.IsHubSite
}
}
Function Get-StartSite {
Function Get-HomeSite() {
try {
Write-Host "Trying to get the home site in your tenant and check whether it matches the root site"
$homeSite = Get-PnPHomeSite -Detailed
if (!$homeSite) { throw "Home Site not found or not set" }
if ($homeSite.Url -ne $RootSiteUrl) { throw "Home Site does not match root site" }
$siteInfo = Get-SiteInfo -SiteUrl $hubSite.SiteUrl
$siteObject = ([Ordered]@{Hub = $siteInfo.Title; Url = $siteInfo.Url; Type = $siteInfo.Type })
if ($Script:RetrieveSiteContent) {
$content = Get-SiteContent -RootSite $siteInfo
if ($content) { $siteObject.Content = $content }
}
$Script:Structure += $siteObject
return $siteInfo
}
catch {
throw $_
}
}
Function Get-HubSite([string] $SiteUrl) {
try {
Write-Host "Trying to get the according hub site: " -NoNewline
$hubSite = Get-PnPHubSite -Identity $RootSiteUrl
if (!$hubSite) { throw "Hub Site not found or not set" }
Write-Host -ForegroundColor DarkGreen "✔︎ Starting with $($hubSite.SiteUrl)"
$siteInfo = Get-SiteInfo -SiteUrl $hubSite.SiteUrl
$siteObject = ([Ordered]@{Hub = $siteInfo.Title; Url = $siteInfo.Url; Type = $siteInfo.Type })
if ($Script:RetrieveSiteContent) {
$content = Get-SiteContent -RootSite $siteInfo
if ($content) { $siteObject.Content = $content }
}
$Script:Structure += $siteObject
return $siteInfo
}
catch {
throw $_
}
}
# Run the start site routine
try {
return Get-HomeSite
}
catch {
Write-Host -ForegroundColor DarkYellow $_
return Get-HubSite -SiteUrl $RootSiteUrl
}
}
Function Get-AssignedSites {
param (
[Parameter(HelpMessage = "the site from where the assigned sites will be retrieved", Mandatory = $true)]
[object] $RootSite
)
# Get all hubs that are assigned to this site site;
# either directly connected sites (first test) or connected hubs (second test)
$children = (Get-PnPHubSiteChild -Identity $RootSite.Url) ?? (Get-PnPHubSite | ? { $_.ParentHubSiteId -eq $RootSite.Id })
foreach ($site in $children) {
$siteInfo = switch ($site.GetType().FullName) {
"Microsoft.Online.SharePoint.TenantAdministration.SiteProperties" { Get-SiteInfo -SiteUrl $site.SiteUrl }
"System.String" { Get-SiteInfo -SiteUrl $site }
"Default" { Get-SiteInfo -SiteUrl $site }
}
Write-Host "👉 $($siteInfo.Url)"
# Get all assigned sites in case of current site is a hub site
if ($siteInfo.IsHubSite) {
$siteObject = ([Ordered]@{Hub = $siteInfo.Title; Url = $siteInfo.Url; Type = $siteInfo.Type; ConnectedHubsite = $RootSite.Url })
if ($Script:RetrieveSiteContent) {
$content = Get-SiteContent -RootSite $siteInfo
if ($content) { $siteObject.Content = $content }
}
$Script:Structure += $siteObject
Get-AssignedSites -RootSite $siteInfo
}
else {
$siteObject = ([Ordered]@{Site = $siteInfo.Title; Url = $siteInfo.Url; Type = $siteInfo.Type; ConnectedHubsite = $RootSite.Url })
if ($Script:RetrieveSiteContent) {
$content = Get-SiteContent -RootSite $siteInfo
if ($content) { $siteObject.Content = $content }
}
$Script:Structure += $siteObject
}
}
}
Function Get-SiteContent {
param (
[Parameter(HelpMessage = "the site from where the content will be retrieved", Mandatory = $true)]
[object] $RootSite
)
$output = @()
$connSite = Connect-PnPOnline -Url $RootSite.Url -Interactive -ReturnConnection
# Get the document libraries & lists
$objects = Get-PnPList -Connection $connSite | `
Where-Object { $_.BaseType -in @("DocumentLibrary", "GenericList", "Events") -and $_.Hidden -eq $false -and $_.EntityTypeName -notin @("Style_x0020_Library", "FormServerTemplates", "SiteAssets", "SitePages") }
if ($objects) {
foreach ($object in $objects) {
$output += switch ($object.BaseType) {
"DocumentLibrary" { [Ordered]@{ DocumentLibrary = $object.Title; Url = "/" + $object.RootFolder.ServerRelativeUrl.Split("/")[-1]; } }
"GenericList" { [Ordered]@{ List = $object.Title; Url = "/" + $object.RootFolder.ServerRelativeUrl.Split("/")[-1]; } }
"Default" { [Ordered]@{ List = $object.Title; Url = "/" + $object.RootFolder.ServerRelativeUrl.Split("/")[-1]; } }
}
}
}
$connSite = $null;
return $output
}
#######################################
# START Main Routine
try {
Initialize-Routine
$startSite = Get-StartSite
Get-AssignedSites -RootSite $startSite
$result = [Ordered]@{
Tenant = $Script:TenantInfo.DisplayName
Version = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
SharePoint = @{TenantId = $Script:TenantInfo.TenantId.ToString() }
Structure = $Script:Structure
}
if ($AsObject.IsPresent) {
return $result
}
else {
$result.Structure
}
}
catch {
Write-Error $_
}
}
Check out the PnP PowerShell to learn more at: https://aka.ms/pnp/powershell
The way you login into PnP PowerShell has changed please read PnP Management Shell EntraID app is deleted : what should I do ?
Source Credit
Sample taken from https://github.com/tmaestrini/easyProvisioning/
Contributors
Author(s) |
---|
Tobias Maestrini |
Disclaimer
THESE SAMPLES ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.