Skip to content

Commit

Permalink
Merge pull request #695 from tmaestrini/spo-get-existing-site-structure
Browse files Browse the repository at this point in the history
Get-SiteStructure: add new sample and documentation
  • Loading branch information
pkbullock authored May 8, 2024
2 parents 49e0f11 + 04b3959 commit cad1a47
Show file tree
Hide file tree
Showing 4 changed files with 327 additions and 0 deletions.
260 changes: 260 additions & 0 deletions scripts/spo-get-existing-site-structure/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
---
plugin: add-to-gallery
---

# 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:
1. initialize some variables,
1. analyze whether to start with a home site or hub site,
1. get informations about the selected root site,
1. get all assigned sites (and hubs),
1. 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 name
* `Version`: Timestamp of the execution
* `SharePoint`: High-level inforamtion about the tenant (for now, only the `TenantId`)
* `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:

```powershell
$adminUrl = "your SPO tenant admin url"
Connect-PnPOnline -Url $adminUrl -Interactive
```

Then, use the function `Get-SiteStructure`:

```powershell
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.:
```powershell
$result | ConvertTo-Json -Depth 4 > hub-structure.json
```


# [PnP PowerShell](#tab/pnpps)

```powershell
# 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 $_
}
}
```
[!INCLUDE [More about PnP PowerShell](../../docfx/includes/MORE-PNPPS.md)]
***

## Source Credit

Sample taken from [https://github.com/tmaestrini/easyProvisioning/](https://github.com/tmaestrini/easyProvisioning)

## Contributors

| Author(s) |
|-----------|
| [Tobias Maestrini](https://github.com/tmaestrini)|


[!INCLUDE [DISCLAIMER](../../docfx/includes/DISCLAIMER.md)]
<img src="https://m365-visitor-stats.azurewebsites.net/script-samples/scripts/spo-copy-hubsite-navigation" aria-hidden="true" />
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions scripts/spo-get-existing-site-structure/assets/sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
[
{
"name": "spo-get-existing-site-structure",
"source": "pnp",
"title": "Get (or export) an existing site structure in a SharePoint Online tenant",
"shortDescription": "Retrieve an existing structure of a SharePoint Online (SPO) tenant",
"url": "https://pnp.github.io/script-samples/spo-get-existing-site-structure/README.html",
"longDescription": [
"This sample contains 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."
],
"creationDateTime": "2024-05-05",
"updateDateTime": "2024-05-05",
"products": [
"SharePoint"
],
"metadata": [
{
"key": "PNP-POWERSHELL",
"value": "2.4.0"
},
{
"key": "POWERSHELL",
"value": "7.4.2"
}
],
"categories": [
"Data",
"Report",
"Site Structure",
"Provision",
"Information Architecture"
],
"tags": [
"Connect-PnPOnline",
"Get-PnPConnection",
"Get-PnPTenantInfo",
"Get-PnPTenantSite",
"Get-PnPHomeSite",
"Get-PnPHubSite",
"Get-PnPHubSiteChild",
"Get-PnPList",
],
"thumbnails": [
{
"type": "image",
"order": 100,
"url": "https://raw.githubusercontent.com/pnp/script-samples/main/scripts/spo-get-existing-site-structure/assets/preview.png",
"alt": "Preview of the sample <title>"
}
],
"authors": [
{
"gitHubAccount": "tmaestrini",
"company": "",
"pictureUrl": "https://avatars.githubusercontent.com/u/69770609?v=4",
"name": "Tobias Maestrini"
}
],
"references": [
{
"name": "Want to learn more about PnP PowerShell and the cmdlets",
"description": "Check out the PnP PowerShell site to get started and for the reference to the cmdlets.",
"url": "https://aka.ms/pnp/powershell"
}
]
}
]

0 comments on commit cad1a47

Please sign in to comment.