TFS 2015 build vNext – Upload directory to FTP

In previous post I showed you how to create a custom build step, upload and use it in the Team Foundation Server 2015. This time we will try to create more complex build task. This will be FTP uploader. Keep reading.

There is already a step which gives you the possibility to upload files to FTP location and it comes out of the box with TFS 2015. This is called cURL and you can find it in Utility tasks section. Using this tool however has at least two downsides. First is that you have to install the cULR tool on the build agent machine, which is not a problem in most cases, but still an administration task to do. The other downside is that the cURL copies single files only instead of whole folders. Now if we want to upload the tree structure of our artifacts to the ftp server we need to find a different solution.

The friend of mine wrote a time ago a ftp client class in c#, which is based on web request and uploads files to the ftp recurrently. This was compiled in a dll and was used in our old, XAML based build definition. I‘ve decided to reuse it and integrate it as a custom build step in new TFS 2015 build vNext. This is how this class looks like

The goal is to use the FtpClient it in powershell so we can create an instance of it in the script. Let’s create new custom build step. First the task.json file

{

"id": "AB66EC11-60D7-49D6-9D80-351782145E92",

"name": "FtpUpload",

"friendlyName": "Ftp upload",

"description": "Uploads directory content to ftp",

"helpMarkDown": "",

"category": "Deploy",

"author": "Mirek",

"version": {

"Major": 1,

"Minor": 0,

"Patch": 0

},

"minimumAgentVersion": "1.83.0",

"instanceNameFormat": "Ftp upload $(solution)",

"inputs": [

{

"name": "sourceDir",

"type": "filePath",

"label": "Directory to upload",

"defaultValue":"**\\bin",

"helpMarkDown": "The directory which content will be uploaded",

"required":true

},

{

"name":"user",

"type":"string",

"label":"Ftp user",

"required":false,

"defaultValue":""

},

{

"name":"password",

"type":"string",

"label":"Ftp password",

"required":false,

"defaultValue":""

},

{

"name":"ftpAddress",

"type":"string",

"label":"Ftp address",

"helpMarkDown": "Ftp targt address, where the folder will be copied",

"required":true,

"defaultValue":""

},

{

"name":"doCleanTarget",

"type":"string",

"label":"Clean target",

"helpMarkDown": "Do clean target frp directory before uploading",

"defaultValue": "false",

"required": false

}

],

"execution": {

"PowerShell": {

"target": "$(currentDirectory)\\FtpUpload.ps1",

"argumentFormat": "",

"workingDirectory": ""

}

}

}

Our build step requires five string type arguments. The source directory, which should contain the directory which content will be uploaded to the ftp server. Note that its type is filePath instead of string. That gives a possibility to select the directory from a graphical tree in the web portal and at the end is stored as a string anyway. Next we have the ftp user, password and ftp target address where the content will be uploaded. Last parameter indicates if the ftp target should be cleaned before upload. This is useful when you have more than one build steps uploading the content to the same ftp address. Then you set to clean the ftp target only on the first step. Since this is a kind of flag parameter using the boolean type would of course makes more sense here. However at the time of writing this post there were some problems interpreting a boolean parameter in the powershell part of the custom build step, so for the sake of simplicity I left it as string.

Now we are ready to implement the actual build step script FtpUpload.ps1

After checking that the source directory has any content to upload (lines 12-13) we first import the FtpClient type from the .cs file (line 16). This is possible thanks to the powershell method called Add-Type which is awesome in my opinion. You can import types from .net assemblies, from source codes, which are compiled on the way. You can even write some inline c# code, assign it as a string variable and use it directly in the powershell. This basically extends the capabilities of the powershell to everything that is available from the regular c# code! Enough to say that thanks to it you have unlimited possibilities to creating a powershell cmdlets that manage the software you implement. Thanks to that we could simply import the c# class and create an object of type FtpClient right in the script (line 18).

Now we are ready to upload our custom build step to the TFS REST API. If you don’t know how to do that I wrote a guide on this available here. If all goes well we should see new build step under the Deploy section in the web portal. here is an example how it looks in the portal.

The source of this custom build step is available as the post attachement.