SharePoint 2013 – PowerShell Script to Move all Documents Between Libraries

I recently had a requirement to move all the contents of a document library to another library and anybody that has had the displeasure of having to move files in SharePoint knows what a pain that can be.

To make matters worse, due to the specific setup I was dealing with the ‘Content & Structure’ tool was a non-option as it wasn’t functioning correctly and there were a large number of nested folders in the source library. Luckily this gave me the chance to use one of my favourite power tools – PowerShell.

This article contains a script which will copy all contents of a single library to another library,  by using the built-in MoveTo command where possible so no data is lost or by copying if the source and target libraries are in different site collections.

PowerShell Script

Instructions

  • This should be run on the SharePoint server, running it remotely will require modification.
  • Running user should ideally have admin access, but full access to both libraries and the server should be adequate.
  • Update the ‘Static Variables’ –
    • SourceWebURL
    • SourceLibraryTitle
    • DestinationWebUrl
    • DestinationLibraryTitle

Script

$ver = $host | select version
if($Ver.version.major -gt 1) {$Host.Runspace.ThreadOptions = "ReuseThread"}
if(!(Get-PSSnapin Microsoft.SharePoint.PowerShell -ea 0))
{
Write-Progress -Activity "Loading Modules" -Status "Loading Microsoft.SharePoint.PowerShell"
Add-PSSnapin Microsoft.SharePoint.PowerShell
}

$ErrorActionPreference = "Stop"

#Set Static Variables
$SourceWebURL = "https://sharepoint.domain/site" #The URL of the source website
$SourceLibraryTitle = "libraryTitle" # The title of the source list

$DestinationWebURL = "https://sharepoint.domain/site" #The URL of the destination website
$DestinationLibraryTitle = "libraryTitle" #The title of the destination list

######################### Gather relevant details ###################################################               

#Retrieve the source web and list
$sWeb = Get-SPWeb $SourceWebURL
$sList = $sWeb.Lists | ? {$_.Title -eq $SourceLibraryTitle}
if($sList -eq $null) {
    throw "Didn't find source list"
}

#Retrieve the destination web and list
$dWeb = Get-SPWeb $DestinationWebURL
$dList = $dWeb.Lists | ? {$_.title -like $DestinationLibraryTitle}
if($dList -eq $null) {
    throw "Didn't find target list"
}

#Retrieve all folders in the source list.
$AllFolders = $sList.Folders
$allItems = $sList.Items

# Check whether Check-In enabled on target library
$checkinEnabled = $dList.ForceCheckout

################### Create all folders in new location ##############################################               

$sortedFolders = $AllFolders.Folder | sort $_.Url

# Loop through the entire list of folders
$progressCount = 0
foreach($Folder in $sortedFolders) {

    # Just writes a nice progress report
    Write-Progress -Id 0 -Activity "Creating folders in target directory" -Status "$progressCount of $($sortedFolders.Count)" -PercentComplete (($progressCount / $sortedFolders.Count) * 100)

    # Define the new location of the folder
    $parentUrl = $Folder.ParentFolder.Url -replace $sList.RootFolder.Name, $dList.RootFolder.Name
    $newLocation = $Folder.Url -replace $sList.RootFolder.Name, $dList.RootFolder.Name
        
    #If a matching folder isn't found in the new library then create it
    if(!($dList.Folders | ? {$_.URL -eq $newLocation})) {
        $newFolder = $dList.AddItem(($DestinationWebURL + "/" + $parentUrl), [Microsoft.SharePoint.SPFileSystemObjectType]::Folder, $Folder.Name)
        $newFolder["Title"] = $Folder.Name
        $newFolder.Update()
    }

    $progressCount++
}
# Stops the progress report
Write-Progress -Id 0 -Activity " " -Status " " -Completed

#################### Move files if in same web ######################################################               

# If in the same location then move the documents
$progressCount = 0
if($SourceWebURL -eq $DestinatioNWebURL) {

    #Move all of the documents
    $count = $allItems.Count - 1

    #Loop through each of the documents discovered in the root of the library and move any items found
    while($count -ne 0) {
        # Just writes a nice progress report
        Write-Progress -Id 0 -Activity "Moving documents to target directory" -Status "$progressCount of $($allItems.Count)" -PercentComplete (($progressCount / $allItems.Count) * 100)

        # Work out the new destination then move the file
        $itemNewUrl = $allItems[$count].Url -replace $sList.RootFolder.Name, $dList.RootFolder.Name
        $Dest = $DestinationWebURL + "/" + $itemNewUrl
        $allItems[$count].File.moveTo($Dest, $true)
        $count--
        $progressCOunt++
    }
}
# Stops the progress report
Write-Progress -Id 0 -Activity " " -Status " " -Completed

###################### Copy files if different web #################################################              

# If the source and destination are different sites then need to copy the item over
$progressCount = 0
if($SourceWebURL -ne $DestinationWebURL) {
    #Loop through each of the documents discovered in the root of the library and perform some action
    foreach($Item in $AllItems)
    {
        # Just writes a nice progress report
        Write-Progress -Id 0 -Activity "Copyring documents to target directory" -Status "$progressCount of $($AllItems.Count)" -PercentComplete (($progressCount / $AllItems.Count) * 100)

        #Get the Binary Stream of the referenced file, such that we can create the same file in the destination environment
        $sBytes = $Item.File.OpenBinary()

        #Create A New file using the name and binary stream from the original document, assign it to a variable.  This variable will later be called when setting properties
        $dFile = $dList.RootFolder.Files.Add($Item.Name, $sBytes, $true)

        #Return all fields for the source item which are not read-only
        $AllFields = $Item.Item.Fields | ? {!($_.sealed)}

        #Loop through all of the fields returned and perform some action
        foreach($Field in $AllFields)
        {
            #If the source item has a property that matches the name of the field perform some action
            if($Item.Properties[$Field.Title])
            {
                #If the destination file does not contain the same property as the source item perform some action.
                if(!($dFile.Properties[$Field.title]))
                {
                    #Add a property to the file matching title and value of the same field in the original item
                    $dFile.AddProperty($Field.Title, $Item.Properties[$Field.Title])
                }
                else
                {
                    #If the property already exists, set the value on the destination item to the same value as it was in the source item.
                    $dFile.Properties[$Field.Title] = $Item.Properties[$Field.Title]
                }

            }
        }
        #Commit the changes by updating the destination file.
        $dFile.Update()

        if($checkinEnabled -and $item.File.CheckOutStatus -ne "None") {
            $item.File.CheckIn("Checked In after scripts Copy/Move")
        }
    }
}
# Stops the progress report
Write-Progress -Id 0 -Activity " " -Status " " -Completed

 

 

I would like to thank Roger Cormier for his article ‘How to Copy SharePoint Documents Between Site Collections Using PowerShell‘ which provided a good basis for this script.

2 thoughts on “SharePoint 2013 – PowerShell Script to Move all Documents Between Libraries

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.