Create SharePoint SPO Navigation links with PowerShell and CSV for Top Navigation and Quick Launch
Step by step demo how to populate the Top Navigation bar with PowerShell PS1 and CSV automation. Attention given to parent > child relationship and how links are nested for compatibility to many branding scenarios. Also supports both QuickLaunch (left) and TopNavigation (top) placement. Cheers
VIDEO
CODE – PS1
function Main() {
# Load PnP PowerShell Module
#Install-Module "PnP.PowerShell" -MaxVersion 1.13 -Scope CurrentUser
Import-Module "PnP.PowerShell"
# Target SharePoint site URL
$tenant = "https://test.sharepoint.com"
$url = "https://spjeffdev.sharepoint.com/sites/Communication"
$location = "QuickLaunch"
# Connect to SharePoint Online site
Connect-PnPOnline -Url $url -UseWebLogin
# Get the current web to display Title and URL
Get-PnPWeb
# Read current top navigation
$topNav = Get-PnPNavigationNode -Location $location
Write-Host "Current Top Navigation Links:"
foreach ($node in $topNav) {
Write-Host "- $($node.Title): $($node.Url)"
}
# Read CSV with schema (ID,Parent,Title,URL) of desired top navigation links including primary key ID and ParentID for parent-child relationships
$csvPath = ".\Fill-Top-Nav.csv"
$topNavLinks = Import-Csv -Path $csvPath
# Create top navigation links (if missing) from CSV
foreach ($link in $topNavLinks) {
$existingLink = Get-PnPNavigationNode -Location $location | Where-Object { $_.Title -eq $link.Title }
if (-not $existingLink) {
Write-Host "Adding top navigation link: $($link.Title)"
# For parent-child relationships, find the parent node by ParentID
if ($link.ParentID -ne 0) {
$parentLink = $topNavLinks | Where-Object { $_.ID -eq $link.ParentID }
$parentNode = Get-PnPNavigationNode -Location $location | Where-Object { $_.Title -eq $parentLink.Title }
if ($parentNode) {
# SCENARIO 1 - Create with Parent
$url = $tenant + $link.URL +"/"
Write-Host $url -Fore Yellow
Add-PnPNavigationNode -Title $link.Title -Url $url -Location $location -Parent $parentNode.Id
Write-Host "Link created [$($link.Title)] under parent [$($parentLink.Title)]" -ForegroundColor "Green"
}
else {
Write-Host "Parent not found for [$($link.Title)]" -ForegroundColor "Yellow"
}
}
else {
# SCENARIO 2 - Create without Parent
$url = $tenant + $link.URL +"/"
Write-Host $url -Fore Yellow
Add-PnPNavigationNode -Title $link.Title -Url $url -Location $location
}
}
else {
Write-Host "Link already exists [$($link.Title)]" -ForegroundColor "Green"
}
}
}
# Open Log
$prefix = $MyInvocation.MyCommand.Name
$host.UI.RawUI.WindowTitle = $prefix
$stamp = Get-Date -UFormat "%Y-%m-%d-%H-%M-%S"
Start-Transcript "$PSScriptRoot\log\$prefix-$stamp.log"
$start = Get-Date
# Run Main
Main
# Close Log
$end = Get-Date
$totaltime = $end - $start
Write-Host "`nTime Elapsed: $($totaltime.tostring("hh\:mm\:ss"))"
Stop-TranscriptCODE – CSV
ID,ParentID,Title,URL
1,0,Business Services,/sites/Communication/Business Services
2,1,Business Services Forms,/sites/Communication/Business Services/BusinessServices-Forms
3,0,Communications,/sites/Communication/Communications
4,3,Placeholder,/sites/Communication/Communications/Placeholder
5,0,Resources,/sites/Communication/Resources
6,5,Records,/sites/Communication/Records
7,0,Director Office,/sites/Communication/DirectorOffice
8,7,Placeholder,/sites/Communication/DirectorsOffice/PH
11,0,Finance,/sites/Communication/Finance
12,0,Operations and Planning,/sites/Communication/OperationsPlanning
13,0,Health,/sites/Communication/Health
13,0,Inventory,/sites/Communication/Inventory
14,0,Planning,/sites/Communication/PlanningREFERENCES
