Connecting to SSH Servers in .NET


Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame

Recently, I had a requirement to monitor a Linux-based server from a Windows-based computer that was being used as a management station. Normally with something like this, you'd often employ some software that interrogates systems using the SNMP (Simple Network Management Protocol) over a UDP connection.

In the case of my requirements, however, this wasn't an option. The only option I had available was to have the application log in to the remote server via SSH, issue a command or two to get the information it needed, and then log back out.


One of the great things about .NET as a framework is all of the wonderful stuff it has under the hood for dealing with network connections. You can do peer to peer, FTP, HTTP, and direct socket connections, along with much more. Unfortunately, one bad thing is that there is no built-in support for is SSH.

For those of you who have never used SSH before, or indeed have any clue about it, SSH is the process of getting a remote command line login to the server in question, and that this remote command line shall be encrypted. Typically (on the Windows platform, at least), most people use a small free application called 'Putty'. If you run this with the appropriate setup and authentication, you'll end up with something not too dissimilar to the following image:

Figure 1: A Putty application

Once I have this terminal open, apart from the information I get at login, I can run a number of commands to obtain information from the system; for example, the 'df' or Disk free command.

Figure 2: With the terminal open

As you can see, the output is fairly uniform, and would be easy to split apart and make use of in a Windows-based application. At this point, you might be thinking, if .NET doesn't do SSH natively, perhaps we can automate the connection by using something like Putty and the .NET process manipulation functions.

We could do that very easily because Putty comes with a few other tools and applications that are designed specifically for automating things directly from other programs.  As way of an example of that, many of you who use Git for source control on the Windows platform might not actually realise that, behind the scenes, it uses tools from the Putty collection to help it do the tasks that it needs to.

For our purposes, however, there is a much easier way.


Many developers will tell you that there's gold to be found in those most unexpected of places, and this couldn't be more true than paying a visit to Codeplex. Codeplex was originally a Microsoft initiative to provide their own open source code and project sharing platform, which by and large they succeeded at, even today there are many excellent projects on there run by some very passionate people.

One of those projects is "SSH.NET".

Going the way of many projects these days, SSH.NET has recently been evolved to support many of the new language features and framework APIs available in .NET 4, and although a backwards compatible version is still available, it's recommended that you use the new .NET4 re-write for new projects.

Enough talk, though; let's take a look at how we might use it. As usual, for simplicity, I'll be creating this as a .NET Windows Forms project. I won't be doing anything WinForms specific, though, and you should easily be able to adapt this to your own projects.

I'm not going to describe the project creation step by step. Instead, I'm just going to show you what my resulting Windows Forms UI looks like in Visual Studio.

If you look back at Figure 2 and count the lines that begin with '/dev', you'll see that I have six drives that I'd like to monitor on the server (the others are special Linux OS ones), so we need a UI with six progress bars on.

My user interface looks like the following:

Figure 3: A sample user interface

I've named the button 'btnUpdate' and the six progress bars 'prgDriveSpace1' through to 'prgDriveSpace2'.

Now, we need to add 'ssh.net' to the project. Initially, I thought that it was only available as a download from Codeplex. However, after typing 'ssh.net' into my NuGet package manager, I was very happy to find that it was available inside NuGet, too.

Figure 4: Adding 'ssh.net' to the project

Clicking install, as usual, does all of the work for us. Now all we need to do is wire it up. For the purposes of this post, I'm just going to do a single update at a time, triggered from a button press.

There are, however, many ways of using the library. Specifically, if you're going to be using a long running task, that might require two-way communication, and regular output from the server (such as the top or mpstat monitoring programs) then you'll want to use ssh.net's 'ShellStream' object, and run this in some kind of background worker, or async task request.

There is a CHM available showing what's available in the library (This can be downloaded from the Codeplex page), but examples of code are mostly available as answers to questions in the Codeplex discussions page for the project. Unfortunately, this does mean that, to find examples of how to use the lib, you're going to need to do quite a bit of reading. When you do find the samples, however, you'll generally find them of good quality; it's just not something that will be a quick exercise to locate.

For our purposes, however, double-click the update button, and then enter the following code into the button handler.

 using(SshClient ssh = new SshClient("server_host_name",
    "server_user_name", "server_password"))
    var result = ssh.RunCommand("df -h");

If we now place a break point on the using clause in the code, run the application, and then trace through to the 'RunCommand' call, we'll see that we get back from that an 'SshCommand' object. This SshCommand object will contain a property called 'Result', a string that contains the text returned to you after calling the 'df' command on the server. If you inspect this returned data, you'll find that it contains the same output as seen just a moment ago using Putty to log in manually.

Figure 5: Viewing the same output as with Putty

For this example, we're only interested in column number 5, and only for lines that begin with '/dev', so the first thing we need to do is grab the returned data, and split it into an array of lines. Because this is coming from a Linux server, the end of line marker will usually just be '\n' and not '\r\n' as many Windows developers might expect.

Once we have an array of lines, then we then use a little bit of Linq magic and pull out only the lines we want, into a string list. The final step, once we've isolated the lines we want, is to get the percentage used figure (from the 5th column), turn that into an integer, and assign it to the appropriate progress gauge.

I've chosen to also get the 'sdx' name from each line, then use this is in a switch to decide which progress bar to update. In a production application, you'd most likely want to generate each bar dynamically so that the application updated as appropriate for however many drives where available.

My final processing function looks like the following:

 private void ProcessResults(string resultData)
    string[] lines = resultData.Split('\n');
    List<string> deviceLines = lines.ToList().Where(x =>
    string matchPattern = @"^(/dev/.+)\s+([0-9].+)\s+([0-9].+)\
    foreach (string deviceLine in deviceLines)
       var res = Regex.Match(deviceLine, matchPattern);
       string deviceName = (res.Groups[1].Value.Replace("/dev/",
       int percentUsed = Convert.ToInt32((res.Groups[5].Value.Replace
          ("%", string.Empty)).Trim());
       case "sda1":
          prgDriveSpace1.Value = percentUsed;
       case "sdb2":
          prgDriveSpace2.Value = percentUsed;
       case "sdc2":
          prgDriveSpace3.Value = percentUsed;
       case "sdd1":
          prgDriveSpace4.Value = percentUsed;
       case "sdc3":
          prgDriveSpace5.Value = percentUsed;
       case "sdd2":
          prgDriveSpace6.Value = percentUsed;

and this is called by using


just after the 'ssh.RunCommand' line in the button handler. If all goes well, then running the program and clicking Update should give you something similar to the following:

Figure 6: The output results

And that's all there is to it.

There are many ways this can be improved; for example, where you call the 'df' command, you could call that in a number of different ways, making the output easier to deal with. Doing this, in turn, means you don't need the Regular expression call that I used, making things easier to maintain.

As I mentioned, you'll most likely want to dynamically create the UI, so you always have the correct number of entries. In my case, I ended up having to write a Windows 32 service that not only grabbed disk space statistics, but a number of other key metrics from the server, too. This service was then put to work feeding an ASP.NET MVC application.

If you have anything you'd like to see in this column, or have an idea for a future post, please let me know your comments in the comment section below, or come hunt me down as @shawty_ds on Twitter and let me know your thoughts.


  • Sr. QA Automated Software Engineer

    Posted by Marlon Cuevas on 06/04/2019 10:19pm

    Hey there, This is a very informative post. Thank you so much for sharing with everyone. I'm reaching out for some guidance, perhaps some knowledge share if i can pick your brain. I'm currently developing some automation code in C# that runs against a VT220 application that runs over UNIX. Once log and initialization has completed i'm in the application. I'm trying to learn how to grab the terminal screen, determine where the cursor is at and programmatically send in Function key and normal string values that would otherwise be keyboard pressed into the terminal. The library does handle string values and it would work if i'm sending in commands at the command line but for a terminal that runs over it i haven't been so lucky in discovering how to do this. Could you offer a suggestion as to where to look or perhaps a different library if i'm using the wrong one. Any kind of input is greatly appreciated. Respectfully M

  • How to connect to another server after connecting to landing server

    Posted by Akhila on 10/15/2018 01:46pm

    Hi, First I would connect to one of the landing server using sshClient(). After connecting to this specifies server, I have to connect to another server. How can I do that using ssh. Thanks, Akhila

  • Almost good... almost.

    Posted by anon on 01/27/2018 09:27pm

    Unfortunately SSH.NET makes a LOT of assumptions and doesn't properly negotiate SSH Connections. As a result it's nowhere near robust. Whilst it's usually fine for attaching to servers - if you try to SSH into network appliances such as HP's switchgear or most Cisco routers/switches it fails hard, it also fails on most embedded systems. You can kinda get around the problem in SSH.NET by grabbing a stream and writing a parser but it's ugly as hell, and you'd be better off pulling in an unmanaged SSL library like openSSL/SSLeay. Unfortunately, every article on SSL under DOTNET is pushing SSH.NET as the answer... it could be, depending on what you want to do with it - but when it's not, it's REALLY not : /

  • request source code

    Posted by donald on 09/15/2017 03:28am

    Hi..i'm starting learning vb.net and get stuck with SSH things, can i get the source code of this tutorial, pleaseee..

  • SSH .NET VT320, VT400+ emulation

    Posted by MishaT on 12/20/2016 11:59pm

    Hi, I'm able to connect and push commands to the server but then I get some lines of illegible encoding I don't see when I connect trough putty. Like: [4i[?4i[0;1234c IT says the console needs to be set to VT320 or VT400+. But I could not find any details on the documentation about this. Is this feature supported by SSH.NET?

  • 2nd ssh connection.

    Posted by Arturo Herrera Sierra on 09/13/2016 09:06pm

    Your tutotrial was really, thanks in advance. Normally, I am connecting to MasterRackControler of my system via putty (e.g. ) then connecting to the SubRackControlers (e.g. ssh to set or get information from each slave modules y commands; as the attachment shows. The system does not allow to connect direct to specific SRC or module directely, it needs to be through MRC ssh connection (open). I am relatively new on C#, but at least I have achieved some actions like connection to Master Rack Controler of my system as initial way to avoid using putty as the attchement shows. But, I cannot get the 2nd connection to any of the SubRack Controles and inquiring Ranci.ssh.net API I could not find something useful. Perhaps, it is sometingh trivial or simple for you. Could you give me a suggetion or tip on how to get it done ? A code example will be more understanfable for me.

  • how to pass multiple parameters to Unix script

    Posted by TKS on 12/09/2015 11:15pm

    I have to execute "aa.ksh" script and this .ksh file need multiple parameters. How do i pass these parameters to this script while calling the script from my C# code using SSH.net lib?

  • Execute two commands. The variables set by first commands should be visible to the next command

    Posted by amit on 11/18/2015 10:54pm

    Execute two commands. The variables set by first commands should be visible to the next command. I have to execute a shell script to set the db instance. And then a second command which make use of this db instance. But when i run the second command it doesn't sees the $DBINSTANCE set by previous command. I tried putting potting the commands in a single command set ( eg. cmdtxt1 && cmdtxt2 ) but it fails. Please suggest some way. It's hard to get good documentation on ssh.net. Thanks

  • Software Engineer

    Posted by Miguel Silva on 11/10/2015 03:33pm

    This Tutorial was really helpful, Thanks.

  • Developer

    Posted by Dee on 10/05/2015 06:04pm

    How do you login with a IP address

    • N/A

      Posted by Patrick M. on 11/19/2015 07:17pm

      new SshClient("server_host_name", "server_user_name", "server_password")); Replace server_host_name with the IP Address

  • Loading, Please Wait ...

  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date