SSH-Sessions Add-on with SCP/SFTP Support

From Svendsen Tech PowerShell Wiki
Jump to: navigation, search

Featured here is a fork of major version one of the popular free SSH-Sessions PowerShell module based on the SSH.NET library found on GitHub. I lost some web server logs, so I don't have exact figures, but it must have been downloaded close to 150,000 times by now.

In addition to most of what is described in that article - regarding the old version 1 - the v2 mentions are not accurate for this add-on module - this SCP add-on version "two" (fork of 1, not the same as v2 on the other page) also features SCP file uploads and downloads. It works with PowerShell version 2 and up (separate modules/DLLs for v2 and v3+). It also creates an SFTP object that you can call methods on (only the SFTP client's GetAttribute() method is currently used by my module).

If you experiment, you can make sudo for SCP work with this module, unlike with Posh-SSH (last I heard).

This is a pretty major rewrite of the first module and some things have changed, but I tried to keep it mostly backwards-compatible.

I refer to the main article for further documentation about the non-SCP related cmdlets/functions (modulo some minor differences in New-SshSession and Get-SshSession).

Using this module you will be able to upload/copy directories (complete directory structures) and files from the PowerShell host to a remote SCP-capable host. You can also download single files with SCP.

I did also add a form of support for downloading an entire directory structure recursively, something I found to be missing as a capability of the SSH.NET library. This method is based on calling the executable "find" on the remote host. If the remote host does not have find, the -UseFindMagic parameter will fail to execute. You also need an established and working SSH connection to use the -UseFindMagic parameter, because the module uses Invoke-SshCommand internally.

If you use the -UseFindItemList parameter instead of -UseFindMagic, you can supply a list of directories to create and files to copy, in the format the find utility on Linux would produce. VMware ESXi 5.x (possibly - and presumably - also earlier versions) has the find utility, so you can use -UseFindMagic with it to, say, copy the log directory locally.

If stuff initially works but then suddenly fails, try reconnecting to the host; I've experienced various timeouts / broken connections during testing. New-SshSession now has a handy -Reconnect parameter (it calls Remove-SshSession internally before connecting).




Send-ScpItem

You must add the -Scp switch parameter to New-SshSession. If you plan to download directory structures using a list of files and directories to copy with the -UseFindItemList parameter - or "find magic" with the -UseFindMagic parameter - you need to also supply the -Sftp switch parameter.

The -ComputerName parameter for Send-ScpItem can be an array of computers.

Send-ScpItem has a -Retry parameter that's set to 10 retry attempts (11 total, I might change it to 3 later) and a -Wait parameter (milliseconds to wait between attempts).

Upload A Single File

PS D:\temp> "This is the test file`nto be copied`nwith SCP" | Set-Content -enc utf8 copy_this.txt

PS D:\temp> gc .\copy_this.txt
This is the test file
to be copied
with SCP

PS D:\temp> Import-Module SSH-Sessions # not necessary on PS v3 and up as they autoload modules

PS D:\temp> New-SshSession -ComputerName ubuntuvm -Username joakimbs -KeyFile .\ubuntukey.txt

PS D:\temp> # oops, forgot -Scp and -Sftp - adding them, and -Reconnect.

PS D:\temp> New-SshSession -ComputerName ubuntuvm -Username joakimbs -KeyFile .\ubuntukey.txt -Scp -Sftp -Reconnect
ubuntuvm should now be disconnected and disposed.

PS D:\temp> Get-SshSession | ft -AutoSize

ComputerName SSH Connected SCP Connected SFTP Connected
------------ ------------- ------------- --------------
ubuntuvm              True          True           True



PS D:\temp> Send-ScpItem -ComputerName ubuntuvm -Path .\copy_this.txt -DestinationPath copy_this_linux.txt
ubuntuvm: Successfully copied '.\copy_this.txt' to 'copy_this_linux.txt'.

PS D:\temp> Invoke-SshCommand -Computer ubuntuvm -q -Command 'cat copy_this_linux.txt'
This is the test file
to be copied
with SCP

PS D:\temp> 

Upload A Complete Directory Structure

PS D:\temp> Invoke-SshCommand -Computer ubuntuvm -q -Command 'ls testdirs'
ls: cannot access testdirs: No such file or directory 

PS D:\temp> Send-ScpItem -ComputerName ubuntuvm -Path .\dirs -DestinationPath testdirs
ubuntuvm: Successfully copied '.\dirs' to 'testdirs'.

PS D:\temp> Invoke-SshCommand -Computer ubuntuvm -q -Command 'find testdirs | tail -n 5'
testdirs/dir4/A.TXT
testdirs/dir4/dir5
testdirs/dir4/dir5/dir63
testdirs/dir4/dir5/dir63/fil.txt
testdirs/dir4/dir5/FOO.txT

PS D:\temp> 

Get-ScpItem

You must add the -Scp switch parameter to New-SshSession. If you plan to download directory structures using a list of files and directories to copy with the -UseFindItemList parameter - or "find magic" with the -UseFindMagic parameter - you need to also supply the -Sftp switch parameter.

The -ComputerName parameter for Get-ScpItem can only be a single computer (use foreach or ForEach-Object and use a counter or the computer name somehow to distinguish - I didn't build this in).

Download a Single File

This is pretty straight-forward. I download the file from the Send-ScpItem single file example to a new destination file name and verify it's got the right content.

I made the mistake of forgetting a full path for the -DestinationPath parameter here. Leaving it in for educational purposes. What then happens, is that it tries to download to the default profile directory for your user in a .NET environment or some mumbo jumbo. Look in your profile root folder if it downloads successfully.

PS D:\temp> Get-ScpItem -ComputerName ubuntuvm -Path copy_this_linux.txt -DestinationPath been_around.txt
Get-ScpItem : ubuntuvm: Error copying 'copy_this_linux.txt' to 'been_around.txt'
At line:1 char:1
+ Get-ScpItem -ComputerName ubuntuvm -Path copy_this_linux.txt -DestinationPath be ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-ScpItem
 

PS D:\temp> # And I'm reminded I need a full path for the -DestinationPath parameter
PS D:\temp> # (I will probably work around this somehow later)

PS D:\temp> Get-ScpItem -ComputerName ubuntuvm -Path copy_this_linux.txt -DestinationPath d:\temp\been_around.txt
ubuntuvm: Successfully copied 'copy_this_linux.txt' to 'd:\temp\been_around.txt'

PS D:\temp> gc .\been_around.txt
This is the test file
to be copied
with SCP

PS D:\temp> 

Download a Directory Structure

Now let's try downloading the testdirs directory from the Send-ScpItem example, to a local destination directory called "scpdirs". I'll use "find magic" since I know the remote computer has a (compatible) find executable (Linux, Ubuntu). I specify -UseFindMagic to trigger this behaviour. Remember that this relies on Invoke-SshCommand internally.

I apologize for the current inconsistencies, but when you copy a directory with -UseFindMagic or list of files/dirs with -UseFindItemList, the -DestinationPath parameter suddenly does have support for a relative path...

PS D:\temp> Get-ScpItem -ComputerName ubuntuvm -Path testdirs -DestinationPath .\scptestdirs -UseFindMagic
ubuntuvm: Successfully created local directory 'D:\temp\scptestdirs\'.
ubuntuvm: Successfully copied 'testdirs/Find-Or-Remove-Empty-Dirs.ps1' to 'D:\temp\scptestdirs\Find-Or-Remove-Empty-Dirs.ps1'.
ubuntuvm: Successfully copied 'testdirs/FILE1.txt' to 'D:\temp\scptestdirs\FILE1.txt'.
ubuntuvm: Successfully copied 'testdirs/rename.zip' to 'D:\temp\scptestdirs\rename.zip'.
ubuntuvm: Successfully copied 'testdirs/C.txt' to 'D:\temp\scptestdirs\C.txt'.
ubuntuvm: Successfully copied 'testdirs/rename.exe' to 'D:\temp\scptestdirs\rename.exe'.
ubuntuvm: Successfully copied 'testdirs/FIL.txt' to 'D:\temp\scptestdirs\FIL.txt'.
ubuntuvm: Successfully copied 'testdirs/b.log' to 'D:\temp\scptestdirs\b.log'.
ubuntuvm: Successfully copied 'testdirs/rename.pl' to 'D:\temp\scptestdirs\rename.pl'.
ubuntuvm: Successfully created local directory 'D:\temp\scptestdirs\dir with spaces'.
ubuntuvm: Successfully copied 'testdirs/dir with spaces/echostuff.cmd' to 'D:\temp\scptestdirs\dir with spaces\echostuff.cmd'.
ubuntuvm: Successfully copied 'testdirs/dir with spaces/HtmlAgilityPack.dll' to 'D:\temp\scptestdirs\dir with spaces\HtmlAgilityPack.dll'.
ubuntuvm: Successfully created local directory 'D:\temp\scptestdirs\dir4'.
ubuntuvm: Successfully copied 'testdirs/dir4/B.Txt' to 'D:\temp\scptestdirs\dir4\B.Txt'.
ubuntuvm: Successfully copied 'testdirs/dir4/A.TXT' to 'D:\temp\scptestdirs\dir4\A.TXT'.
ubuntuvm: Successfully created local directory 'D:\temp\scptestdirs\dir4\dir5'.
ubuntuvm: Successfully created local directory 'D:\temp\scptestdirs\dir4\dir5\dir63'.
ubuntuvm: Successfully copied 'testdirs/dir4/dir5/dir63/fil.txt' to 'D:\temp\scptestdirs\dir4\dir5\dir63\fil.txt'.
ubuntuvm: Successfully copied 'testdirs/dir4/dir5/FOO.txT' to 'D:\temp\scptestdirs\dir4\dir5\FOO.txT'.

PS D:\temp> 

Download a Directory Structure Based on a File with Remote Paths

This file is expected to have a format like that produced from the GNU+Linux find utility. It will look like this for the example above:

PS D:\temp> Invoke-SshCommand -Computer ubuntuvm -q -Command 'find testdirs' | sc -enc utf8 findItemList.txt

PS D:\temp> gc .\findItemList.txt
testdirs
testdirs/Find-Or-Remove-Empty-Dirs.ps1
testdirs/FILE1.txt
testdirs/rename.zip
testdirs/C.txt
testdirs/rename.exe
testdirs/FIL.txt
testdirs/b.log
testdirs/rename.pl
testdirs/dir with spaces
testdirs/dir with spaces/echostuff.cmd
testdirs/dir with spaces/HtmlAgilityPack.dll
testdirs/dir4
testdirs/dir4/B.Txt
testdirs/dir4/A.TXT
testdirs/dir4/dir5
testdirs/dir4/dir5/dir63
testdirs/dir4/dir5/dir63/fil.txt
testdirs/dir4/dir5/FOO.txT

PS D:\temp> 

NB! Internally, when you use the -UseFindMagic parameter, the following is used to get a list of targets, because Invoke-SshCommand really returns an array of multiline strings, but this is all flattened when you pipe to Set-Content/sc.

$ItemList = (Invoke-SshCommand -Computer $Computer -Quiet -Command "find $Path")[0].Split("`n")

Now you can download them using this file with the parameter -UseFindItemList (wrap in parentheses and use gc/Get-Content). This does exactly the same as using the -UseFindMagic parameter in this case.

PS D:\temp> Get-ScpItem -Computer ubuntuvm -Path testdirs -Destination .\scptestdirs2 -UseFindItemList (gc .\findItemList.txt)
ubuntuvm: Successfully created local directory 'D:\temp\scptestdirs2\'.
ubuntuvm: Successfully copied 'testdirs/Find-Or-Remove-Empty-Dirs.ps1' to 'D:\temp\scptestdirs2\Find-Or-Remove-Empty-Dirs.ps1'.
ubuntuvm: Successfully copied 'testdirs/FILE1.txt' to 'D:\temp\scptestdirs2\FILE1.txt'.
ubuntuvm: Successfully copied 'testdirs/rename.zip' to 'D:\temp\scptestdirs2\rename.zip'.
ubuntuvm: Successfully copied 'testdirs/C.txt' to 'D:\temp\scptestdirs2\C.txt'.
ubuntuvm: Successfully copied 'testdirs/rename.exe' to 'D:\temp\scptestdirs2\rename.exe'.
ubuntuvm: Successfully copied 'testdirs/FIL.txt' to 'D:\temp\scptestdirs2\FIL.txt'.
ubuntuvm: Successfully copied 'testdirs/b.log' to 'D:\temp\scptestdirs2\b.log'.
ubuntuvm: Successfully copied 'testdirs/rename.pl' to 'D:\temp\scptestdirs2\rename.pl'.
ubuntuvm: Successfully created local directory 'D:\temp\scptestdirs2\dir with spaces'.
ubuntuvm: Successfully copied 'testdirs/dir with spaces/echostuff.cmd' to 'D:\temp\scptestdirs2\dir with spaces\echostuff.cmd'.
ubuntuvm: Successfully copied 'testdirs/dir with spaces/HtmlAgilityPack.dll' to 'D:\temp\scptestdirs2\dir with spaces\HtmlAgilityPack.dll'.
ubuntuvm: Successfully created local directory 'D:\temp\scptestdirs2\dir4'.
ubuntuvm: Successfully copied 'testdirs/dir4/B.Txt' to 'D:\temp\scptestdirs2\dir4\B.Txt'.
ubuntuvm: Successfully copied 'testdirs/dir4/A.TXT' to 'D:\temp\scptestdirs2\dir4\A.TXT'.
ubuntuvm: Successfully created local directory 'D:\temp\scptestdirs2\dir4\dir5'.
ubuntuvm: Successfully created local directory 'D:\temp\scptestdirs2\dir4\dir5\dir63'.
ubuntuvm: Successfully copied 'testdirs/dir4/dir5/dir63/fil.txt' to 'D:\temp\scptestdirs2\dir4\dir5\dir63\fil.txt'.
ubuntuvm: Successfully copied 'testdirs/dir4/dir5/FOO.txT' to 'D:\temp\scptestdirs2\dir4\dir5\FOO.txT'.

PS D:\temp> 

New Features Compared to Original

Specify a Key File Passphrase

Implemented 2014-09-12: Use one of these parameters with New-SshSession, to specify a key file passphrase (introduced in v2.0.0.2):

  • -PromptForKeyPass: Makes the module ask for a passphrase like it would a password.
  • -KeyPass: Allows you to specify a key file passphrase directly.

Buy/Download SSH-Sessions v2 Module

About 150,000 people have downloaded the original SSH-Sessions module, so it's quite popular. It's simple and easy to use - especially for experienced PowerShell people. I plan to rewrite this module to be like the "real v2" on the other page, but with the SCP features. Previous customers will receive a copy of the new version for free (sent to the email it was bought with - this has to be accessible to you still, of course).


  • Prices as per 2017-09-26:


License count Price Price per license
1 license 20.00 USD 20.00 USD
10 licenses 35.00 USD 3.50 USD
50 licenses 50.00 USD 1.00 USD
999 licenses / site license 109.89 USD 0.11 USD


One license costs USD $20.


Order-now-button.png


If you buy 10 licenses, it will cost 10 * 3.50 USD = 35 USD. Similarly, for 50 licenses * 1.00 USD, the cost is 50 USD in total.

If you want a site license for the software, get the 999 licenses (it's a maximum limitation of the sales web site), and this counts as an unlimited site license. The cost for a site license is then USD 109.89 at 999 licenses * 0.11 USD.

One site license refers to "installed on" or "use" within a single company/unit/organization or a single person. If you have questions, please contact me at sales@svendsentech.no or svendsentech@gmail.com .

You can buy the module now via my sales and download provider MyCommerce/Share-It. You will receive both the PowerShell version 2 module (.NET 3.5) and the one for version 3 and up (.NET 4.0).

You can get in touch with Svendsen Tech at sales@svendsentech.no or support@svendsentech.no (alternative mail: svendsentech@gmail.com ).

Remember to unblock the zip file before extracting!

The file you will receive with "35" in the name refers to .NET 3.5 and is the version designed for PSv2. The other one is for PSv3 and up.

To purchase the module and receive a download link, use this link, or the order button above, to purchase it via MyCommerce/Share-It, a Digital River company.

To get in touch with MyCommerce/Share-It customer service and refer to my vendor ID, use this link.

My primary testing environment is with PowerShell version 4 on Windows 8.1 with the .NET 4 module - against Ubuntu 16.04 and CentOS 7.

The license terms can be found here.