src/Public/SharePoint/Copy-SP365Nav.ps1

function Copy-SP365Nav {
    <#
    .SYNOPSIS
        Copies SharePoint navigation nodes between navigation locations or sites.
 
    .DESCRIPTION
        Reads the navigation hierarchy from a source location and recreates it in
        a target location. Existing target navigation is cleared by default.
 
        Provide SourceSite and TargetSite for a cross-site copy. Set SameSite to
        true to copy between navigation locations in the source site.
 
    .PARAMETER SourceSite
        The absolute URL of the SharePoint site from which navigation is read.
 
    .PARAMETER TargetSite
        The absolute URL of the destination SharePoint site. Provide this
        parameter unless SameSite is true.
 
    .PARAMETER SameSite
        Indicates that the source and target navigation locations belong to the
        source site. The command does not establish a separate target connection
        when this value is true.
 
    .PARAMETER ClearTargetNav
        Controls whether existing target navigation nodes are removed before the
        source nodes are added. The default value is true.
 
    .PARAMETER SourceNavigationLocation
        The PnP navigation location from which nodes are read, such as QuickLaunch
        or TopNavigationBar.
 
    .PARAMETER TargetNavigationLocation
        The PnP navigation location to which nodes are added, such as QuickLaunch
        or TopNavigationBar.
 
    .EXAMPLE
        Copy-SP365Nav -SourceSite 'https://contoso.sharepoint.com/sites/source' `
            -TargetSite 'https://contoso.sharepoint.com/sites/target' `
            -SourceNavigationLocation QuickLaunch `
            -TargetNavigationLocation QuickLaunch
 
        Copies Quick Launch navigation between two SharePoint sites.
 
    .EXAMPLE
        Copy-SP365Nav -SourceSite 'https://contoso.sharepoint.com/sites/operations' `
            -SameSite $true `
            -SourceNavigationLocation QuickLaunch `
            -TargetNavigationLocation TopNavigationBar `
            -ClearTargetNav $false
 
        Appends Quick Launch nodes to the top navigation in the same site.
 
    .INPUTS
        None.
 
    .OUTPUTS
        None.
 
    .NOTES
        The source navigation is not modified. Use Move-SP365Nav to remove the
        source navigation after populating the target.
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)]
        [string]$SourceSite,
        [parameter(Mandatory = $false)]
        [string]$TargetSite,
        [bool]$SameSite = $false,
        [bool]$ClearTargetNav = $true,
        [PnP.Framework.Enums.NavigationType]$SourceNavigationLocation,
        [PnP.Framework.Enums.NavigationType]$TargetNavigationLocation
    ) 
    Begin {
        $isVerbose = $VerbosePreference -ne 'SilentlyContinue'
        Write-Verbose "Starting Copy-SP365Nav function"
        Write-Debug "SourceSite: $SourceSite, TargetSite: $TargetSite, SameSite: $SameSite, ClearTargetNav: $ClearTargetNav, SourceNavigationLocation: $SourceNavigationLocation, TargetNavigationLocation: $TargetNavigationLocation"
        $privatePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath "private"
        Write-Verbose "Private path set to $privatePath"
        @(Get-ChildItem -Path $privatePath -Recurse -Filter "*.ps1") | ForEach-Object {
            try {
                . $_.FullName
            }
            catch {
                Write-Verbose "Failed to source $($_.FullName): $($_.Exception.Message)"
            
            }
        }
        Test-ForModuleUpdate
        Write-Verbose "Initialization complete"
        if (-not $SourceSite -and -not $TargetSite) {
            throw "At least one of the parameters (`$SourceSite or `$TargetSite) must be provided."
        }
    }
    Process {
        try {
            Connect-SP365 -Url $SourceSite
            Write-Host "Starting to retrieve navigation from source site"
            $siteNav = Get-SiteNav -NavigationNodeLocation $SourceNavigationLocation
            Write-Host "Finished retrieving navigation from source site"
            Write-Host "Total navigation nodes retrieved: $($siteNav.Count)"
            Write-Host ""
            
            if (-not $SameSite) {
                Connect-SP365 -Url $TargetSite
            }
            if ($ClearTargetNav) {
                Clear-SiteNav -Location $TargetNavigationLocation
            }
            Add-SiteNav -newNavNodes $siteNav -Location $TargetNavigationLocation
        }
        catch {
            Write-Verbose "An error occurred: $($_.Exception.Message)"
        }
    }
}
# SIG # Begin signature block
# MIIHMAYJKoZIhvcNAQcCoIIHITCCBx0CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCB1y773qMeMXSU9
# NVe9o30oKj7afm+Gjdb4vc/4sJWEPKCCBBwwggQYMIICgKADAgECAhBH6tQY1Crt
# r02dueiKibmNMA0GCSqGSIb3DQEBCwUAMCQxIjAgBgNVBAMMGU0zNjUuVG9vbGtp
# dCBDb2RlIFNpZ25pbmcwHhcNMjYwNzE2MTY1NjE0WhcNMjcwNzE2MTcwNjEzWjAk
# MSIwIAYDVQQDDBlNMzY1LlRvb2xraXQgQ29kZSBTaWduaW5nMIIBojANBgkqhkiG
# 9w0BAQEFAAOCAY8AMIIBigKCAYEAxQCBRWJ923yIy73AwGWtFvfBL/dZHvwCBO05
# dJopbkHGcCqifl9R21PXr5OIjcEFv16ObYL23cPwZaNKCiejuiU32MHwidYHU1k5
# AzFpjwW2uwjeA7Hip3cURwoeOH/LzNa/xNTBRiO70M7gunZCm+FixDcPKf66Tvb1
# iM6pbOFmJvzbGjFqKM6mQRofaCPU89SHW5y43HyZNdiWsJTN4TsWj96Rps4fKhh2
# gToexqi0GhLHhLtVQ8ZxMic6w0C9sCWhYBl3kw1OJdaa/MVBdVAXDOIL0HIG8jOa
# l1x67U1LihXEw1hdc3YabhvSYZ5ZXPV1AZ2z6+Qa0EL9XlJWqBkn0rO6sZB1IkOo
# gfeTxVuI6SjhqpW6QrAFFJzb81qd0dDWmmuVka1yoWEic7naZpDfuh3JZPOUilE1
# Wv3qhBDYN5qg1qXeh0RMosR8rMfKjE8CFvsB7LTibom+22in1x16vmIiv9ep5HNc
# eS0hRa/84+vmXQ8R6hN8x+oWbG8NAgMBAAGjRjBEMA4GA1UdDwEB/wQEAwIHgDAT
# BgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUu9RWiOCtUYtlrLgwzVDsor7P
# p88wDQYJKoZIhvcNAQELBQADggGBAKJksJX6tgjdng//9wRM2ztQn8breDnY/Zki
# 8iXrC94sqCYoYg1lT0pVHZq1ZHpdUNh/xQnjP9asbIqgUt0YiCIASCONV2nM2wyK
# VjrvxaC08xefuHiO/R6Uc9sDW38mID+gDoAmswmB9JV5qm+j8b4hw3vcwuUe3nla
# 5bn3YoKXLY4/YJjHNA62TmhYNfs7iwdk2vyH/dCvD35LdBhP/y2ChmOxzI1aSp4L
# c65zCFVDPzw1IED/5q9NM48QUVDgVAX6VNuocjXZpWd7fHf7KFLRJSExXtaYQCdk
# hBSITzd5WBT2opKF02TrTyc5h1zCTr6HU+1Eqr92fvEGfqk4yYSt4plyqbm6Unnq
# gEgwDqlgm9ZNA8hzorcoxSetVVZ8z+50KB55JRTENZ/5kOJWzPtoIA+PdmFvg3wU
# jQHhO6C32rdVUk+j1hI1zL6sR8gYn5VmLPVjfs49yKf+2t3iDsaYVRNawOqHrMZj
# g59c2M2hT5D6krLsI8r3F/t3YfHbKjGCAmowggJmAgEBMDgwJDEiMCAGA1UEAwwZ
# TTM2NS5Ub29sa2l0IENvZGUgU2lnbmluZwIQR+rUGNQq7a9Nnbnoiom5jTANBglg
# hkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3
# DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEV
# MC8GCSqGSIb3DQEJBDEiBCBorq8rlkjrbhhZLQwYbgU/SRC8pyGsIz+k6EMKEo2Z
# BjANBgkqhkiG9w0BAQEFAASCAYBXbYohBwtfqzAnN0PJy62uG7BsJDzt6YfkoVMd
# aOdDFkYqv4uV8KZI9oocQzFVr/fzNnZsB+0BX7BwAXUNOv4FkpXOBNG0Gzf9keLk
# +V7oo0sGUrxHKDQLS4wxQms/8LtgAx4W0IZ/gOS/W8EPA7s9IOq90aiUgMjxoMW6
# ChYTwKt6Iohi9xsEZ31OJKSFNAP0UIPn5tQNX5YtVTf1j8dz1JkXNqrbard7REEV
# kR78KpcnISAS78FMxmmF+CFJPjIbB654hQQroqYk49TmCxRzhJQiN9DTTVZfzmMR
# kApnvVP2Fy3kf+KnkuCwV/9OBqmUXeTb+qUJ+Y4XH2vYhIq9cM7QmSHZWnkkeUXi
# rjw2VDMgdhR2uWQg916TgLGaSTv7BqtqtNMBbn6yDdEUMPqcJEUkS+TOTYSfT02Z
# swsc6e5QUD+aJXBgpql7A0pxiVqd6W+FN7ZsO+JANe4Dp6ZC+tHBv8PsD7e0Jo7I
# msDPZQpdKW2ZT5WbkioPEA15Q1Y=
# SIG # End signature block