Public/Copy-LocalFolderToSharePoint.ps1

<#
.SYNOPSIS
    Copies a local folder structure and its files to a specified SharePoint Online library folder.
 
.DESCRIPTION
    This function connects to a SharePoint Online site, ensures the target folder exists, and recursively copies
    the local folder structure and its files (excluding .ps1 files) to the specified SharePoint folder. It allows
    for excluding a specific folder and file from being overwritten.
 
.PARAMETER LocalFolderPath
    The path of the local folder to copy.
 
.PARAMETER SharePointSiteUrl
    The URL of the SharePoint Online site.
 
.PARAMETER SharePointFolderRelativeUrl
    The relative URL of the folder within the SharePoint Online site where the files will be copied.
 
.PARAMETER ExcludeFolder
    The relative URL of the folder to exclude from overwriting.
 
.PARAMETER ExcludeFile
    The name of the file to exclude from overwriting.
 
.OUTPUTS
    None
 
.EXAMPLE
    Copy-LocalFolderToSharePoint -LocalFolderPath "C:\LocalFolder" -SharePointSiteUrl "https://yoursharepointsite" -SharePointFolderRelativeUrl "/Shared Documents/YourFolder" -ExcludeFolder "/Shared Documents/YourFolder/CitrixDetails" -ExcludeFile "file.txt"
     
    This command copies the contents of "C:\LocalFolder" to the specified folder within the SharePoint Online site, excluding the "CitrixDetails" folder and "file.txt" from being overwritten.
 
#>


function Copy-LocalFolderToSharePoint {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$LocalFolderPath,
        
        [Parameter(Mandatory = $true)]
        [string]$SharePointSiteUrl,
        
        [Parameter(Mandatory = $true)]
        [string]$SharePointFolderRelativeUrl,

        [Parameter(Mandatory = $false)]
        [string]$Username,

        [Parameter(Mandatory = $false)]
        [string]$keypath,
        
        [Parameter(Mandatory = $false)]
        [string]$EncryptedCredsName,

        [Parameter(Mandatory = $false)]
        [string]$ExcludeFolder = "",
        
        [Parameter(Mandatory = $false)]
        [string]$ExcludeFile = ""
    )

    # Connect to SharePoint Online site
    if ($PSBoundParameters.ContainsKey('Username') -and $PSBoundParameters.ContainsKey('keypath')) {
        $securepass = Get-CredentialSecureString -FilePath $keypath
        $securePassword = ConvertTo-SecureString -String $securepass -AsPlainText -Force
        $credential = New-Object System.Management.Automation.PSCredential($Username, $securePassword)
        Connect-PnPOnline -Url $SiteUrl -Credentials $credential -WarningAction SilentlyContinue
    } else {
        $creds = Get-SecureCredential -EncryptedCredsName $EncryptedCredsName
        Connect-PnPOnline -Url $SiteUrl -Credentials $creds -WarningAction SilentlyContinue
    }


    # Ensure SharePoint folder exists
    function Ensure-SharePointFolderExists {
        param (
            [string]$RelativeUrl
        )

        $folders = $RelativeUrl -split '/'
        $currentPath = ''
        foreach ($folder in $folders) {
            if ($folder -ne '') {
                $currentPath = "$currentPath/$folder"
                $folderExists = Get-PnPFolder -Url $currentPath -ErrorAction SilentlyContinue
                if (-not $folderExists) {
                    Write-Host "Creating folder $currentPath"
                    Add-PnPFolder -Name $folder -Folder $currentPath.Substring(0, $currentPath.LastIndexOf('/'))
                }
            }
        }
    }

    # Recursively copy folder structure and files, excluding .ps1 files
    function Upload-Folder {
        param (
            [string]$Path,
            [string]$RelativeUrl
        )

        # Ensure the folder exists in SharePoint
        Ensure-SharePointFolderExists -RelativeUrl $RelativeUrl

        # Get all files excluding .ps1 files
        $files = Get-ChildItem -Path $Path -File | Where-Object { $_.Extension -ne '.ps1' }
        foreach ($file in $files) {
            $fileRelativeUrl = "$RelativeUrl/$($file.Name)"
            $existingFile = Get-PnPFile -Url $fileRelativeUrl -ErrorAction SilentlyContinue
            if ($existingFile) {
                if ($RelativeUrl -notlike "$ExcludeFolder*" -and $file.Name -ne $ExcludeFile) {
                    Write-Host "File $fileRelativeUrl already exists. Overwriting..."
                    Remove-PnPFile -ServerRelativeUrl $fileRelativeUrl -Force -Recycle -ErrorAction SilentlyContinue
                } else {
                    Write-Host "Skipping file $fileRelativeUrl"
                    continue
                }
            }
            Write-Host "Uploading $($file.FullName) to $fileRelativeUrl"
            Add-PnPFile -Path $file.FullName -Folder $RelativeUrl
        }

        # Process subfolders recursively
        $subFolders = Get-ChildItem -Path $Path -Directory
        foreach ($subFolder in $subFolders) {
            $folderRelativeUrl = "$RelativeUrl/$($subFolder.Name)"
            Upload-Folder -Path $subFolder.FullName -RelativeUrl $folderRelativeUrl
        }
    }

    # Start the upload process
    Upload-Folder -Path $LocalFolderPath -RelativeUrl $SharePointFolderRelativeUrl

    # Disconnect from SharePoint Online
    Disconnect-PnPOnline
}