Azure DevOps Server, SharePoint 2016, TFS Migration to Azure DevOps Server

How to Automatically Download All Files in Lists from SharePoint Farm Web Application including files in all related Site collections and their Sub Sites in one Single Powershell Script

I recently came across a task in which I had to download all the files in lists from sharepoint farm, targeting one single web application but it had close to 16 site collections and hundreds of sub sites in them.


To be exact we were doing Source code migration from TFS 2013 to Azure DevOps Server. The main problem is the project portal which is built in SharePoint and SharePoint portal is deprecated in Azure DevOps Server, and is replaced by Wiki Pages. How do we do that, go no further I have pre-built solution for you which even address the permissions on site collection along the way.

Before you Start:

  1. Make sure you have farm admin permissions
  2. Make sure you have site collection administrator permissions for all related site collections ( Even though you don’t have, I have automated this step as well, you just need to un-comment below line and put your account in global variables, which are at the very end of the actual script.
#Set-SPSite -Identity $Site.Url -SecondaryOwnerAlias $Global:SecondaryOwnerAliasAccount

3. Enough Disk Space to download all the files, belief me I have seen large videos checked in to Project Portal.

4. Time, it may take considerable time to download all the files, depending on the size and volume of the files and number of site collections and sub sites.

5. Use List filter to adjust what you want to download, I went with Document Libraries and skipped all other lists which are either system or not intended to download.

6. Run the script in powershell on the SharePoint Application Server with elevated privileges

Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue

#Function to Download All Files from a SharePoint Folder
Function Download-SPFolder($SPFolderURL, $LocalFolderPath, $currentWeb)
    Try {
        #Get the Source SharePoint Folder
        $SPFolder = $currentWeb.GetFolder($SPFolderURL)
        $LocalFolderPath = Join-Path $LocalFolderPath $SPFolder.Name 
        #Ensure the destination local folder exists! 
        If (!(Test-Path -path $LocalFolderPath))
            #If it doesn't exists, Create
            $LocalFolder = New-Item $LocalFolderPath -type directory 
        #Loop through each file in the folder and download it to Destination
        ForEach ($File in $SPFolder.Files) 
            #Download the file
            $Data = $File.OpenBinary()
            $FilePath= Join-Path $LocalFolderPath $File.Name
            [System.IO.File]::WriteAllBytes($FilePath, $data)
            Write-host -f Green "`tDownloaded File:"$File.ServerRelativeURL
        #Process the Sub Folders & Recursively call the function
        ForEach ($SubFolder in $SPFolder.SubFolders)
            If($SubFolder.Name -ne "Forms") #Leave "Forms" Folder
                #Call the function Recursively
                Download-SPFolder $SubFolder $LocalFolderPath
    Catch {
        Write-host -f Red "Error Downloading Document Library:" $_.Exception.Message
#Main Function
Function Export-SPLibraries()
    Try {
        #Get the Web Application       
        $WebApp = Get-SPWebApplication $WebAppURL
        #Get all Site collections from the web application
        $SitesCollection  = $WebApp.Sites
        #Enumerate all site collections in the web application
        Foreach($Site in $SitesCollection)
            Try {
                    #Get Site Collection URL, Owner, Content Database Details
                    Write-host -f Cyan "Working : " $Site.URL

                    #Get the Source Web
                    $global:Web = Get-SPWeb $Site.Url

                    #Set site Owner
                    #Set-SPSite -Identity $Site.Url -SecondaryOwnerAlias $Global:SecondaryOwnerAliasAccount

                    $LocalFolderPathWithCurrentSite = $Global:DownloadToLocalFolderPath+$Site.RootWeb.Title

                    Write-host -f White "About to emplty LocalFOlderPath : " $LocalFolderPathWithCurrentSite

                    #Delete any existing files and folders in the download location
                    #If (Test-Path $LocalFolderPathWithCurrentSite) {Get-ChildItem -Path $LocalFolderPathWithCurrentSite  -Recurse| ForEach-object {Remove-item -Recurse -path $_.FullName }}

                    #Iterate through each website insde rootwebsite
                    ForEach($Web in $global:Web.Site.AllWebs)
                        Try {
                                Write-host -f Yellow "Wokring on: " $Web
                                #Array to Skip System Libraries
                                $SystemLibraries =@("Pages", "Converted Forms", "Master Page Gallery", "Customized Reports", 
                                              "Form Templates", "List Template Gallery", "Theme Gallery", "Reporting Templates", 
                                                  "Site Collection Documents", "Site Collection Images", "Site Pages", "Solution Gallery", 
                                                       "Style Library", "Web Part Gallery","Site Assets", "wfpub")
                                #Get all document libraries - Exclude Hidden and System Libraries
                                #$LibraryCollection = $Web.lists | Where-Object  { ($_.BaseType -eq "DocumentLibrary") -and ($_.hidden -eq $false) -and ($SystemLibraries -notcontains $_.Title)}
                                $LibraryCollection = $Web.lists | Where-Object  { ($_.BaseType -eq "DocumentLibrary") -and ($_.hidden -eq $false) -and ($SystemLibraries -notcontains $_.Title)}

                                #Iterate through each list and export
                                ForEach($Library in $LibraryCollection)
                                            Write-host -f magenta "Downloading Document Library:" $Library.Title
                                            #Call the function to download the document library
                                            Download-SPFolder -SPFolderURL $Library.RootFolder.Url -LocalFolderPath $LocalFolderPathWithCurrentSite\$Web.Name -currentWeb $Web
                                        Write-host -f Red "Error Downloading the files:" $_.Exception.Message
                            Write-host -f Red "Error web accessing:" $_.Exception.Message
                Write-host -f Red "Error Site Collection accessing:" $_.Exception.Message
        Write-host "*** Download Completed  ***"
    Catch {
        Write-host -f Red "Error Downloading Document Library:" $_.Exception.Message
#region Runtime-Variables
$Global:WebApplication = "http://WebApplicationURL"
$Global:DownloadToLocalFolderPath = "C:\DownloadedSharepointDocs\"
$Global:SecondaryOwnerAliasAccount = "domain\FarmAdminUser"

#endregion Runtime-Variables

#Call the Function to download all document libraries from a site

Good luck!

Thanks to Sharepointdiary for providing Starting code


Solution: The following Windows service is installed on your computer: elasticsearch-service-x64. Remove elasticsearch-service-x64 to continue.


This error may come when installing Azure DevOps Server, all you need to do is to open command prompt as administrator and navigate to Elastic search directory which is usullay as below:

D:\Program Files\Azure DevOps Server 2019\Search\ES\elasticsearchv6.2\bin

And run below command:

elasticsearch-service.bat remove

You will get message as below upon success:

The service ‘elasticsearch-service-x64’ has been removed

Azure DevOps Server, CICD, VSTS

TF401219: The team project collection cannot be detached because its version ID is different than the ID for the configuration database. The collection has the following version: Dev12.M53. The Azure DevOps Server is at the following version: Dev17.M153.5.


  1. Delete Collection Database using SQL Management Studio.
  2. Now Run below command to delete the collection using TFSConfig Utility:
tfsconfig collection /delete /collectionName:MyCollection

You may need to add Environment Path Variable to TFSConfig tool location usually it is located at: C:\Program Files\Azure DevOps Server 2019\Tools

Now go to your OLD TFS installation and run below command to Detach the Database:

TFSConfig collection /detach /collectionName:MyProject

Take backup, move the database to new TFS SQL Server and re-attach the Project collection either by GUI or through command.

Best of luck.

Entity Framework

Solved : context has changed since the database was created. Consider using Code First Migrations to update the database


Are you moving changes from Development Environment to staging and you are getting above error, well there is a very quick and simple solution to the problem.

Just got to database and delete this table, and it will solve the issue, given that you already have moved the changes to the required tables etc using SQL compare or any other tool.


Considerations and Other Solutions:

There is an other approach to automatically move the changes to database by using below code in configuration.cs file in Migrations folder.

AutomaticMigrationsEnabled = true;

Please for the love of God don’t go for drop and recreate database option, as you will lose data when accidentally code is moved to staging or production, code as given below(Warning, You will lose data by using this)

Database.SetInitializer<NameOfDbContext>(new DropCreateDatabaseIfModelChanges<NameOfDbContext>());

Visit Stack Over flow for more discussion.