Azure click once publish for test and production CI/CD

Change config and sign a click once deployment for test in an Azure build application:

I have the following release pipeline in Azure:

Continuous Integration pipeline that will build for every check in on master branch. This build will trigger a Continuous Delivery pipeline that will deploy the release for the click once application to test. The same installation wil be deployed to production after approval in test on this pipeline.

I want the same build that I tested to go to production. The click once is compiled for release. The app.config I have in source is the production version. So once installed the app will default be configured to connect to production datasources.

For test however I want the app.config to be replaced with a app.config.debug version. This is a app.config that contains the test and debug datasources.

After putting this config file in the deployment the signature for the click once is no longer valid. I resigned the click once manifest for the changed config with a tool from Microsoft .NET SDK.

I used the mage.exe (from .NET framework Tools). Path for my installation: C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7.2 Tools\mage.exe.

I added 3 files to the source directory:

mage.exe
app.config.debug
ACertificateFile.pfx. This pfx is the same I used in the project to sign the manifest file.

After adding these 3 files to the source folder I added a copy step in the build pipeline to add these files to the app.publish folder:

And a script to update the app.config and resign the manifest has to be executed before copying the files to the local deployment server.

@echo on
for /f “delims=” %%F in (‘dir “$(system.defaultworkingdirectory)_Bla.WPF.BLA-.NET Desktop-CI\$(System.TeamProject)-$(Build.BuildNumber)\Application Files” /B /AD’) do set deployDir=%%F
set “to=$(system.defaultworkingdirectory)_Bla.WPF.BLA-.NET Desktop-CI\$(System.TeamProject)-$(Build.BuildNumber)\Application Files\%DeployDir%”
set “from=$(system.defaultworkingdirectory)_Bla.WPF.BLA-.NET Desktop-CI\$(System.TeamProject)-$(Build.BuildNumber)”

copy /Y “%from%\App.config.debug” “%to%\BLA.exe.config.deploy”

ren “%to%*.deploy” *.
“%from%\mage” -sign “%to%\BLA.exe.manifest” -CertFile “%from%\ACertificateFile.pfx”
“%from%\mage” -update “%to%\BLA.exe.manifest” -CertFile “%from%\ACertificateFile.pfx”
“%from%\mage” -update “%from%\BLA.application” -appmanifest “%to%\BLA.exe.manifest” -CertFile “%from%\ACertificateFile.pfx”

ren “%to%*.*” *.*.deploy
ren “%to%*.manifest.deploy” *.
del “%from%\mage.exe”
del “%from%\App.config.debug”
del “%from%\ACertificateFile.pfx”

Important steps to publish a click once app using Azure

Publish in Azure pipeline:

step 1 Build solution with parameter /target:publish

YAML: #Your build pipeline references an undefined variable named ‘Parameters.solution’. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab. See https://go.microsoft.com/fwlink/?linkid=865972

#Your build pipeline references the ‘BuildPlatform’ variable, which you’ve selected to be settable at queue time. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab, and then select the option to make it settable at queue time. See https://go.microsoft.com/fwlink/?linkid=865971

#Your build pipeline references the ‘BuildConfiguration’ variable, which you’ve selected to be settable at queue time. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab, and then select the option to make it settable at queue time. See https://go.microsoft.com/fwlink/?linkid=865971

steps:

– task: VSBuild@1

  displayName: ‘Build solution **\*.sln’

  inputs:

    solution: ‘$(Parameters.solution)’

    msbuildArgs: ‘/target:publish’

    platform: ‘$(BuildPlatform)’

    configuration: ‘$(BuildConfiguration)’

Step 2: Copy artifact files to publish folder

How to set up a local deployment for an Azure build application

*Important note: This solution will only work when you do NOT have a .gitignore file in your repository*

Configure a local agent

First requirement is that you set up a local agent that will be used for the local tasks.

How to configure local build and deploy agents is explained here:

https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/v2-windows?view=vsts

The result should look somewhat like this:

To control this agent you can choose to install it as a service on Windows.

Or you can choose to run the agent from the command line. To start and stop the agent I added two scripts:

Start.cmd:

Start.cmd:
cd c:
cd \EK-VSTS-Agent
start “EK-VSTS Azure agent” .\run.cmd
exit

Stop.cmd:

taskkill /FI “WindowTitle eq EK-VSTS Azure agent*” /T /F

Setup a build and release pipeline in Azure

Goto Pipelines in your Azure Devops project and click on new pipeline. My example uses a project named WPFDatasetWithSQL.

*Important note: This solution will only work when you do NOT have a .gitignore file in your repository*

Click continue and choose .Net Desktop and click Apply.

If you want to build the solution using a hosted machine keep the “Agent pool” set on “Hosted VS2017”. If you need local components to build you could choose to use a local machine or set up the required components in this build script.

For this example I have no need for extra components and I will keep the Agent pool on Hosted VS2017.

We are going to change a few setps in this script:

1 Set the MSBuild Arguments to /target:publish. This changes the MSBuild to add a app.publish to the build directory for click once deployment.

2 Change the step Copy Files to add the app.publish folder to the artifacts folder.
Display name = Copy Files to: $(build.artifactstagingdirectory)
Source Folder = $(Build.SourcesDirectory)\src\BLM\bin\$(BuildConfiguration)\app.publish
Contents = **\**

3 Change the artifact name.
Display name = Publish Artifact: $(System.TeamProject)-$(Build.BuildNumber)
Artifact name = $(System.TeamProject)-$(Build.BuildNumber)

Click Save and keep the default name.

Set up a release pipeline

Now we will set up a release pipeline in which we can control and manage releases for this application.

Click on Releases in the menu and click New pipeline.

Choose a Empty job template. The release pipeline is going to contain not much more than a few copy tasks.

For starters we will have to choose an artifact. Choice is simple, we are going to use the artifacts from the build pipeline. Select the Source Build pipeline set up in the previous step and finish this by clicking the Add button below.

Next step in this release pipeline is a deployment to “Test”. For this purpose we will rename the default “Stage 1” to “Test”. For this, clicking the Stage1 image (not on the link to job with task) will open a properties window. Rename Stage1 to Test and click save right top in the corner.

Now click the link to job and task in the Test stage. Click the agent job and change the agent pool to the pool where you added the local agent. In my example I added the local agent to a pool named “local machine”.

Now we will add a job to copy the publish folder to a local directory. Click on the puls sign next to “Agent job” and search for “Copy Files”

Select The task added below Job agent and fill in the details:

Select The task added below Job agent and fill in the details:
Display name = Copy Files to: c:\drop\$(System.TeamProject)\$(Release.EnvironmentName)\$(Release.ReleaseName)\
Source Folder = $(system.defaultworkingdirectory)_WPFDatasetWithSQL-.NET Desktop-CI * This last directory name is the build pipeline name
Target Folder = c:\drop\$(System.TeamProject)\$(Release.EnvironmentName)\$(Release.ReleaseName)\

The source folder will contain the pipeline name for the build pipeline preceded by an underscore:

Click save in top right hand corner.

Now we are going to add the production stage and the required copy jobs for this stage.

Click on releases in the left menu and click edit.

Click “Clone” in Test stage. And rename this new stage “Copy of Test” to “Production”. Click the task details and here I added System.TeamProject to the source folder name. This removes the build number from the destination name.

Next click the plus sign for the “Agent job” to add a command line script. With this command line we will first clean the install folder before we copy the new release in that location. The command line script is rd /S /Q c:\drop\$(System.TeamProject)\Install\

Last task for this job is to add a second “Copy Files” task. This task will copy the publish content in the install folder.

For the first run disable the Command line script because the folder will not yet exist. This will cause an error if the command is executed while the directory does not exist. After the first run the command can be enabled.

Last option is to add an approval trigger on production. A test manager or a group of testers can be allowed to approve the release after testing.

Another nice feature is to enable continuous integration and continuous deployment in Azure. For this go to the build pipeline and click the checkbox for “Enable continuous integration” in the tab “Triggers”.

Second, go to release pipeline click the continuous deployment trigger and enable continuous deployment every time a new build is available. Click save.

First two times the deployment failed. I checked the logging and fixed some typing errors.

After approving the release the install folder will be updated with the required binaries.

All done. Enjoy.

Continuously Deploy Your ClickOnce Application From Your Build Server

http://blog.danskingdom.com/continuously-deploy-your-clickonce-application-from-your-build-server/ from Daniel Schroeder’s (aka deadlydog) Programming Blog

ClickOnce applications are a great and easy way to distribute your applications to many users, and have the advantage of offering automatic application updates out-of-the-box. Even though ClickOnce applications are super easy to deploy from Visual Studio (literally 3 clicks, just click Build –> Publish –> Finish), you may still want to have your build system publish the updates for various reasons, such as:

You don’t want you (or your team members) to have to remember to manually publish a new version all the time; even if it is very quick and easy.

If you are signing your ClickOnce application (to make your app more secure and avoid annoying Windows security warnings during every update), your team members may not have the certificate installed, or you may not want them to know the certificate password.

All of the other benefits of deploying automatically from a build server; you can be sure the project was built in Release mode, all unit tests have run and passed, you are only publishing a new version of the application from a specific branch (e.g. master), etc.

In this post I’m going to show how to continuously deploy your ClickOnce application using Visual Studio Team Services (VSTS), but you can adopt what’s shown here to work on any build system, such as Team City or Jenkins.

Step 1 – Configure and publish your ClickOnce application manually

Before we can publish a new version of the application from the build server, we first have to build the project to create the artifacts to publish. And even before that, we have to make sure the project is setup properly with all of the required ClickOnce metadata. You will typically do this by going into your project Properties page and going to the Publish tab. There are several other websites and blog posts that discuss configuring a project for ClickOnce deployment, including the official MSDN documentation for configuring and publishing ClickOnce applications, so I won’t go into it any further here.

Basically you should have your project in a state where you can easily publish a new version manually, and it is configured the way that you want (includes the necessary files, has the destination to publish to specified, specifies if the application is only available online or not, etc.). Once you’ve published your project manually and confirmed that it’s configured the way you like, we can move onto the next step.

Step 2 – Setup the build on your build server

On your build server you will want to configure a new vanilla build that builds your project/solution. There are a couple modifications you will need to make that are different from building a regular console app or class library project.

The first difference is that you will need to provide the “/target:Publish” argument to MSBuild when it builds your project. Here is what this looks like in VSTS:

This will cause MSBuild to build the required artifacts into an “app.publish” directory. e.g. bin\Debug\app.publish.

The next difference is that you will want to copy that “app.publish” directory to your build artifacts directory. To do this, you will need to add a Copy Files step into your build process that copies the “app.publish” directory from the ClickOnce project’s bin directory to where the build artifacts are expected to be. You will want to do this before the step that publishes your build artifacts. Here is what this looks like in VSTS:

So we copy the files into the build artifacts directory, and then the Publish Build Artifacts step at the end will copy those files to wherever you’ve specified; in my case it’s a network share.

If you like you can now run the build and see if it succeeds. If the build fails with an error relating to an expired certificate or pfx file, see my other blog post on importing the required certificate on the build server at build-time, which involves adding one more “Import-PfxCertificate.ps1” build step before the MSBuild step.

For completeness sake, this is what my Publish Build Artifacts step looks like in VSTS, and you’ll also notice the “Import-PfxCertificate.ps1” step before the MSBuild step as well:

So we now have the ClickOnce artifacts being generated and stored in the appropriate directory. If you wanted, you could publish the build artifacts to the ClickOnce application’s final destination right now (instead of a file share as I’ve done here), but I’m going to follow best practices and separate the application “build” and “deployment” portions into their respective subsystems, as you may want separate control over when a build gets published, or maybe you don’t want to publish EVERY build; only some of them.

Hopefully at this point you are able to create a successful build, but we’re not done yet.

 Step 3 – Publish the build artifacts to the ClickOnce application’s destination

Now that we have the build artifacts safely stored, we can publish them to the ClickOnce application’s destination. With VSTS this is done by using the Release subsystem. So create a new Release Definition and setup an Environment for it. By default it adds a “Copy and Publish Build Artifacts” step to the release definition. When configuring and using this default step, I received the error:

[error]Copy and Publish Build Artifacts task is not supported within Release

So instead I removed that default step and added a simple “Copy Files” step, and then configured it to grab the files from the file share that the build published the artifacts to, and set the destination to where the ClickOnce application was publishing to when I would do a manual publish from within Visual Studio. Here is what that step looks like in VSTS:

You should be able to run this release definition and see that it is able to post a new version of your ClickOnce application. Hooray! I setup my releases to automatically publish on every build, but you can configure yours however you like.

If you make changes and commit them, the build should create new artifacts, and the release should be able to publish the new version. However, if you launch your ClickOnce application, you may be surprised that it doesn’t actually update to the latest version that you just committed and published. This is because we need one additional build step to actually update the ClickOnce version so it can detect that a new version was published. If you launch another build and publish them, you’ll see that the files being published are overwriting the previous files, instead of getting published to a new directory as they are supposed to be.

You may be wondering why we need another build step to update the ClickOnce version. Part of the build artifacts that are created are a .application manifest file, and a directory that contains the applications files, both of which have the ClickOnce version in them. Can’t we just modify the directory name and .application manifest file to increment the old version number? This was my initial thought, but the .application manifest file contains a cryptography hash to ensure that nobody has tampered with it, meaning that it needs to contain the proper version number at the time that it is generated.

Step 4 – One more build step to update the ClickOnce version

The ClickOnce version is defined in your project file (.csproj/.vbproj) (as the <ApplicationVersion> and <ApplicationRevision> xml elements), and is different from the assembly version that you would typically set to version your .dll/.exe files via the AssemblyInfo.cs file. Unless you manually updated the ClickOnce version in Visual Studio, it will still have its original version. When publishing the ClickOnce version in Visual Studio, Visual Studio automatically takes care of incrementing ClickOnce version’s Revision part. However, because we’re publishing from our build system now, we’ll need to come up with an alternative method. To help solve this problem, I have created the Set-ProjectFilesClickOnceVersion GitHub repository. This repository houses a single Set-ProjectFilesClickOnceVersion.ps1 PowerShell script that we will want to download and add into our build process.

In order for the build system to access this Set-ProjectFilesClickOnceVersion.ps1 script you will need to check it into source control. Also, you need to make sure you add this build step before the MSBuild step. Here is what this step looks like in VSTS:

The Script Filename points to the location of the Set-ProjectFilesClickOnceVersion.ps1 script in my repository. Also note that I set the Working Folder to the directory that contains the .csproj project file. This is necessary since we are dealing with relative paths, not absolute ones.

For the Arguments I provide the name of the project file that corresponds to my ClickOnce application, as well as the VSTS Build ID. The Build ID is a globally unique ID that gets auto-incremented with every build, and most (all?) build systems have this. The script will translate this Build ID into the Build and Revision version number parts to use for the ClickOnce version, to ensure that they are always increasing in value. So you should never need to manually modify the ClickOnce version, and every build should generate a version number greater than the last, allowing the ClickOnce application to properly detect when a newer version has been published.

Maybe you update your application’s version number on every build, and want your ClickOnce version to match that version. If this is the case, then use the –Version parameter instead of –BuildSystemsBuildId. The other alternative is to use the –IncrementProjectFilesRevision switch to simply increment the ClickOnce Revision that is already stored in the project file. If you use this switch, you need to be sure to check the project file back into source control so that the Revision is properly incremented again on subsequent builds. I like to avoid my build system checking files into source control wherever possible, so I have opted for the BuildSystemsBuildId parameter here.

The last argument I have included is the UpdateMinimumRequiredVersionToCurrentVersion parameter, and it is optional. If provided, this parameter will change the ClickOnce application’s Minimum Required Version to be the same as the new version, forcing the application to always update to the latest version when it detects that an update is available. I typically use this setting for all of my ClickOnce applications. If you still plan on publishing your ClickOnce applications from Visual Studio, but want this functionality, simply install the AutoUpdateProjectsMinimumRequiredClickOnceVersion NuGet Package.

That’s it! Now if you launch another build it should create new artifacts with a larger ClickOnce version number, and once published your application should update to the new version.

 

Bonus – Displaying the ClickOnce version in your application

The ClickOnce application version number may be different than your assembly version number, or perhaps you don’t update your product’s version number on every publish; it’s easy to forget. I typically like to use semantic versioning for my projects, which only involves the first 3 version parts. This leaves the last version part, the Revision number, available for me to set as I please. I will typically use the ClickOnce Revision in my application’s displayed version number. This makes it easy to tell which actual release a client has installed in the event that the product version was not updated between releases. The code to do it is fairly simple.

public Version ApplicationVersion = new Version(“1.11.2”);
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// If this is a ClickOnce deployment, append the ClickOnce Revision to the version number (as it gets updated with each publish).
if (ApplicationDeployment.IsNetworkDeployed)
ApplicationVersion = new Version(ApplicationVersion.Major, ApplicationVersion.Minor, ApplicationVersion.Build,
ApplicationDeployment.CurrentDeployment.CurrentVersion.Revision);
// Display the Version as part of the window title.
wndMainWindow.Title += ApplicationVersion;
}

Here I hard-code the product version that I want displayed in my application in a variable called ApplicationVersion. When the WPF application is launched, I obtain the ClickOnce Revision and append it onto the end of the version number. I then display the version in my application’s window title, but you might want to show it somewhere else, such as in an About window. If you want, you could even display both the full application version and full ClickOnce version.