With the release of Team Foundation Server 2010 and Team Explorer Everywhere Microsoft extended the reach of TFS beyond just the Microsoft ecosystem and provided a way for people doing Linux and Mac development to use TFS to meet their Application Lifecycle Management (ALM) needs.

Whilst TFS works great for source control and work items for Linux development it doesn’t include a Linux specific build agent so many people think it can’t be done.  But that’s not quite true. And yes, before people point out the obvious, there are a number of excellent Linux specific build engines that can do automated builds from TFS source by calling the tf command line, but they don’t integrate into TFS in anywhere near the same way that Team Build does which is why Team Build a desirable option for many places.

Anyway, back to the issue at hand, thanks to tools like PuTTY we can do remote shell calls to Linux boxes as part of the build process, and you we can also copy files to and from the Linux box as part of the same process which gives us the ability to compile on Linux and still get the compiled binaries placed in the drop location as per normal windows builds.

For example I have a customer at the moment who is moving their Progress source code (yuck!) out of Roundtable and into TFS, and as part of that transition we’re moving the build from a custom set of shell scripts into Team Build.  Without going into the specifics of their particular needs, let’s have a quick look at some of the key ingredients needed to get Team Build successfully executing commands on a remote Linux server.

Install and Configure PuTTY on the Windows Build Agent

As mentioned before we’re going to use some of the PuTTY tools to connect to the remote Linux box.  If you haven’t already done so, go grab the Windows PuTTY installer from the download page and install it on your build server.

Now we need to configure PuTTY so it knows how to connect to the remote server.  We’re going to use SSH to do this, and since we don’t want to store passwords in our build scripts we want to use a public key for authentication.

Login to your build server using the build service account.  Note: This is important! If you don’t do this then you will likely have issues the first time you run a build as ssh will prompt the first time you use a public key for authentication and the build will hang waiting for a response.

Now open a command prompt, go to the install folder for PuTTY and run puttygen.exe.  A dialog will appear.


Click Generate and move the mouse around a little as requested (such fun!) until you get a key generated.


Note that we’re intentionally leaving the passphrase blank so that we don’t get prompted for a password during the build process.  This of course means there is a potential security hole if someone attacks the windows build agent machine, so make sure that the account you will log in to on the Linux box is not a privileged account.  Save both your public and private keys.

Next, login to your Linux box and open the $HOME/.ssh/authorized_keys file in vim and paste in the public key as a new entry in that file, then save the changes, close the file and log off.

Now to test it and get through that first configuration prompt.

From your windows command prompt run plink to do a listing of your home directory on the Linux box.  For example:

plink –batch –ssh –i privateKey.ppk [email protected] “ls –l”

When prompted to store the key answer with a “y"es.  Once that’s been done once we won’t get asked again when we run automatic builds.

Assuming this works, and you see something coming back then we’re right to move on.

Customising TFS Team Build to Build on Linux

From here it’s pretty simple and works much the same as the customisation for VB6 builds I’ve posted about in the past.  This won’t be a complete blow-by-blow on how to do it – just enough information to cover the important parts you need to know.

For all the remote Linux interactions we’re going to rely on the InvokeProcess workflow activity to make the calls we need for our build process.

As a note, I usually take the existing Default Process template and gut it – removing most of the activities from after the workspace has synced (i.e. the get latest section) and the code has been pulled down to the build agent and use that as a starting point for building up the Linux build process.

Regardless, once the code has been pulled down into the build agent’s workspace we have two choices for making the source available to Linux.  We could define a network share that points to the build agent’s $(Sources) folder and get Linux to build the sources using a UNC path (via samba), or alternatively we could use PuTTY’s pscp command to copy the sources to the Linux machine for local compilation and copy the compiled output back when the build completes.  You should do whatever you are more comfortable with, and since both have pros and cons it will be a matter of how your Linux build works that dictates the best approach.

For this example let’s do a copy of code onto the Linux box and then call the compile.

Begin by dragging an InvokeProcess activity into your build process workflow at an appropriate point:


and set the properties of it as follows

FileName: """" & Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) & "\PuTTY\pscp.exe" & """"
Arguments: "-batch -scp -i """Path\to\\privateKey.ppk"" “ & SourcesDirectory &  “ [email protected]:/build/sources"""

Don’t forget to check for error conditions when the task completes.

Next drag another InvokeProcess activity into your workflow and this time use plink instead of pscp and call the command or script you need to do the compile. The following activity properties show an example:

FileName: """" & Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) & "\PuTTY\plink.exe" & """"
Arguments: "-batch -ssh -i ""Path\to\privateKey.ppk""  [email protected] ""<command to run – e.g. make all>"""

Again, don’t forget to check for errors.

Finally when the build is done use another InvokeProcess activity to call pscp as shown above and copy any compiled output to the drop location.  For reference the drop location folder can be found using the BuildDetail.DropLocation property.


Hopefully this is enough to get you on your way to building your Linux applications via TFS 2010’s Team Build.  Good luck!