Search code examples
powershellsmb

PowerShell: Download a List of Files from SMB Shares without Overwriting


I'd like to download a list of pre-defined files from remote network shares, without overwriting local files that have the same name. I have this functionality mostly working in PowerShell through use of the Bitstransfer module. You can see in the code that C:\SMB\paths.txt points to a local file which includes remote SMB paths.

SMBBulkDownload.ps1:

Import-Module bitstransfer

foreach($line in Get-Content C:\SMBDump\paths.txt) {
    $sourcePath = $line
    $destPath = "C:\SMBDump\"
    Start-BitsTransfer -Source $sourcePath -Destination $destPath     
}

where C:\SMB\paths.txt contains (sample snippet):

\\10.17.202.21\some\dir\app.config
\\10.19.197.68\some\dir\app.config
\\10.28.100.34\some\dir\Web.config

The code above downloads the files correctly if they have different file names. In the case where the file names are the same bitstransfer returns an ACCESSDENIED error. This could be due to the module not supporting same-name file copies, or due to race conditions in copying files with the same name at the same time. This is unfortunate because my work requires bulk downloads of many different files named the same, such as App.Config, Web.Config, etc.

Error:

Start-BitsTransfer : Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))

Does anyone have a solution that can get around this same-name file copy block? I'd ideally like a solution that copies files with redundant names into the same directory with a "_1" or "_2" or _ suffix.


Solution

  • Assuming your errors are rooted in your file names theory this would be something to try out.....

    Your paths all have one unique component: the server IP address or name. I would suggest making a subdirectory for each address or append that to the name.

    Files organized in folders by host

    foreach($line in Get-Content C:\SMBDump\paths.txt) {
        $hostName = ([uri]$line).Host
        $fileName = Split-Path $line -Leaf
        $destPath = "C:\SMBDump\$hostName\$fileName"
        Start-BitsTransfer -Source $sourcePath -Destination $destPath     
    }
    

    or

    Files in same folder with host naming prefix

    foreach($line in Get-Content C:\SMBDump\paths.txt) {
        $hostName = ([uri]$line).Host
        $fileName = Split-Path $line -Leaf
        $destPath = "C:\SMBDump\$hostName-$fileName"
        Start-BitsTransfer -Source $sourcePath -Destination $destPath     
    }
    

    This should cause no name conflicts and as long as your file contains unique values there should be no contention here at all.