Export Megamenu Navigation

This PnP PowerShell script exports the megamenu navigation structure from a SharePoint Online site to a CSV file. It captures all navigation levels — parent, child, and grandchild nodes — along with their URLs and visibility settings.

Purpose

This script helps you:

  • Export the full megamenu navigation hierarchy to CSV
  • Audit and document existing site navigation
  • Back up navigation structure before making changes
  • Compare navigation across environments

Prerequisites

  • PnP PowerShell module installed
  • Site collection administrator permissions
  • Connection to your SharePoint Online site
  • A registered Azure AD app with appropriate permissions

PowerShell Script

# Configuration Variables
$SiteURL = "https://tenantName.sharepoint.com/sites/siteName"  # Set your SharePoint site URL here
$ClientId = ""  # Client ID here
$LogFile = "MegaMenu_Export_Log_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
$ExportFile = "MegaMenu_Export_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"

# Logging Function
function Write-Log {
    param(
        [string]$Message,
        [ValidateSet('Info','Warning','Error','Success')]
        [string]$Level = 'Info'
    )

    $Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $LogEntry = [PSCustomObject]@{
        Timestamp = $Timestamp
        Level = $Level
        Message = $Message
    }

    # Console output with color
    $Color = switch($Level) {
        'Info' { 'White' }
        'Warning' { 'Yellow' }
        'Error' { 'Red' }
        'Success' { 'Green' }
    }
    Write-Host "[$Timestamp] [$Level] $Message" -ForegroundColor $Color

    # Export to log file
    $LogEntry | Export-Csv -Path $LogFile -Append -NoTypeInformation
}

# Validate parameters
if ([string]::IsNullOrWhiteSpace($SiteURL)) {
    Write-Log "SiteURL is required. Please set the variable." -Level Error
    exit
}
if ([string]::IsNullOrWhiteSpace($ClientId)) {
    Write-Log "ClientId is required. Please set the variable." -Level Error
    exit
}

Write-Log "=== Starting MegaMenu Navigation Export ===" -Level Info
Write-Log "Site URL: $SiteURL" -Level Info

try {
    # Connect to SharePoint Online
    Write-Log "Attempting to connect to SharePoint Online..." -Level Info

    Connect-PnPOnline -Url $SiteURL -Interactive -ClientId $ClientId -ErrorAction Stop

    Write-Log "Successfully connected to SharePoint Online" -Level Success

    # Initialize results array
    $NavigationResults = @()

    # Get MegaMenu navigation using REST API
    Write-Log "Retrieving MegaMenu navigation..." -Level Info

    try {
        # Use the navigation MenuState endpoint which contains MegaMenu data
        $restUrl = "/_api/navigation/MenuState"
        $menuState = Invoke-PnPSPRestMethod -Url $restUrl -Method Get

        Write-Log "Successfully retrieved MenuState data" -Level Success

        if ($menuState.Nodes -and $menuState.Nodes.Count -gt 0) {
            Write-Log "Found $($menuState.Nodes.Count) top-level navigation nodes" -Level Success

            foreach ($node in $menuState.Nodes) {
                # Add parent node
                $NavItem = [PSCustomObject]@{
                    Level = "Parent"
                    ParentTitle = ""
                    Title = $node.Title
                    Url = $node.SimpleUrl
                    Key = $node.Key
                    FriendlyUrlSegment = $node.FriendlyUrlSegment
                    IsExternal = $node.OpenInNewWindow
                    IsHidden = $node.IsHidden
                    IsVisible = -not $node.IsHidden
                }
                $NavigationResults += $NavItem
                Write-Log "Exported Parent: $($node.Title) - $($node.SimpleUrl)" -Level Info

                # Check for child nodes
                if ($node.Nodes -and $node.Nodes.Count -gt 0) {
                    Write-Log "Found $($node.Nodes.Count) children for: $($node.Title)" -Level Info

                    foreach ($child in $node.Nodes) {
                        $ChildItem = [PSCustomObject]@{
                            Level = "Child"
                            ParentTitle = $node.Title
                            Title = $child.Title
                            Url = $child.SimpleUrl
                            Key = $child.Key
                            FriendlyUrlSegment = $child.FriendlyUrlSegment
                            IsExternal = $child.OpenInNewWindow
                            IsHidden = $child.IsHidden
                            IsVisible = -not $child.IsHidden
                        }
                        $NavigationResults += $ChildItem
                        Write-Log "Exported Child: $($child.Title) (Parent: $($node.Title)) - $($child.SimpleUrl)" -Level Info

                        # Check for grandchildren (3rd level)
                        if ($child.Nodes -and $child.Nodes.Count -gt 0) {
                            Write-Log "Found $($child.Nodes.Count) grandchildren for: $($child.Title)" -Level Info

                            foreach ($grandchild in $child.Nodes) {
                                $GrandchildItem = [PSCustomObject]@{
                                    Level = "Grandchild"
                                    ParentTitle = $child.Title
                                    Title = $grandchild.Title
                                    Url = $grandchild.SimpleUrl
                                    Key = $grandchild.Key
                                    FriendlyUrlSegment = $grandchild.FriendlyUrlSegment
                                    IsExternal = $grandchild.OpenInNewWindow
                                    IsHidden = $grandchild.IsHidden
                                    IsVisible = -not $grandchild.IsHidden
                                }
                                $NavigationResults += $GrandchildItem
                                Write-Log "Exported Grandchild: $($grandchild.Title) (Parent: $($child.Title)) - $($grandchild.SimpleUrl)" -Level Info
                            }
                        }
                    }
                }
            }

            Write-Log "MegaMenu navigation export completed" -Level Success
        }
        else {
            Write-Log "No navigation nodes found in MenuState" -Level Warning
        }
    }
    catch {
        Write-Log "Error retrieving MegaMenu navigation: $($_.Exception.Message)" -Level Error
        Write-Log "Stack Trace: $($_.Exception.StackTrace)" -Level Error
    }

    # Export to CSV
    if ($NavigationResults.Count -gt 0) {
        Write-Log "Exporting $($NavigationResults.Count) navigation items to CSV..." -Level Info
        $NavigationResults | Export-Csv -Path $ExportFile -NoTypeInformation -Encoding UTF8
        Write-Log "Export completed successfully: $ExportFile" -Level Success

        # Display summary
        Write-Log "`n=== Navigation Summary ===" -Level Info
        $parentCount = ($NavigationResults | Where-Object { $_.Level -eq "Parent" }).Count
        $childCount = ($NavigationResults | Where-Object { $_.Level -eq "Child" }).Count
        $grandchildCount = ($NavigationResults | Where-Object { $_.Level -eq "Grandchild" }).Count

        Write-Log "Parent Nodes: $parentCount" -Level Success
        Write-Log "Child Nodes: $childCount" -Level Success
        Write-Log "Grandchild Nodes: $grandchildCount" -Level Success
        Write-Log "Total Items: $($NavigationResults.Count)" -Level Success

        # Show first few items as preview
        Write-Log "`nPreview of exported items:" -Level Info
        $NavigationResults | Select-Object -First 10 | Format-Table Level, ParentTitle, Title, Url -AutoSize
    }
    else {
        Write-Log "No navigation items found to export" -Level Warning
    }

    # Summary
    Write-Log "n=== Export Summary ===" -Level Info
    Write-Log "Export File: $ExportFile" -Level Info
    Write-Log "Log File: $LogFile" -Level Info
}
catch {
    Write-Log "Critical error during navigation export: $($_.Exception.Message)" -Level Error
    Write-Log "Stack Trace: $($_.Exception.StackTrace)" -Level Error
}
finally {
    # Disconnect from SharePoint
    try {
        Write-Log "Disconnecting from SharePoint Online..." -Level Info
        Disconnect-PnPOnline -ErrorAction SilentlyContinue
        Write-Log "Disconnected successfully" -Level Success
    }
    catch {
        Write-Log "Error during disconnect: $($_.Exception.Message)" -Level Warning
    }
}

Write-Log "n=== MegaMenu Navigation Export Process Completed ===" -Level Info

Usage Notes

  • Update $SiteURL and $ClientId before running
  • The script exports up to three levels: Parent, Child, and Grandchild
  • Output CSV includes title, URL, visibility, and hierarchy information
  • A separate log CSV is generated for each run with timestamped filename
  • Run in a test environment first to verify connectivity and output

Output Files

  • Export CSV - Full navigation hierarchy with all node properties
  • Log CSV - Timestamped log of all actions and errors during the run

Fields Exported

  • Level (Parent / Child / Grandchild)
  • Parent title
  • Node title and URL
  • Key and friendly URL segment
  • External link and hidden/visible flags