2

How do I download files from a remote server over a PSSession? I'm aware that PS5 introduced Copy-Item -FromSession, but both local and remote may be not running PS5. My files are also quite large so a simple Get-Content may be problematic.

tyteen4a03
  • 1,812
  • 24
  • 45
  • You can still copy files from one machine to another using the UNC path. – EBGreen Apr 10 '18 at 13:21
  • 1
    [You understand wrongly](https://stackoverflow.com/a/39122508/4137916). The remote side can have a PowerShell as low as 2.0. (I've never tested with 1.0, for lack of machines that still have that, but I doubt that would work.) The server where you *initiate* the command must have PowerShell 5, but you can just swap things around and use `-ToSession` if required. – Jeroen Mostert Apr 10 '18 at 13:23
  • @JeroenMostert Looks like [this bug](https://github.com/PowerShell/PowerShell/issues/2469) means that the solution won't work for me. Something that doesn't require PS5 would be great. – tyteen4a03 Apr 10 '18 at 13:39
  • @EBGreen Pretty sure `Copy-Item` doesn't take credentials, which I need. – tyteen4a03 Apr 10 '18 at 13:42
  • 1
    Weeeellll, if you want to get technical... apparently this only broke with PowerShell 5.1, so 5.0 should still be good. :-P Without `Copy-Item` or the administrative shares, your options get far more ugly. I mean, [look at this stuff](https://stackoverflow.com/a/11948096/4137916). In the past, I've "solved" this by simply installing my own service on the machine so I could download files over HTTP (complete with compression). That assumed I was free to install whatever I wanted on the remote server, but did get around SMB's poor performance. – Jeroen Mostert Apr 10 '18 at 13:44
  • @tyteen4a03: You can (temporarily) establish a PS drive mapped to your UNC path; `New-PSDrive` _does_ accept credentials. – mklement0 Apr 10 '18 at 14:03
  • @mklement0 Due to some weird configuration issues that I'm not going to get into here, there is a chance that PSRemote works but UNC access doesn't, so a pure PSRemote solution is preferred. – tyteen4a03 Apr 10 '18 at 14:10
  • Something like [this](https://gist.github.com/PetSerAl/ea4cb46270172e45d4110fb6670077c1)? – user4003407 Apr 10 '18 at 15:00
  • @PetSerAl Could you attach that in an answer? – tyteen4a03 Apr 10 '18 at 15:05

1 Answers1

2

You can read file on remote side as sequence of byte arrays, stream them thru remoting, and then assemble them back to file locally:

function DownloadSingleFile {
    param(
        [System.Management.Automation.Runspaces.PSSession] $FromSession,
        [string] $RemoteFile,
        [string] $LocalFile,
        [int] $ChunkSize = 1mb
    )
    Invoke-Command -Session $FromSession -ScriptBlock {
        param(
            [string] $FileName,
            [int] $ChunkSize
        )
        $FileInfo = Get-Item -LiteralPath $FileName
        $FileStream = $FileInfo.OpenRead()
        try {
            $FileReader = New-Object System.IO.BinaryReader $FileStream
            try {
                for() {
                    $Chunk = $FileReader.ReadBytes($ChunkSize)
                    if($Chunk.Length) {
                        ,$Chunk
                    } else {
                        break;
                    }
                }
            } finally {
                $FileReader.Close();
            }
        } finally {
            $FileStream.Close();
        }
    } -ArgumentList $RemoteFile, $ChunkSize | ForEach-Object {
        $FileName = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($LocalFile)
        $FileStream = [System.IO.File]::Create($FileName)
    } {
        $FileStream.Write($_, 0, $_.Length)
    } {
        $FileStream.Close()
    }
}
user4003407
  • 21,204
  • 4
  • 50
  • 60