Over the last weeks I’ve been working with a customer who is migrating to VSTS for building and releasing desktop applications. For years they had been compiling their applications, signing their binaries and creating their setups on a dedicated machine using custom batch scripts that ran on MSBuild post-build only on that specific machine. After this, they copied the resulting installer to a FTP server for download by their customers. Moving to VSTS meant that their packaging process needed to be revisited.

In this blog I will share a proof of concept that I did for them using VSTS, SignTool.exe and InnoSetup for building and releasing an installer. I wanted to proof that we could, securely, store all the code and configuration we needed in source control and configure VSTS build & release to create an setup file using InnoSetup and distribute that to our users using Azure Blob Storage.

Creating the application

Let’s start by creating a simple console application. After creation, I moved in this mighty code:

var obj = new
{
	Message = "This is a very complicated Console application with a dependency on Newtonsoft.Json"
};

var json = JsonConvert.SerializeObject(obj);

Console.WriteLine(json);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();

Next to this source code in Program.cs, I also added two more files: cert.pfx and setup.iss. The first is the certificate that I want to use for signing my executable. This file should be encrypted using a password and a decent algorithm (see here for a discussion of how to check that) so that we can safely store it in source control and reuse it later on. The second file is the configuration that we will use later on to create a Windows Setup that can install my application on the computer of a customer.

Setting up the build

After this, I created a VSTS build. After creating a default .NET Desktop build, I added two more tasks of type Command Line. The first task for signing my executable, the second for creating the setup.

The actual commands I am using are:

  1. For signing the exe: “C:\Program Files (x86)\Windows Kits\10\bin\10.0.17134.0\x86\signtool.exe” sign /f $(Build.SourcesDirectory)\ConsoleApplication\ConsoleApplication\bin\$(BuildConfiguration)\cert.pfx /p %PASSWORD% $(Build.SourcesDirectory)\ConsoleApplication\ConsoleApplication\bin\$(BuildConfiguration)\ConsoleApplication.exe
  2. For creating the setup: “c:\Program Files (x86)\Inno Setup 5\ISCC.exe” $(Build.SourcesDirectory)\ConsoleApplication\ConsoleApplication\bin\$(BuildConfiguration)\setup.iss

This requires that that I configure the first execution with an environment variable with name PASSWORD (lower right of the screenshot above), which I in turn retrieve from the variables of this build. This is the password that is used to access cert.pfx to perform the actual signing. This build variable is of the type secret and can thus not be retrieved from VSTS and will not be displayed in any log file. The second task of course requires InnoSetup to be installed on the build server.

Finally, instead of copying the normal build output I am publishing an build artifact containing only the setup that was build in the previous step.

Setting up the release

Having a setup file is of no use if we are not distributing it to our users. Now in this case, I need to support three types of users: testers from within my organisation, alpha users who receive a weekly build and all other users that should only receive stable builds. It is important to provide testers with the same executable (installer) that will be delivered to users later and not having a seperate delivery channel for them. At the same time, we do not want alpha users to get a version that is not tested yet or all users to receive a version that hasn’t been in alpha for a while yet. To accomodate this, I have created a release pipeline with three environments: test, alpha and production:

Every check-in will automatically trigger a build, which will automatically trigger a release to my test environment. After this, I manually have to approve a release to the alhpa group and from there on to all customers. We call this ring based deployments. While every release to an environment contains just one step, namely copy of the setup to Azure Blob Storage – the locations all differ, thus enabling us to deliver different versions of our software to different user groups:

All in all, it took me about an hour and a half to set this all up and proof that it was working. This shows how easy you can get going with continuous deployment of desktop applications.