Create Navigation
This PnP PowerShell script automates the creation of navigation structure in SharePoint Online sites. It helps establish consistent navigation menus across your SharePoint environment, improving user experience and site organization.
Purpose
Navigation scripts enable you to:
- Standardize site navigation across multiple sites
- Create hierarchical menu structures programmatically
- Update navigation links efficiently
- Maintain consistent user experience
Prerequisites
- PnP PowerShell module installed
- Site owner or designer permissions
- Connection to your SharePoint Online site
PowerShell Script
# SharePoint Navigation Creation Script
# navigation-config.json and this script should be in the same directory
# Configuration
$siteUrl = "https://tenantName.sharepoint.com/sites/siteName" # You only need to specify the full site URL here
$ClientId = ""
$jsonFilePath = "createNavigation.json"
$logFilePath = ".\navigation-creation-log.csv"
$logEntries = @()
# Function to write to log file
function Write-ToLog {
param(
[string]$Action,
[string]$NavigationNode,
[string]$Status,
[string]$Message
)
$logEntry = [PSCustomObject]@{
Timestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
Action = $Action
NavigationNode = $NavigationNode
Status = $Status
Message = $Message
}
$logEntries += $logEntry
}
# Function to safely create navigation node
function Add-SafeNavigationNode {
param(
[string]$Title,
[string]$Location = "QuickLaunch",
[string]$Url,
[int]$Parent
)
try {
if ($Parent -and $Url) {
$node = Add-PnPNavigationNode -Location $Location -Title $Title -Url $Url -Parent $Parent -ErrorAction Stop
}
elseif ($Parent) {
$node = Add-PnPNavigationNode -Location $Location -Title $Title -Parent $Parent -ErrorAction Stop
}
else {
$node = Add-PnPNavigationNode -Location $Location -Title $Title -ErrorAction Stop
}
Write-ToLog -Action "Create" -NavigationNode $Title -Status "Success" -Message "Created navigation node"
return $node
}
catch {
Write-Host "Warning: Could not create node '$Title' with URL '$Url'. Creating without URL validation." -ForegroundColor Yellow
Write-ToLog -Action "Create" -NavigationNode $Title -Status "Warning" -Message "Created node without URL validation"
# Fallback: Create node without URL validation
if ($Parent) {
return Add-PnPNavigationNode -Location $Location -Title $Title -Parent $Parent
}
else {
return Add-PnPNavigationNode -Location $Location -Title $Title
}
}
}
# Function to build full URL from base URL and relative path
function Build-FullUrl {
param(
[string]$BaseUrl,
[string]$RelativePath
)
# Ensure the base URL doesn't end with a slash and the relative path starts with one
$BaseUrl = $BaseUrl.TrimEnd('/')
if (-not [string]::IsNullOrEmpty($RelativePath)) {
$RelativePath = $RelativePath.TrimStart('/')
if (-not [string]::IsNullOrEmpty($RelativePath)) {
return "$BaseUrl/$RelativePath"
}
}
return $BaseUrl
}
try {
# Connect to SharePoint Online
Write-Host "Connecting to SharePoint Online..." -ForegroundColor Yellow
Connect-PnPOnline -Url $siteUrl -Interactive -ClientId $ClientId
# Verify connection and permissions
try {
$site = Get-PnPWeb
Write-Host "Successfully connected to site: $($site.Title)" -ForegroundColor Green
# Test navigation access
$testNav = Get-PnPNavigationNode -Location QuickLaunch
Write-Host "Successfully accessed navigation" -ForegroundColor Green
} catch {
Write-Host "Error accessing site or navigation. Please verify permissions." -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Red
exit
}
Write-ToLog -Action "Connection" -NavigationNode "N/A" -Status "Success" -Message "Connected to SharePoint Online"
# Read JSON configuration file
$navConfig = Get-Content $jsonFilePath -Raw | ConvertFrom-Json
Write-ToLog -Action "ConfigRead" -NavigationNode "N/A" -Status "Success" -Message "Navigation configuration loaded"
# Get the base URL from the config
# Use the site URL from the script configuration as sitebase
$siteBaseUrl = $siteUrl
# Clear existing navigation
Write-Host "Clearing existing navigation..." -ForegroundColor Yellow
$existingNodes = Get-PnPNavigationNode -Location QuickLaunch
foreach ($node in $existingNodes) {
Remove-PnPNavigationNode -Identity $node.Id -Force
Write-ToLog -Action "Remove" -NavigationNode $node.Title -Status "Success" -Message "Removed existing navigation node"
}
# Create Level 1 navigation (Labels)
foreach ($level1 in $navConfig.level1) {
Write-Host "Creating Level 1 node: $($level1.title)" -ForegroundColor Green
# Build full URL for level 1 if it has a URL
$level1Url = $null
if ($level1.url) {
$level1Url = Build-FullUrl -BaseUrl $siteBaseUrl -RelativePath $level1.url
}
$level1Node = Add-SafeNavigationNode -Location QuickLaunch -Title $level1.title -Url $level1Url
# Create Level 2 navigation
if ($level1Node -and $level1.children) {
foreach ($level2 in $level1.children) {
Write-Host "Creating Level 2 node: $($level2.title)" -ForegroundColor Cyan
# Build full URL for level 2
$level2Url = $null
if ($level2.url) {
$level2Url = Build-FullUrl -BaseUrl $siteBaseUrl -RelativePath $level2.url
}
$level2Node = Add-SafeNavigationNode -Location QuickLaunch -Title $level2.title -Url $level2Url -Parent $level1Node.Id
# Create Level 3 navigation
if ($level2Node -and $level2.children) {
foreach ($level3 in $level2.children) {
Write-Host "Creating Level 3 node: $($level3.title)" -ForegroundColor White
# Build full URL for level 3
$level3Url = $null
if ($level3.url) {
$level3Url = Build-FullUrl -BaseUrl $siteBaseUrl -RelativePath $level3.url
}
$level3Node = Add-SafeNavigationNode -Location QuickLaunch -Title $level3.title -Url $level3Url -Parent $level2Node.Id
}
}
}
}
}
# Verify navigation was created
$finalNodes = Get-PnPNavigationNode -Location QuickLaunch
Write-Host "Created $($finalNodes.Count) navigation nodes" -ForegroundColor Green
# Export log to CSV
$logEntries | Export-Csv -Path $logFilePath -NoTypeInformation
Write-Host "Navigation creation completed. Check $logFilePath for details." -ForegroundColor Green
} catch {
Write-ToLog -Action "Error" -NavigationNode "N/A" -Status "Failed" -Message $_.Exception.Message
$logEntries | Export-Csv -Path $logFilePath -NoTypeInformation
Write-Host "An error occurred. Check $logFilePath for details." -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Red
} finally {
Disconnect-PnPOnline
Write-ToLog -Action "Connection" -NavigationNode "N/A" -Status "Success" -Message "Disconnected from SharePoint Online"
}
JSON Configuration File
{
"navigation": {
"topNavigation": [
{
"title": "Home",
"url": "/sites/yoursite",
"isExternal": false,
"children": []
},
{
"title": "Departments",
"url": "/sites/yoursite/departments",
"isExternal": false,
"children": [
{
"title": "IT Department",
"url": "/sites/yoursite/departments/it"
},
{
"title": "HR Department",
"url": "/sites/yoursite/departments/hr"
}
]
}
],
"quickLaunch": [
{
"title": "Documents",
"url": "/sites/yoursite/Shared Documents",
"isExternal": false
},
{
"title": "Lists",
"url": "/sites/yoursite/lists",
"isExternal": false
}
]
}
}
Usage Notes
- Customize navigation structure based on your site requirements
- Test navigation links before deploying to production
- Consider mobile responsiveness when creating navigation
- Update site URL to match your environment