Ever feel like your files are playing hide and seek? One moment they’re here, the next they’re over there, and sometimes, who knows where! If your documents are scattered like confetti after a party, it’s time to gather them all in one place.

Organizing your files in OneDrive can make a world of difference. Not only will it save you time, but it will also ensure that you can find what you need when you need it. Plus, if you have files that are important to others, consider moving them to a Team or a SharePoint site. This way, everyone who needs access can find them easily, and you can collaborate more effectively.

So, let’s put an end to the game of hide and seek and bring all your documents together in one organized, accessible location!

1. Directly from the Document Library:

    • Pros:
      • Super easy and intuitive.
      • No need for additional tools or downloads.
    • Cons:
      • Can be time-consuming for large volumes of files.
      • Limited control over metadata.

2. Download and Upload:

    • Pros:
      • Simple and straightforward.
      • Great for small batches of files.
    • Cons:
      • Tedious for large amounts of data.
      • Risk of losing metadata and file properties.

3. PowerShell:

    • Pros:
      • Highly efficient and powerful.
      • Preserves metadata and file integrity.
    • Cons:
      • Requires some technical know-how.
      • Initial setup can be a bit daunting.

1. Directly from the Document Library:

Moving Files and Folders in Teams or SharePoint is the quickest way for a user to move a couple of files or folders. This is how: 

  • Open the Teams app and navigate to the channel where your files are located, or the SharePoint site and Documents. 
  • Click on the Files tab at the top.
  • Hover over the file or folder you want to move.
  • Click on the three dots () for more options.

Move or Copy:

  • Choose Move to or Copy to from the dropdown menu.
  • Select the destination folder within the same channel or another channel.
  1. Choose the team or the SharePoint site you wish to move to.
  2. Channels in Teams are listed as folders in this view. NB NB: Never move a file or folder outside a channel. It will not show up in Teams if its not stored within a channel.
  3. Click on Move here when you have found the new location.

That’s it! Files moved!

A little side note: I usually copy to and then delete the source when it’s finished. That’s because I have serious trust issues…

2. Download and upload

Here is how:

  • Find the files or folders you want to download.
  • Hover over the file or folder you want to move.
  • Click on the three dots () for more options.

Go to the team and channel or the SharePoint site where you want to upload the files:

  1. Click on Upload
  2. Choose Files or Folder according what you want to upload.
  3. Browse to where the downloaded files or folders are and click upload.

You can also do a drag and drop from File explorer to Teams files or the SharePoint library!

3. So, the fun part! POWERSHELL!

To run this script successfully, you need the following prerequisites and setup:

1. PowerShell Environment

  • Ensure you are running the script in PowerShell 5.1 or later, or PowerShell Core.
  • Run PowerShell as an administrator to avoid permission issues.

2. Install PnP PowerShell Module

  • The script uses the PnP PowerShell module to interact with SharePoint Online.
  • Install the module by running the following command in PowerShell:

Install-Module -Name PnP.PowerShell -Force -AllowClobber

3. Azure AD App Registration

  • The script requires an Azure AD App Registration for authentication.
  • Ensure you have registered an app in Azure AD with the following:
    • Client ID: This is the $clientId parameter in the script.
    • Tenant ID: This is derived from the $tenantname parameter (e.g., contoso.onmicrosoft.com).
    • Permissions: The app must have the following API permissions for SharePoint:
      • Sites.ReadWrite.All
      • Sites.FullControl.All
    • Grant admin consent for these permissions.

Please note that the app has delegated permissions, so the app itself has no permissions.

4. SharePoint Online Setup

  • Ensure you have access to the SharePoint Online tenant specified in the $tenantname parameter.
  • Verify that the source sitetarget site, and their respective document libraries exist:
    • $sourceSiteUrlName: The source site URL name (e.g., TeamGremlins).
    • $targetSiteUrlName: The target site URL name (e.g., TeamProjectHunt).
    • $sourceLibraryTitle: The name of the source document library.
    • $targetLibraryTitle: The name of the target document library.

5. Permissions

  • Ensure the Azure AD app or the user running the script has the following permissions:
    • Full Control on the source and target SharePoint sites.
    • Access to the document libraries being copied.

6. Configure Parameters

  • Update the script parameters to match your environment:
    • $clientId: The Client ID of your Azure AD app registration.
    • $tenantname: Your SharePoint Online tenant name (e.g., Contoso).
    • $sourceLibraryTitle: The name of the source document library.
    • $targetLibraryTitle: The name of the target document library.
    • $sourceSiteUrlName: The source site URL name.
    • $targetSiteUrlName: The target site URL name.
    • $targetFolderName: The target folder path within the target library.

7. Authentication

  • The script uses PnP PowerShell’s device login for authentication.
  • When prompted, follow the instructions to authenticate using the device login code.

The script!

This is the script! And this was made with a huge help from my besties Pål Andre and Per Ove, whenever I ask them about ANYTHING they always find the time to help! Thank you guys!

And off course, Copilot for Github – always there to help out to figure out errors!!

# This script copies folders and files from a source library to a target library in SharePoint Online.

# Parameters
param (
    [string]$clientId = "xxx0a6f-199d-xx33-9e9b-xxx1fc4xxxx",   # Azure AD App Client ID
    [string]$tenantname = "Contoso",                            # SharePoint Online tenant name
    [string]$sourceLibraryTitle = "Documents",                  # Source document library name
    [string]$sourceSiteUrlName = "TeamGremlins",                # Source site URL name
    [string]$targetLibraryTitle = "Documents",                  # Target document library name
    [string]$targetSiteUrlName = "TeamProjectHunt",             # Target site URL name
    [string]$targetFolderName = "Hunting/Project"               # Target folder name
)

# Helper Functions
function Connect-ToSite {
    param (
        [string]$url,
        [string]$accessToken
    )
    try {
        return Connect-PnPOnline -Url $url -AccessToken $accessToken -ReturnConnection
    } catch {
        Write-Warning "Failed to connect to site: $url"
        return $null
    }
}

function Get-OrCreateLibrary {
    param (
        [string]$libraryTitle,
        [object]$connection,
        [string]$targetSiteUrl
    )
    try {
        return Get-PnPList -Identity $libraryTitle -Connection $connection
    } catch {
        Write-Warning "Library $libraryTitle not found. Creating it..."
        return Copy-PnPList -Identity $sourceList -Title $libraryTitle -Connection $connection -DestinationWebUrl $targetSiteUrl
    }
}

# Calculate URLs
$rootsiteurl = "https://$tenantname.sharepoint.com"
$sourceSiteUrl = "https://$tenantname.sharepoint.com/teams/$sourceSiteUrlName"
$targetSiteUrl = "https://$tenantname.sharepoint.com/sites/$targetSiteUrlName"
$tenanthostname = "$tenantname.onmicrosoft.com"

# Connect to Root Site
Write-Information "Connecting to SharePoint Root Site..."
$con_sproot = Connect-PnPOnline -Url $rootsiteurl -ClientId $clientId -Tenant $tenanthostname -DeviceLogin -ReturnConnection -ValidateConnection

# Get Access Token
$AccessToken = Get-PnPAccessToken -Connection $con_sproot -ResourceTypeName SharePoint

# Connect to Source Site
Write-Information "Connecting to Source Site..."
$con_sourceSite = Connect-ToSite -url $sourceSiteUrl -accessToken $AccessToken
if (-not $con_sourceSite) { break }

# Get Source Library
$sourceList = Get-PnPList -Identity $sourceLibraryTitle -Connection $con_sourceSite
if (-not $sourceList) {
    Write-Warning "Source Library $sourceLibraryTitle not found."
    break
}

# Connect to Target Site
Write-Information "Connecting to Target Site..."
$con_targetSite = Connect-ToSite -url $targetSiteUrl -accessToken $AccessToken
if (-not $con_targetSite) { break }

# Get or Create Target Library
$targetList = Get-OrCreateLibrary -libraryTitle $targetLibraryTitle -connection $con_targetSite -targetSiteUrl $targetSiteUrl

# Calculate Relative URLs
$sourceLibraryFolderRelativeUrl = $sourceList.RootFolder.ServerRelativeUrl
$targetLibraryFolderRelativeUrl = $targetList.RootFolder.ServerRelativeUrl + "/" + $targetFolderName

Write-Information "Source Library Relative Path: $sourceLibraryFolderRelativeUrl"
Write-Information "Target Library Relative Path: $targetLibraryFolderRelativeUrl"

# Copy folders from the source library to the target folder
$rootfolders = Get-PnPFolder -ListRootFolder $sourceList -Connection $con_sourceSite | Get-PnPFolderInFolder -Connection $con_sourceSite -ExcludeSystemFolders
foreach ($folder in $rootfolders) {
    Write-Information -MessageData ('Copying {0} ' -f $folder.Name)
    $param_CopyFolder = @{
        SourceUrl            = $sourceLibraryFolderRelativeUrl + "/" + $folder.Name.Trim()
        TargetUrl            = $targetLibraryFolderRelativeUrl
        IgnoreVersionHistory = $true
        Connection           = $con_sourceSite
        Force                = $true
        Overwrite            = $true
    }
    Write-Host ("Copying {0} to {1}" -f $folder.Name, $targetLibraryFolderRelativeUrl)
    Copy-PnPFolder @param_CopyFolder
}

# Copy files in the root folder to the target folder
$rootfiles = $Alllistitems | Where-Object { $_.FileDirRef -eq $sourceLibraryFolderRelativeUrl }
foreach ($file in $rootfiles) {
    Write-Information -MessageData ('Copying {0} ' -f $file.Name)
    $param_CopyFile = @{
        SourceUrl            = $file.FileRef
        TargetUrl            = $targetLibraryFolderRelativeUrl + "/" + $file.FileLeafRef
        IgnoreVersionHistory = $true
        Connection           = $con_sourceSite
        Force                = $true
        Overwrite            = $true
    }
    Write-Host ("Copying {0} to {1}" -f $file.FileLeafRef, $targetLibraryFolderRelativeUrl)
    Copy-PnPFile @param_CopyFile
}

This approach does not align with Per-Torben’s security philosophy, we have strayed from the true path, may the Demo Gods forgive us…

We should implement certificate-based authentication. For guidance, refer to the blog post How to Connect PowerShell to Various M365 Services Using Certificate-Based Authentication. You can then modify the script in this post to accommodate the certificate-based method.

Author

  • Åsne Holtklimpen

    Åsne is a Microsoft MVP within Microsoft Copilot, an MCT and works as a Cloud Solutions Architect at Crayon. She was recently named one of Norway’s 50 foremost women in technology (2022) by Abelia and the Oda network. She has over 20 years of experience as an IT consultant and she works with Microsoft 365 – with a special focus on Teams and SharePoint, and the data flow security in Microsoft Purview.

    View all posts

Discover more from Agder in the cloud

Subscribe to get the latest posts sent to your email.

By Åsne Holtklimpen

Åsne is a Microsoft MVP within Microsoft Copilot, an MCT and works as a Cloud Solutions Architect at Crayon. She was recently named one of Norway’s 50 foremost women in technology (2022) by Abelia and the Oda network. She has over 20 years of experience as an IT consultant and she works with Microsoft 365 – with a special focus on Teams and SharePoint, and the data flow security in Microsoft Purview.

Related Post

Leave a Reply