Connecting to SSH Servers in .NET

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 '' to the project. Initially, I thought that it was only available as a download from Codeplex. However, after typing '' into my NuGet package manager, I was very happy to find that it was available inside NuGet, too.

Figure 4: Adding '' 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'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.


  • SSH .NET VT320, VT400+ emulation

    Posted by MishaT on 12/20/2016 03: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 02: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 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 03: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 lib?

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

    Posted by amit on 11/18/2015 02: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 Thanks

  • Software Engineer

    Posted by Miguel Silva on 11/10/2015 07:33am

    This Tutorial was really helpful, Thanks.

  • Developer

    Posted by Dee on 10/05/2015 11:04am

    How do you login with a IP address

    • N/A

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

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

  • unprintability of pages

    Posted by Gjuro Kladaric on 01/26/2015 03:14am

    have you ever tried to print your web articles? it is a very sad business, even when I click 'print article' button... and you are kind of place that show how software developers should do the business... ccc... I like to read you articles, but am unable to print them (nicely) :-) fix that, I know you can :-)

    • Re:unprintability of pages

      Posted by on 02/05/2015 05:37am

      We made some changes to the print pages. It should be much better now.

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

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

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