Deploy click-once application on Azure Blob with Azure DevOps

It was a long time ago I blogged on how to publish a click-once application from a VSTS Build to Azure Blob, long time was passed, and lots of stuff changed. The whole process is now simpler, thanks to many dedicated tasks that avoid doing any manual work.

My new build always start with a  GitVersion custom tasks, that populates some environment variables with version numbers generated by GitVersion, this will allow me to simply add an MsBuild task in the build to publish click-once using automatic GitVersion versioning.

image

Figure 1: MsBuild task to publish click once application

You need to build the csproj that contains the project, using $(BuildConfiguration) to build in the right configuration and adding custom argument arguments (3) as the following list

/target:publish 
/p:ApplicationVersion=$(AssemblyVersion)  
/p:PublishURL=https://aaaaaaa.blob.core.windows.net/atest/logviewer/  
/p:UpdateEnabled=true  
/p:UpdateMode=Foreground 
/p:ProductName=Jarvis.LogViewer 

ApplicationVersion is set to variable $(AssemblyVersion) that was populated by GitVersion task, publish Url is a simple address of an azure blob storage, that contains a blob container called atest, with a  folder named logviewer that has public read access.

image

Figure 2: Public read access for a simple blob in my azure subscription

This will allows me to have a simple public blob, with a public address, that everyone can read, so everyone can download my application.

Azure blob can be given a public read access to everyone, making your click-once application available to everyone.

MsBuild task simply creates a app.publish subfolder that contains all the files that needs to be copied into the blob azure storage, the first step is a Copy file Task to copy them in the artifacts directory, as for the following picture.

image

Figure 3: All files of click-once publishing were copied in the artifacts staging directory.

Now everything is ready, just add a final task of type Azure File Copy and configure to copy everything from artifacts directory to the right subfolder of azure blob.

SNAGHTML18f3d81

Figure 4: Configuration of Azure Blob File to deploy click-once generated files.

Configuration is really simple, because thanks to a direct connection to Azure Subscription (3) we can simply connect Azure Dev Ops to an azure subscription so we can simply select storage account (4) blob name (5) and subfolder of the blob (6).

One of the advantage of the dedicated Azure File Copy Task, is that it can simply use one of  the Azure account linked to the Azure DevOps account, making super-simple to choose right blob where to upload click-once package.

Once you’ve finished configuration, you can simply run a build and your application will be automatically published.

image

Figure 5: Summary of the build with click once publishing.

Now you can simply point to the setup.exe file with the address of the blob and the game is done.

Gian Maria.

Deploying a click-once application with build vNext in Azure Blob Storage

Thanks to the new build system in TFS / VSTS, publishing an application with Click-once during a build is really simple.

Versioning the click-once app

The project is using Git and GitFlow, thus it comes natural to use GitVersion (as described in a previous post) to automatically add Semantic Versioning. In previous post I’ve demonstrated how to use this technique to publish Nuget Packages and nothing changes for Click Once applications.

Thanks to GitVersion and GitFlow I do not need to care about version management of my project.

The only difference is in how numbering is created for ClickOnce applications. Click-once does not support suffix like –unstable or –beta as Nuget can support for pre-release package. To have a good Major.Minor.Path.Suffix number the solution is splitting the prerelease tag (ex –unstable.3) to grab only the last number part. The result is: instead of having  version 1.2.5-unstable7 (perfectly valid for nuget but not for click-once) the build will use version 1.2.5.7, perfectly valid for ClickOnce.

The whole operation is simply managed by powershell script.

$splitted = $preReleaseTag.Split('.')
$preReleaseNum = $splitted[$splitted.Length - 1]
if ([string]::IsNullOrEmpty($preReleaseNum))
{
    $preReleaseNum = "0"
}
$clickOnceVersion = $version.MajorMinorPatch + "." + $preReleaseNum

Building click-once package

In MSDN official documentation there is a nice page that explain how to publish click-once application from command line with MSBuild. This is an exceptional starting point, because parameters can be easily passed to MSBuild during vNext build thus we need no customization to create click-once package.

Publishing with MSBuild command line interface makes easy to integrate in vNext Build.

First step is declaring a bunch of Build Properties in the build, to store all needed values during the build.

image

Figure 1: Variables to add for vNext build

The build needs three variables: $(ClickOnceVersion) will contain the version generated by with GitVersion, $(AzureBlobUrl) will contain the address of Azure Blob Url where the application will be published. Finally $(AzureBlobPrefix) is the prefix (folder) to categorize publishing, basically is the subfolder in the Azure Blob where the files will be copied.

Powershell script does some sting manipulation to detect whitch branch is about to be published. Thanks to GitVersion in $preReleaseTag variable the build can know the logical branch that is about to be compiled (ex: unstable.1). If it is empty the build is building the stable branch, if it is not null a simple split by dot charachter gives the logical branch: unstable, beta, etc. Using GitVersion preReleaseTag is better than checking branch name, because it better explain the role of current branch in GitFlow.

Knowing the logical branch (stable, unstable, beta) allows the build to publish different version of the product (stable, beta, unstable) on different subdirectory of azure blob.

This technique allows people to choose the distribution they want, similar to what Chrome does.

$preReleaseString = $splitted[0]
if ([string]::IsNullOrEmpty($preReleaseString))
{
    $preReleaseString = "stable"
    $ProductName = "MDbGui.Net"
}
else
{
    $ProductName = "MDbGui.Net-$preReleaseString"
}

Now all variables are ready, it is time to specify MSBuild Arguments to the task that build the solution and this is the only modification needed to trigger publishing the Click-once package. These are the command line options needed to publish the click-once package from MSBuild

/target:publish 
/p:ApplicationVersion=$(ClickOnceVersion) 
/p:PublishURL=$(AzureBlobUrl)/$(AzureBlobPrefix)/ 
/p:UpdateEnabled=true 
/p:UpdateMode=Foreground 
/p:ProductName=$(ProductName) 

Handling artifacts

Previous MSBuild command line produces all files needed for click-once publishing inside a subdirectory called app.publish inside the output directory. To have a clean and nice build, it is better to copy all relevant file the file inside staging directory.

This procedure was fully described in a previous post that deal with managing artifacts in a vNext Build. I always prefer including a script to copy all files that needs to be published as result for the build inside staging directory instead of relying on publishing every file inside bin folders.

image

Figure 2: Call a PowerShell script to copy all relevant files on Staging Directory

At the end of this step the all click-once related files are placed inside a subfolder of staging directory. The last step is moving those files on a web site or azure blob storage. ClickOnce apps needs only to be placed on accessible site, an instance of IIS is perfectly valid, but cost of Azure Blob Storage is really low and there are already a nice Build vNext task to copy file on it, so it would be stupid not to take advantage of it.

Move everything on Azure Blob

The easiest solution to copy all click-once related file on a public accessible location is creating a public Azure Blob storage, because you already have an action in build vNext to accomplish this task.

image

Figure 3: Azure Blob File copy task can copy files easily in target Blob Storage

To connect an Azure Account with Azure Endpoint please consult my previous post on the argument: Use the right Azure Service Endpoint in build vNext to avoid doing my mistake of trying to access a classic Blob Storage with Azure ARM.

As you can see from Figure 3 I specified the name of the storage acount, the container name and a prefix that is used to publish different branches for the application.

At the end of the build you have your application published on a url like this one: https://portalvhdsdlrhmlyhqzrk1.blob.core.windows.net/mdbgui/unstable/MDbGui.Net.application

Conclusion

All the scripts used to create this articles are in project https://github.com/stefanocastriotta/MDbGui.Net/tree/develop/Tools/BuildScripts you can download them and use in your project if you want. Thanks to the ability to change build number here is the result

image

Figure 4: you can easily find the version number in build number.

Gian Maria.

Error publishing Click-once moving from .NET 3.5 to 4.5:

I’ve a customer where we set up a TFS Build that automatically compile, obfuscates assembly and finally publish with click-once on an internal server. As a part of the process, a tool is used to move the published packages from the internal server to public server, to make it available to final customers. This tool uses mage.exe to change some properties of the package and then repack to publish to final server.

When the solution moved from .NET 3.5 to .NET 4.5, published application failed to install with this error:

ERROR SUMMARY
Below is a summary of the errors, details of these errors are listed later in the log.
* Activation of http://www.mycompany.net/test/uploadtest/uploadtest.application resulted in exception. Following failure messages were detected:
+ Application manifest has either a different computed hash than the one specified or no hash specified at all.
+ File, UploadTest.exe.manifest, has a different computed hash than specified in manifest.

We first thought that the culprit was the obfuscation process or something related to the build process, because publishing directly from Visual Studio generates a correct installer. Then we were able to replicate the error with a simple application with a single form so we were sure that something wrong happened during the build+publish process. Finally we determined that the problem generates when the published url for the package is changed during the build to point to final location (test server or production server).

Actually the process of changing publishing location was done with a direct call to mage.exe command line utility, and after some investigation we found that the encryption mechanism of Click-once was changed to SHA256RSA in .NET 4.5. Unfortunately mage.exe, does not automatically detect .NET version used by the application to apply the correct hash and uses SHA1 by default. If you want to use mage.exe to change some properties of a click-once application based on .NET 4.5 or greater version you must use -a command line option to choose sha256RSA algorithm to calculate manifest hash. The correct command line must contain –a sha256RSA option to generate a correct package.

In my opinion we have a couple of problem here that Microsoft could address.

1) The error message you got when you try to install published application, should states that the hash is computed with an incorrect algorithm, allowing you to better diagnose the error. Telling you that the hash is different from the one specified in the manifest is something misleading because it lead to incorrect assumption that something modifies the files after the hash is generated.

2) Mage.exe should automatically find version of Framework used by the package, and should give you a warning. If checking framework version is not simple, it would be better to always display a warning that tells the user “Warning: starting with .NET 4.5 you should use the option -a sha256RSA to resign manifest because we changed the algorithm used”.

This is a story that is true even for your application. If you change something so radical from one version to another of your application, always display clear and informative warning to the user, to avoid problems.

Gian Maria.

Mshtml and Click-Once the saga continues

I must admit that I really do not like deploying Click-Once application that uses MSHTML.dll, because I had several problem in the past and another one rigth now. Today I have to deploy (click once) a new version of an application that was migrated from VS 2008 to Visual Studio 2010, and I found a really strange problem. When I checked the ApplicationFiles to be sure that MSHTML.dll is included in the Click-once package, I did not see it listed from the available assemblies to be included in the deploy.

SNAGHTML271abe4

Figure 1: The Mshtml.dll is missing from the list of assemblies that can be included in the click-once setup.

I tried to recreate the reference, but it seems that I’m not be able anymore to include the MSHTML.dll in the click-once deploy after the project was migrated to VS2010. I’ve tried to deploy the application without it, but as I suspected, if you try to install in a pc without .NET SDK, the installation fails because the computer misses the mshtml.dll prerequisite.

The only solution I found to this problem is to edit the visual studio project file directly (it is a simple XML file), just locate the part where the project references the mshtml.dll file.

image

Figure 2: The Visual Studio project file indicates that mshtml.dll should Embed the interop types

It is strange, but it seems that Visual Studio decides to embed the interop, so you can simply remove the EmbedInteropTypes and reload the project again, now you should be able to include mshtml.dll again in the ApplicationFiles window.

image

Figure 3: you should now be able to include the mshtml.dll in the clickonce package.

Now I can deploy the application including mshtml.

alk.