EL MOJO
EL MOJO

Reputation: 793

Powershell File Transfer to Remote Machine Via Stream Fails

I am trying to transfer a file from a local machine to a remote machine via Powershell. I am running Powershell version 3.0 and do not have the ability to upgrade Powershell.

I must a remote session with specified credentials.

Below is the code that I am using:

# $credential variable created and set elsewhere

$command = {
param( [System.IO.FileStream]$inputStream, [string]$filePath, [string]$fileName )

    $writeStream = [System.IO.File]::Create( "$filePath\$fileName" );
    $inputStream.Seek(0, [System.IO.SeekOrigin]::Begin);
    $inputStream.CopyTo( $writeStream );
    $writeStream.Close();
    $writeStream.Dispose();
}

$readStream = [System.IO.File]::OpenRead( "C:\temp\tempfile.txt" );
$path = "C:\temp";
$file = "test.txt";

$session = New-PSSession -ComputerName RemoteMachineName -UseSSL -Credential $credential;

# this works when copying the file/stream locally
Invoke-Command -ArgumentList $readStream, $path, $file -ScriptBlock $command
# this does not work when copying the file/stream remotely
Invoke-Command -Session $session -ArgumentList $readStream, $path, $file -ScriptBlock $command

$readStream.Close();
$readStream.Dispose();

The code above works when attempting to copy this file locally. But when attempting to copy this file to a remote machine via a session, I get the following error:

Cannot process argument transformation on parameter 'inputStream'. Cannot convert the "System.IO.FileStream" value of type "Deserialized.System.IO.FileStream" to type "System.IO.FileStream". + CategoryInfo : InvalidData: (:) [], ParameterBindin...mationException + FullyQualifiedErrorId : ParameterArgumentTransformationError + PSComputerName : RemoteMachineName

I cannot find anywhere on the internet any reference to the class "Deserialized.System.IO.FileStream". My guess is that the stream is getting serialized as it gets transferred to the remote machine and that this is what is causing the issue.

Does anyone know how to do this properly or have a better method for transferring this file? The final file(s) are large so I cannot just read them into a byte array without running out of memory. Also, I must use the specified credentials and the transfer must be encrypted.

Upvotes: 1

Views: 830

Answers (1)

StephenP
StephenP

Reputation: 4081

The objects in the remote session are torn down, sent across the wire, then reconstructed. Some types can be rebuilt into live objects while others are Deserialized versions as you suspected. There is a blog post here that explains it.

Note that PSRemoting is always encrypted with AES-256 after initial authentication regardless of HTTP or HTTPS protocol being used.

Working from this answer for downloading a file over PsSession I reversed the process to send 1MB chunks across the wire and write them to a stream on the remote machine. Since this uses whatever session you have already established it should meet your requirements. It may be possible to optimize this but I ran some basic tests on 1GB file and it worked as expected.

function UploadSingleFile {
    param(
        [System.Management.Automation.Runspaces.PSSession] $RemoteSession,
        [string] $RemoteFile,
        [string] $LocalFile,
        [int] $ChunkSize = 1mb
    )
    $FileInfo = Get-Item -LiteralPath $LocalFile
    $FileStream = $FileInfo.OpenRead()
    try {
        $FileReader = New-Object System.IO.BinaryReader $FileStream
        try {
            for() {
                $Chunk = $FileReader.ReadBytes($ChunkSize)
                if($Chunk.Length) {
                        Invoke-Command -Session $RemoteSession -ScriptBlock {
                        param(
                            [string] $RemoteFile,
                            [byte[]] $Chunk
                        )
                        $FileStream = [System.IO.FileStream]::new($RemoteFile, [System.IO.FileMode]::Append, [System.IO.FileAccess]::Write)
                        $FileStream.Write($Chunk, 0, $Chunk.Length)
                        $FileStream.Close()      
                    } -ArgumentList $RemoteFile, $Chunk
                } else {
                    break;
                }
            }
        } finally {
            $FileReader.Close();
        }
    } finally {
        $FileStream.Close();
    }
}

Invocation:

$session = New-PSSession -ComputerName ComputerOne -UseSSL -Credential $credential
UploadSingleFile -Session $session -RemoteFile 'c:\temp\Destination.bin' -LocalFile 'C:\temp\source.bin'

I then used Get-FileHash to confirm the file copied successfully.

PS C:\> Get-FileHash C:\temp\source.bin

Algorithm       Hash                                                                   Path                                                                                                                                                                                               
---------       ----                                                                   ----                                                                                                                                                                                               
SHA256          C513BFBCF4501A06BCC5F6F7A589532F7D802AA2C032D88143B0A31C1CFBD5F4       C:\temp\source.bin                                                                                                                                                                                   



PS C:\> Get-FileHash '\\ComputerOne\c$\temp\Destination.bin'

Algorithm       Hash                                                                   Path                                                                                                                                                                                               
---------       ----                                                                   ----                                                                                                                                                                                               
SHA256          C513BFBCF4501A06BCC5F6F7A589532F7D802AA2C032D88143B0A31C1CFBD5F4       \\ComputerOne\c$\temp\Destination.bin

Upvotes: 1

Related Questions