Recieve files over the current Session

This topic contains 2 replies, has 2 voices, and was last updated by Profile photo of Justin King Justin King 2 months, 3 weeks ago.

Viewing 3 posts - 1 through 3 (of 3 total)
  • Author
    Posts
  • #45412
    Profile photo of Justin King
    Justin King
    Participant

    In an effort to continually improve my deployment scripts, I stumbled on a function someone wrote to allow me to push files over WinRM which alleviates me opening additional ports _just_ for installation. It's a nice little function I've start including in my scripts. I attached the code at the bottom for reference.

    That said, some of my installs I need to actually _fetch_ files from the resulting build, not just push files, so I was wondering if I should try to reverse engineer a "Get-File" function or if there was a more "natural" way to use the same logic but in reverse: use the WINRM service to retrieve a file rather than opening additional ports. Has anyone done anything like this? I'm curious as to other people's approach.

    To restate the goal: I'd like to be able to push/pull small simple configs to/from remote systems without having to open the additional file and print sharing ports.

    function Send-File
    {
    	
    	[CmdletBinding()]
    	param
    	(
    		[Parameter(Mandatory)]
    		[ValidateNotNullOrEmpty()]
    		[string[]]$Path,
    		
    		[Parameter(Mandatory)]
    		[ValidateNotNullOrEmpty()]
    		[string]$Destination,
    		
    		[Parameter(Mandatory)]
    		[System.Management.Automation.Runspaces.PSSession]$Session
    	)
    	process
    	{
    		foreach ($p in $Path)
    		{
    			try
    			{
    				if ($p.StartsWith('\\'))
    				{
    					Write-Verbose -Message "[$($p)] is a UNC path. Copying locally first"
    					Copy-Item -Path $p -Destination ([environment]::GetEnvironmentVariable('TEMP', 'Machine'))
    					$p = "$([environment]::GetEnvironmentVariable('TEMP', 'Machine'))\$($p | Split-Path -Leaf)"
    				}
    				if (Test-Path -Path $p -PathType Container)
    				{
    					Write-Log -Source $MyInvocation.MyCommand -Message "[$($p)] is a folder. Sending all files"
    					$files = Get-ChildItem -Path $p -File -Recurse
    					$sendFileParamColl = @()
    					foreach ($file in $Files)
    					{
    						$sendParams = @{
    							'Session' = $Session
    							'Path' = $file.FullName
    						}
    						if ($file.DirectoryName -ne $p) ## It's a subdirectory
    						{
    							$subdirpath = $file.DirectoryName.Replace("$p\", '')
    							$sendParams.Destination = "$Destination\$subDirPath"
    						}
    						else
    						{
    							$sendParams.Destination = $Destination
    						}
    						$sendFileParamColl += $sendParams
    					}
    					foreach ($paramBlock in $sendFileParamColl)
    					{
    						Send-File @paramBlock
    					}
    				}
    				else
    				{
    					Write-Verbose -Message "Starting WinRM copy of [$($p)] to [$($Destination)]"
    					# Get the source file, and then get its contents
    					$sourceBytes = [System.IO.File]::ReadAllBytes($p);
    					$streamChunks = @();
    					
    					# Now break it into chunks to stream.
    					$streamSize = 1MB;
    					for ($position = 0; $position -lt $sourceBytes.Length; $position += $streamSize)
    					{
    						$remaining = $sourceBytes.Length - $position
    						$remaining = [Math]::Min($remaining, $streamSize)
    						
    						$nextChunk = New-Object byte[] $remaining
    						[Array]::Copy($sourcebytes, $position, $nextChunk, 0, $remaining)
    						$streamChunks +=, $nextChunk
    					}
    					$remoteScript = {
    						if (-not (Test-Path -Path $using:Destination -PathType Container))
    						{
    							$null = New-Item -Path $using:Destination -Type Directory -Force
    						}
    						$fileDest = "$using:Destination\$($using:p | Split-Path -Leaf)"
    						## Create a new array to hold the file content
    						$destBytes = New-Object byte[] $using:length
    						$position = 0
    						
    						## Go through the input, and fill in the new array of file content
    						foreach ($chunk in $input)
    						{
    							[GC]::Collect()
    							[Array]::Copy($chunk, 0, $destBytes, $position, $chunk.Length)
    							$position += $chunk.Length
    						}
    						
    						[IO.File]::WriteAllBytes($fileDest, $destBytes)
    						
    						Get-Item $fileDest
    						[GC]::Collect()
    					}
    					
    					# Stream the chunks into the remote script.
    					$Length = $sourceBytes.Length
    					$streamChunks | Invoke-Command -Session $Session -ScriptBlock $remoteScript
    					Write-Verbose -Message "WinRM copy of [$($p)] to [$($Destination)] complete"
    				}
    			}
    			catch
    			{
    				Write-Error $_.Exception.Message
    			}
    		}
    	}
    	
    }#End Function Send-File
    
    • This topic was modified 2 months, 3 weeks ago by Profile photo of Justin King Justin King.
    #45417
    Profile photo of Don Jones
    Don Jones
    Keymaster

    As a note, Remoting in v5 natively supports in-channel file copy.

    #45419
    Profile photo of Justin King
    Justin King
    Participant

    Thats certainly good to know, unfortunately it's a no-fly-zone for us right now, likely until we standardize on 2016 so v5 is prepackaged.

    Still nice to know it's incoming 🙂

    Anyone else write something in the meantime that works on v4?

Viewing 3 posts - 1 through 3 (of 3 total)

You must be logged in to reply to this topic.