Forum Discussion

Koen_RealConnections's avatar
Koen_RealConnections
Copper Contributor
May 16, 2024

Copy/move folders from SharePoint site to OneDrive user using Powershell

Hi community members! Hope you are doing allright!

 

I'm hoping one of you is willing to help me out a bit. I will try to describe my case as clear as possible:

 

A customer of mine is using an archive-user in their Microsoft 365 tenant. It's a user with a OneDrive (plan 2) license added to it. The storage of this user has been extended up to 5 TB. So far, so good, as the customer know how to use this archive user and how to manage it.

 

This customer has a lot (and I do mean a lot!) of Teams groups and SharePoint sites which contains lots of (old) data. This customer wants to reduce their Extra File Storage costs in SharePoint (which makes sense), and asked me to help out with migrating/moving the data in some of the groups to this archive user.

 

Now, I know how to copy/move folders or files in the webbrowser and/or file explorer from SharePoint to OneDrive, but these ways are just to slow and/or not sufficient enough.

 

So, I was looking in to Powershell as a solution to move/copy folders from a SharePoint site to the OneDrive user.

I've found some scripts, which might be useful, but I'm getting stuck as how to add the URL's of both the SharePoint site and the OneDrive user.

 

These are the scripts I've found so far:

 

If somebody is able to help me out how I can (easily ;P) copy/move data from a SharePoint site to OneDrive, or provide me with a script that I could use, I would really be obliged!

 

Thanks in advance!

  • NicolasKheirallah's avatar
    NicolasKheirallah
    May 21, 2024

    Koen_RealConnections 

    Yeah! just change from Copy-PnPFile to Move-PnPFile.

     

    You can also make the code more efficient using Move-PnPFolder. But it doesn't really like large folders in my experience

     

    # Connect to the source site collection
    $sourceSiteUrl = "https://yourtenant.sharepoint.com/sites/SourceSiteCollection"
    Connect-PnPOnline -Url $sourceSiteUrl -Interactive
    
    # Connect to the target site collection
    $targetSiteUrl = "https://yourtenant.sharepoint.com/sites/TargetSiteCollection"
    Connect-PnPOnline -Url $targetSiteUrl -Interactive
    # Define source and target library and folder paths
    $sourceLibrary = "Documents"
    $targetLibrary = "Documents"
    
    # Get all items from the source library
    $sourceItems = Get-PnPListItem -List $sourceLibrary -PageSize 2000
    
    foreach ($item in $sourceItems) {
        $sourceFileUrl = $item.FieldValues["FileRef"]
        $targetFileUrl = $sourceFileUrl -replace "$sourceLibrary", $targetLibrary
        $targetFileUrl = $targetFileUrl -replace $sourceSiteUrl, $targetSiteUrl
    
        try {
            Move-PnPFile -SourceUrl $sourceFileUrl -TargetUrl $targetFileUrl -Force -OverwriteIfAlreadyExists
            Write-Host "Moved: $sourceFileUrl to $targetFileUrl"
        } catch {
            Write-Host "Failed to move: $sourceFileUrl"
        }
    }
    

     

  • Koen_RealConnections 

     

    So I would approach this in a different way and just use SharePoint Archiving and Backup instead. Just Archive the Files/Sites that is not in use and retrieve them when needed instead of being in someone's OneDrive. Also using archiving the storage is 75% Cheaper

     

    # Install the PnP PowerShell module if not already installed
    Install-Module -Name "PnP.PowerShell" -Force -SkipPublisherCheck
    
    # Connect to the source SharePoint site
    $sourceSiteUrl = "https://yourtenant.sharepoint.com/sites/SourceSite"
    Connect-PnPOnline -Url $sourceSiteUrl -Interactive
    
    # Store the connection for later use
    $sourceConnection = Get-PnPConnection
    
    # Connect to the destination SharePoint site
    $destinationSiteUrl = "https://yourtenant.sharepoint.com/sites/DestinationSite"
    Connect-PnPOnline -Url $destinationSiteUrl -Interactive
    
    # Store the connection for later use
    $destinationConnection = Get-PnPConnection
    
    
    
    # Define source and destination library and folder
    $sourceLibrary = "Documents"
    $sourceFolder = "FolderToMove"
    
    $destinationLibrary = "Documents"
    $destinationFolder = "FolderToMove"
    
    # Use the source connection
    Set-PnPConnection -Connection $sourceConnection
    
    # Get all files and subfolders in the source folder
    $files = Get-PnPFolderItem -FolderSiteRelativeUrl "$sourceLibrary/$sourceFolder" -ItemType File
    $subFolders = Get-PnPFolderItem -FolderSiteRelativeUrl "$sourceLibrary/$sourceFolder" -ItemType Folder
    
    # Copy each file to the destination folder
    foreach ($file in $files) {
        $sourceFileUrl = $file.ServerRelativeUrl
        $destinationFileUrl = $sourceFileUrl -replace $sourceSiteUrl, $destinationSiteUrl
    
        Set-PnPConnection -Connection $destinationConnection
        Copy-PnPFile -SourceUrl $sourceFileUrl -TargetUrl $destinationFileUrl -OverwriteIfAlreadyExists
    }
    
    # Copy each subfolder and its contents recursively
    function Copy-FolderContents {
        param (
            [string]$currentFolderUrl
        )
    
        # Get files and subfolders in the current folder
        $currentFiles = Get-PnPFolderItem -FolderSiteRelativeUrl $currentFolderUrl -ItemType File
        $currentSubFolders = Get-PnPFolderItem -FolderSiteRelativeUrl $currentFolderUrl -ItemType Folder
    
        # Copy each file
        foreach ($file in $currentFiles) {
            $sourceFileUrl = $file.ServerRelativeUrl
            $destinationFileUrl = $sourceFileUrl -replace $sourceSiteUrl, $destinationSiteUrl
    
            Set-PnPConnection -Connection $destinationConnection
            Copy-PnPFile -SourceUrl $sourceFileUrl -TargetUrl $destinationFileUrl -OverwriteIfAlreadyExists
        }
    
        # Recursively copy each subfolder
        foreach ($folder in $currentSubFolders) {
            $sourceFolderUrl = $folder.ServerRelativeUrl
            $destinationFolderUrl = $sourceFolderUrl -replace $sourceSiteUrl, $destinationSiteUrl
    
            Set-PnPConnection -Connection $destinationConnection
            New-PnPFolder -Name $folder.Name -Folder (Split-Path -Path $destinationFolderUrl -Parent)
    
            Copy-FolderContents -currentFolderUrl $sourceFolderUrl
        }
    }
    
    Copy-FolderContents -currentFolderUrl "$sourceLibrary/$sourceFolder"
    

     

    • Koen_RealConnections's avatar
      Koen_RealConnections
      Copper Contributor

      NicolasKheirallah Thank you for your suggestion! In the script you've sent, I see that script is using a copy option. Is this also possible for moving? Or not?

       

      I'm interested in this archiving solution, but how does this work exactly? Aside from that script? I mean, if any data is ever needed, how can this data be approached in userfriendly way? As in, would something like be also do-able for an end-user, or is this more a admin job?

       

      If you have any more tips/tricks how to start setting up this SharePoint archiving solution, I'm really looking forward to your reaction!


      Thanks in advance!

      • NicolasKheirallah's avatar
        NicolasKheirallah
        MVP

        Koen_RealConnections 

        Yeah! just change from Copy-PnPFile to Move-PnPFile.

         

        You can also make the code more efficient using Move-PnPFolder. But it doesn't really like large folders in my experience

         

        # Connect to the source site collection
        $sourceSiteUrl = "https://yourtenant.sharepoint.com/sites/SourceSiteCollection"
        Connect-PnPOnline -Url $sourceSiteUrl -Interactive
        
        # Connect to the target site collection
        $targetSiteUrl = "https://yourtenant.sharepoint.com/sites/TargetSiteCollection"
        Connect-PnPOnline -Url $targetSiteUrl -Interactive
        # Define source and target library and folder paths
        $sourceLibrary = "Documents"
        $targetLibrary = "Documents"
        
        # Get all items from the source library
        $sourceItems = Get-PnPListItem -List $sourceLibrary -PageSize 2000
        
        foreach ($item in $sourceItems) {
            $sourceFileUrl = $item.FieldValues["FileRef"]
            $targetFileUrl = $sourceFileUrl -replace "$sourceLibrary", $targetLibrary
            $targetFileUrl = $targetFileUrl -replace $sourceSiteUrl, $targetSiteUrl
        
            try {
                Move-PnPFile -SourceUrl $sourceFileUrl -TargetUrl $targetFileUrl -Force -OverwriteIfAlreadyExists
                Write-Host "Moved: $sourceFileUrl to $targetFileUrl"
            } catch {
                Write-Host "Failed to move: $sourceFileUrl"
            }
        }
        

         

Resources