Create a release in TFS 2015 / VSTS Release Management

This is the end of the journey of the last serie of posts. I’m now at the situation where I have a build that produces a single zip file with everything I need to deploy the software and a bunch of PowerShell scripts that relase the software using that zip as a source artifact.

Now it is time to automate the process with Release Management. I want to use RM because the process is automated on a chain of environments, but also I have traceability, auditing, and verification of the release procedures. I’m not going to cover all the steps needed to create the a release definition, I want to focus on how simple is creating a Release Process when you adopt Zip+PowerShell approach. I strongly suggest to have a look at official site if you never heard of VSTS / TFS Release Management.

If you have a zip and PowerShell files that release the software using only the zip, you are ten minutes away from a working Release Management definition

Let’s start creating a first release, the most important aspect is adding a build as Artifacts source, this allows the release to consume what is produced by the build.

this image shows how a build can be linked to Release MAnagement, so the RM process can access artifacts produced by the build

Figure 1: Add your build as a artifacts source for the release process

This build produces a couple of artifacts, the zipped file with everything and the set of PowerShell scripts needed to run the software (as I suggested they should always be stored in source control and moved as build artifacts). Thus the release process is really simple and is composed by only three steps.

The entire release process is only three task, as described further in the post.

Figure 2: The entire release process is composed only by three scripts

First two steps are used to copy zip file and installation scripts in a folder of the target machine (c:\deploy\jarvis\…). as you can see I’m using local administrator user, so this technique can be used even if the machine is not in the domain. You should now run this script on the machine where the agent is running.

Set-Item WSMan:\localhost\Client\TrustedHosts -Value WebTest -Force

This instruction should be run to the machine where the build agent is running, and specify that the target machine is a Trusted Hosts. If you are using domain credentials, this is not needed.

The password is stored inside a Release variable, to use secret variable feature, so noone that can edit this release can see the password in clear text.

Password for target machine is stored as secret variable in Configuration section

Figure 3: Password for target machine is stored as secret variable in Configuration section

Final step in Figure 2 is using the task to run the installation script in a remote machine machine.

This image shows the configuration of the Remote Powershell task that allows you to execute a powershell in a remote machine.

Figure 4: Run PowerShell in a remote machine to install software

If you already tested the script manually, there is no reason why it should fail in RM process. Remember that you should never use command that interact with the shell, and you should use Write-Output instead of Write-Host in your PowerShell script to be sure that no shell is used.

With only three task I’ve created a release definition for my software

Now you can trigger a new release, or have each build trigger a new release on the dev machine. The really interesting aspect is, using GitVersion and GitFlow, when you select the build you want to release, you can choose the branch you want to deploy instead of understanding what is in a build from its number.

Thanks to gitversion when you trigger a new release you can choose the build that you want to deploy, and you can choose using a semantic versioning like 1.5.0 or 1.5.0-beta1 instead that a generic build number

Figure 5: Trigger a release manually allows to specify the build you want to release

Ad you can see from Figure 5, you can easily choose the version of the software you want to release, it is really clear from the name of the build. Once the Release is started, if the deploy is set to occur automatically, the release immediately starts

This image simply shows that the release is in progress

Figure 6: Relase is in progress

Once the release is in progress, from the summary you can verify what version of the software is released in what environment. I have only one environment for this demo, and I can verify that 4 minutes ago the version 1.5.0 is deployed in that environment.

This image shows all releases done with this definition. New release of version 1.5.0 was completed 4 minutes ago

Figure 7: New release was deployed successful

I can now verify that the new version was really deployed correctly opening the software in target machine.

Opening the software the new version confirm that the deploy was really successful

Fgiure 8: New version of the software is correctly deployed

Once you have everything in place and use simple PowerShell approach, setting up a release management process is really straightforward.

Gian Maria.

How to manage PowerShell installation scripts

In  previous post I explained how I like to release software using a simple paradigm:

build produces one zipped file with everything needed for a release, then a PowerShell scripts accepts the path of this zipped release and installation parameters and executes every step to install/upgrade the software.

This approach has numerous advantages, first of all you can always test script with PowerShell ISE in a Developer Machine. Just download from build artifacts the version you want to use for test, load installation script in PowerShell ISE, then run the script, and if something went wrong (the script has a bug or needs to be updated) just debug and modify it until it works.

My suggestion is to use Virtual Machines with snapshots. The process is,

restore a snapshot of the machine without the software installed, then run the script, if some error occurred, just restore the snapshot, fix the script, and run again.

You can do the very same using a snapshot of the VM where the software has a previous version of the software installed, so you can verify that the script works even for an upgrade, not only for a fresh installation. This is a really simple process, that does not involve any tool related to any release technology.

The ability to debug script using VM and snapshot, is a big speedup for release script development. If you are using some third part engine for Release Management software, probably you will need to trigger a real release to verify your script. Another advantage is that this process allows you to do a manual installation where you can simply launch the script and manually verify if everything is good.

You should store all scripts along with source control, this allows you to:

1) Keep scripts aligned with the version of the software they install. If a modification of the software requires change in installation scripts, they are maintained togheter.
2) You can publish script directory as build artifacts, so each build contains both the zipped release, and the script needed to install it.
3) History of the scripts allows you to track the entire lifecycle of the script, and you can understand why someone changed the script in version Y of your software.
4) You can test installation script during builds or run during a Build for a quick release on a test environment

Final advantage is: the script runs on every Windows machine, without the need to use tasks, agents, or other stuff. Once the script is ready to go, you first start testing in DEV, QA and Production environment manually. Manual installation is really simple, just download artifacts from the build, run the script, check script log and manually checks that everything is ok. If something went bad (in DEV or QA) open a Bug and let Developers fix the script until everything is ok.

Once the script start to be stable, you can proceed to automate the release.

Gian Maria.

Checklists are prerequisites for Release Automation

I’ve dealt in some posts on how to deploy an application with a PowerShell script that uses an archive produced by a build. Automating a release could be simple or complex, depending on the nature of the software to be deployed, but there is a single suggestion that I always keep in my mind:

If you don’t have one or more Checklists for manual installation of a software do not even try to autmate installation process

Automatic Release and Continuous Deployment is a real key part of DevOps culture, but before starting to write automation scripts, it is necessary to ask yourself: Do I really know every single step to be done in order to release the software?

This seems a simple question, but in my experience people always tends to forget installation steps: obscure settings in windows registry or in some files, operating system settings, prerequisites, service pack and so on.

Since the devil is in the details, deploying a software manually without a cheklist is almost impossible, because the knowlege on “how to deploy the software” is scattered among team members.

Scripts are stupid, there is no human intelligence behind a script, it simply does what you told it to do, nothing more.

Before even try to write a script, you should write one or more CheckLists for the installation: PreRequisite Checklist, Installation Checklist, Post Installation Checklist and so on. Each checklist should contain a series of tests and tasks to be accomplished in order to release the software. If you are not able to take a person, give her/him installation checklists and have her/him install the software successfully, how you can think to write a script that automate the process?

The correct process to create automated script is:

1) Generate PreRequisite, Installation, PostInstallation CheckList.
2) Run them manually until they are complete and correct, write down the time needed for each step
3) Start automating the most time consuming steps.
4) Proceed until every step is automated.

Usually it is not necessary to immediately try to automate everything, as an example, if you use virtual machine, you can use golden images to use machines with all prerequisites already installed. This simply the deployment process because you can avoid writing PowerShell DSC, Puppet or Chef scripts to install Prerequisites.

If a specific installation step is difficult to automate (because there is some human intelligence behind it) let this task to be executed manually, but automate other expensive part of the checklist. There is always value in automating everything that can be easily automated.

Try to use Virtual Machine templates and sysprepped VM to simplify setup of prerequisites. Start creating automation for operations that are repeated for each upgrade, not those ones that are done only once.

Step by step you will eventually reach the point where you automated everything, and you can start to do Continous Deployment. If you followed CheckList and rigorous process, your deployment environment will be stable. If you immediately jump in writing automation scripts, even before knowing all the operation required to install the software (after all it is a simple web site), you will end with a fragile Automatic Deployment Environment, that will eventually require more maintenance time than a manual install.

Gian Maria.

Create a Release with PowerShell, Zipped Artifacts and Chocolatey

In previous post I described how to create a simple PowerShell scripts that is capable of installing a software starting from a zipped file that contains the “release” of a software (produced by a build) and some installation parameters. Once you have this scenario up and running, releasing your software automatically is quite simple.

Once you automated the installation with a PowerShell script plus an archive file with Everything is needed to install the software, you are only a step away from Continuous Deployment.

A possible release vector is Chocolatey, a package manager for Windows based on NuGet. I’m not a Chocolatey expert, but at the very basic level, to create a chocolatey package you should only have a bunch of files and a PowerShell script that install the software.

The most basic form of a Chocolatey package is composed by files that contains artifacts to be installed and a PowerShell script that does the installation.

The very first step is creating a .nuspec file that will define what will be contained in Chocolatey package. Here is a possible content of release.nuspec file.


        <file src=”Tools\Setup\chocolateyInstall.ps1″ target=”Tools” />
        <file src=”Tools\Setup\ConfigurationManagerSetup.ps1″ target=”Tools” />
        <file src=”Tools\Setup\JarvisUtils.psm1″ target=”Tools” />
        <file src=”release\” target=”Artifacts” />


The real interesting part of this file is the <files> section, where I simply list all the files I want to be included in the package. ConfigurationManagerSetup.ps1 and JarvisUtils.psm1 are the original installation script I wrote, and is the name of the artifacts generated by the PackRelease.ps1 script.

To support Chocolatey you only need to create another script, called chocolateyInstall.ps1 that will be called by chocolatey to install the software. This script is really simple, it needs to parse Chocolatey input parameters and invoke the original installation script to install the software.

Write-Output "Installing Jarvis.ConfigurationManager service. Script running from $PSScriptRoot"

$packageParameters = $env:chocolateyPackageParameters
Write-Output "Passed packageParameters: $packageParameters"

$arguments = @{}

# Script used to parse parameters is taken from

# Now we can use the $env:chocolateyPackageParameters inside the Chocolatey package
$packageParameters = $env:chocolateyPackageParameters

# Default the values
$installationRoot = "c:\jarvis\setup\ConfigurationManager\ConfigurationManagerHost"

# Now parse the packageParameters using good old regular expression
if ($packageParameters) {
    $match_pattern = "\/(?([a-zA-Z]+)):(?([`"'])?([a-zA-Z0-9- _\\:\.]+)([`"'])?)|\/(?([a-zA-Z]+))"
    $option_name = 'option'
    $value_name = 'value'

    Write-Output "Parameters found, parsing with regex";
    if ($packageParameters -match $match_pattern )
       $results = $packageParameters | Select-String $match_pattern -AllMatches
       $results.matches | % {
        Throw "Package Parameters were found but were invalid (REGEX Failure)"

    if ($arguments.ContainsKey("installationRoot")) {

        $installationRoot = $arguments["installationRoot"]
        Write-Output "installationRoot Argument Found: $installationRoot"
} else 
    Write-Output "No Package Parameters Passed in"

Write-Output "Installing ConfigurationManager in folder $installationRoot"

$artifactFile = "$PSScriptRoot\..\Artifacts\"

if(!(Test-Path -Path $artifactFile ))
     Throw "Unable to find package file $artifactFile"

Write-Output "Installing from artifacts: $artifactFile"

if(!(Test-Path -Path "$PSScriptRoot\ConfigurationManagerSetup.ps1" ))
     Throw "Unable to find package file $PSScriptRoot\ConfigurationManagerSetup.ps1"

if(-not(Get-Module -name jarvisUtils)) 
    Import-Module -Name "$PSScriptRoot\jarvisUtils"

&amp; $PSScriptRoot\ConfigurationManagerSetup.ps1 -deployFileName $artifactFile -installationRoot $installationRoot

This script does almost nothing, it delegates everything to the ConfigurationManagerSetup.ps1 file. Once the .nuspec file and this script are writenn, you can use nuget.exe to manually generate a package and verify installing in a target system.

When everything is ok and you are able to create and install a chocolatey package locally, you can schedule Chocolatey package creation with a TFS Build. The advantage is automatic publishing and SemVer version management done with Gitversion.exe.


Figure 1: Task to generate Chocolatey Package.

To generate Chocolatey Package you can use the standard NuGet Packager task, because Chocolatey uses the very same technology as NuGet. As you can see from Figure 1 the task is really simple and needs very few parameters to work. If you like more detail I’ve blogged in the past about using this task to publish NuGet packages.

Publish a Nuget Package to NuGet/MyGet with VSO Build

This technique is perfectly valid for VSTS or TFS 2015 on-premises. Once the build is finished, and the package is published, you should check on NuGet or MyGet if the package is ok.


Figure 2: Package listing on MyGet, I can verify all published versions

Now you can install with this command

choco install Jarvis.ConfigurationService.Host
 	-source ''
	-packageParameters "/installationRoot:C:\Program files\Proximo\ConfigurationManager" 

All parameters needed by the installation scripts should be specified with the –packageParameters command line options. It is duty of chocolateyInstall.ps1 parsing this string and pass all parameters to the original installation script.

If you want to install a prerelease version (the ones with a suffix) you need to specify the exact version and use the –pre parameter.

A working example of scripts and nuspec file can be found in ConfigurationManager project.

Gian Maria.

Using PowerShell scripts to deploy your software

I often use PowerShell scripts to package a “release” of a software during a build because it gives me a lots of flexibility.

Different approaches for publishing Artifacts in build vNext

The advantage of using PowerShell is complete control over what will be included in the “release” package. This allows you to manipulate configuration files, remove unnecessary files, copy files from somewhere else in the repository, etc etc.

The aim of using PowerShell in a build is creating a single archive that contains everything needed to deploy a new release

This is the first paradigm, all the artifacts needed to install the software should be included in one or more zip archives. Once you have this, the only step that separates you from Continuous Deployment is creating another scripts that is capable of using that archive to install the software in the current system. This is the typical minimum set of parameters of such a script.

    [string] $deployFileName,
    [string] $installationRoot

This scripts the name of the package file and the path where the software should be installed. More complex program accepts other configuration parameteres, but this is the simplest situation for a simple software that runs as a service in Windows and needs no configuration. The script does some preparation and then start the real installing phase.

$service = Get-Service -Name "Jarvis - Configuration Service" -ErrorAction SilentlyContinue 
if ($service -ne $null) 
    Stop-Service "Jarvis - Configuration Service"

Write-Output 'Deleting actual directory'

if (Test-Path $installationRoot) 
    Remove-Item $installationRoot -Recurse -Force

Write-Output "Unzipping setup file"
Expand-WithFramework -zipFile $file.FullName -destinationFolder $installationRoot

if ($service -eq $null) 
    Write-Output "Starting the service in $finalInstallDir\Jarvis.ConfigurationService.Host.exe"

    &amp; "$installationRoot\Jarvis.ConfigurationService.Host.exe" install

First step is obtaining a reference to a Windows service called “Jarvis – Configuration Service”, if the service is present the script stops it and wait for it to be really stopped. Once the service is stopped it deletes current directory, and then, extract all the files contained in the zipped archive to the same directory. If the service was not present (it is the very first installation) it invokes the executable with the install option (we are using TopShelf).

The goal  of the script is being able to work for a first time installation, as well of subsequent installations.

A couple of aspect are interesting in this approach: first, the software does not have any specific configuration in the installation directory, when it is time to update, the script delete everything, and then copy the new version on the same path. Second, the script is made to work if the service was already installed, or if this is the very first installation.

Since this simple software indeed uses some configuration in the app.config file, it is duty of the scripts to reconfigure the script after the deploy.

$configFileName = $installationRoot + "\Jarvis.ConfigurationService.Host.exe.config"
$xml = (Get-Content $configFileName)
Edit-XmlNodes $xml -xpath "/configuration/appSettings/add[@key='uri']/@value" -value "http://localhost:55555"
Edit-XmlNodes $xml -xpath "/configuration/appSettings/add[@key='baseConfigDirectory']/@value" -value "..\ConfigurationStore"


This snippet of code uses an helper function to change the configuration file with XPath. Here there is another assumption: no-one should change configuration file, because it will be overwritten on the next installation. All the configurations that are contained in configuration file should be passed to the installation script.

The installation script should accept any configuration that need to be store in application configuration file. This is needed to allow for a full overwrite approac, where the script deletes all previous file and overwrite them with the new version.

In this example we change the port the service is listening and change the directory where this service will store configuration files (it is a configuration file manager). The configuration store is set to ..\ConfigurationStore, a folder outside the installation directory. This will preserve content of that folder on the next setup.

To simplify updates, you should ensure that it is safe to delete old installation folder and overwrite with a new one during upgrade. No configuration or no modification must be necessary in files that are part of the installation.

The script uses hardcoded values: port 55555 and ..\ConfigurationStore folder, if you prefer, you can pass these values as parameter of the installation script. The key aspect here is: Every configuration file that needs to be manipulated and parametrized after installation, should be placed in other directory. We always ensure that installation folder can be deleted and recreated by the script.

This assumption is strong, but avoid complication of  installation scripts, where the script needs to merge default settings of the new version of the software with the old settings of previous installation. For this reason, the Configuration Service uses a folder outside the standard installation to store configuration.

All scripts can be found in Jarvis Configuration Service project.

Gian Maria.