Introduction
ClickOnce is a very flexible tool when developing a customer installation. One challenge I worked with was to have one set of publish manifest files that can be source controlled but you want to reuse that manifest to deploy into various application life-cycle environments such as Test, QA, and, finally, PROD. This would be a good example where a build server does not exist and/or the development of applications is very linear. In each environment, such as Test, QA, Prod, specific application settings can be different based on that environment. In the attached command line project I created for this article, I am demonstrating how you can update the manifest files and the setup EXE file within a single ClickOnce published set of files to work within different environment endpoints (i.e., Test, QA, PROD environments).
Background
It is well understood that an IIS service can accommodate multiple IP (NIC) along with multiple ports per IP. This is one way to have testing/QA/prod environments for an application for a single ClickOnce publish for an application. However, if resources are limited and cost is a factor, there is a way to use one IP with one port by dynamically updating the ClickOnce published files. In this way, you can also source control those files with all the necessary dependencies but be able to update the hashed and setup EXE to point to acknowledge a new published location. This article provides an example of how to complete this task.
Completing this article provided experience and more understanding of how a ClickOnce publish works. It also made me realize other possibilities for developing a ClickOnce deployment, including overriding some of the capabilities.
Using the Code
The attached sample code uses a command project built in Visual Studio 2015 and can also be found on GitHub (https://github.com/randykroeger/SandBox). Using a command project provides a great way to demonstrate how this power shell code updates the application’s publish manifest files and hence pointing a deployment to a different endpoint. The solution contains a readme file that provides help on setting up IIS and also some trouble-shooting tips I worked through when building this sample project.
The Project
This section explains the attached project. It is recommended to download the project and reference it as you review this article. With the project at hand, I decided not to include code samples from the project in this article. It seemed easier to review the code as you’re reading the article.
Points of the Project
- This sample project is a simple command project. I have added a common reference so as to provide an example how references are handled when updating the ClickOnce manifest.
- The project structure adds a deployment folder to the project. I have added a deployment folder so that changes for an application can be source controlled along with all appropriate binary references, and so that the person that executes deployments, can easily find the deployment. Where a build server is used, this would be configured differently. Seldom would you source control the published files.
- Under the deployment folder, I created a Publish folder and within that folder, a bin folder was created. The bin folder is used as the publishing location for the application. When it is time to publish a new ClickOnce deployment, the new binaries along with the manifest files are published to the bin folder (i.e., right click project->publish).
- Project publish settings: With the application publish files being created within the bin folder, this leaves the Publish folder open for other dependencies not directly related to the publish files. This would be a good use for build events.
Setting up the Publish parameters (step by step):
- Right click on the project, and select “Publish” (Note: You can access publish via the project properties as well).
- You will notice that the publish location is set as a relative path “.\Deployment\Publish\bin\”.
- Click Next.
- For the website, you will notice that the path is: ”http://dell-dev64/Test/”. You will need to change that value to your application endpoint. This is the same endpoint you will use to update the “
$deploymentUrlFormat
” variable. This is further explained when I review the power shell logic below, under “Points of Interest”. For the most part, it really does not matter if you do not change this value within the project’s publish properties. As long as you update the “$deploymentUrlFormat
” variable with your endpoint within the power shell script, that is all that matters. You can make the power shell script read the project property to get that endpoint. That way, it is easily changed by a developer and seamless. You have two other options for your deployment, this article does not cover those options. - Click Next.
- I made the application only available online. It really does not matter for this example which you choose.
- Click Next or Finish. A new set of published files are created.
- The Mage utility is used to complete modifications to the manifest deployment files. Please do review this utility. You will see that this project’s power shell can be modified in different ways to accomplish the same result (https://docs.microsoft.com/en-us/dotnet/framework/tools/mage-exe-manifest-generation-and-editing-tool).
- Execute.ps1. This file is located in the deployment folder. This deployment folder can be copied to any server and executed to update the ClickOnce publish for an application (i.e., this console app). The top portion of this power shell script has all supporting functions. I have enclosed these functions with a comment - “
Region
” and “EndRegion
”. Below this section is the entry point of the script.
Points of Interest
The focal point of this article is the power shell script called Execute.ps1 located within the attached project. This is where the magic happens.
Before you can start testing, you will need to open the Execute.ps1 script and update the hardcoded variable “$deploymentUrlFormat
” with the endpoint where your virtual directories for each environment are located (virtual paths) as described in the publish setup above. Notice that the “$deploymentUrlFormat
” is a format string with the 0
index. The index is the environment being selected from the power shell prompt. This will become clear as you review the steps within the scripts.
Line from power shell script to update: $deploymentUrlFormat
= "http://dell-dev64/Test/{0}"
Script Review: For ease of reference, I have also labeled the comments in the Execute.ps1 power shell script to description below.
- Query for what environment this publish script will be updated and deployed for publication. This value is used in step two.
- Sets the
$deploymentUrl
variable using the $deploymentUrlFormat
and the environment value entered in “Step one”.
Note: Ensure that you have your virtual directories setup accordingly. In this example, we are using Test, QA, PROD. So, for example, locally, I would have http://dell-dev64/Test/Test, http://dell-dev64/Test/QA, and http://dell-dev64/Test/Prod setup, with http://dell-dev64/Test being setup as an application and Test, QA, Prod are virtual directories. - Query the physical path that is related to the endpoint above.
- Remove previous publish manifest files, if existing, and copy from the deployment path. NOTE: The deployment path would be wherever the Deployment folder (as shown in the illustration above within the project) was copied locally on the IIS server. Copying the deployment folder locally removes any network copy or security user security constraints.
- Describes updating the ClickOnce manifest and setup to point to the new endpoint:
- Variable setup. For an easy fast demo, I have hardcoded the names of the expected manifest files. Notice that the “
App.Config.{0}
” is a format string. The index 0
is for Test, QA, PROD. Within the deployment folder, you will see the three files predefined. They will be used to copy and rename within the coming steps. - Creating the file pointers. This is where we create the full required paths of all manifests that will be updated. You will note the variable “
$destBinaryLocation
”. I am grabbing the top value from the collection of paths found in “$applicationFilePaths
”. Originally, I was allowing each publish update the folder name to a new version. That became problematic if I wanted to commit changes to source control. So, I kept the folder name using version 1.0.0.0. This way, source control could track changes. If later I changed the method of resolving the publish path, then the logic where we take the top path within the “$applicationFilePaths
” collection should still work. It will depend on your implementation. - Validation step. As with any application, a script should follow the same principle of validating all variables constructed from user input.
- At this point, we are ready to make the updates. First item is to change the endpoint within the setup.exe that was created by the application publish. This setup executable is the downloadable install created by the project for publishing along with the manifest file. There are parameters associated with this executable where you can change what was added initially in the publishing.
- In this step, the hash entry of the manifest file is being updated. The purpose for this update is due to the changes we made to the configuration files due to endpoint changes and any other settings change. I have enclosed the code for updating the manifest entries into a function. I am also making a copy of the deploy files, removing the “.deploy”. I had found when I do that, I am not seeing the MSB3113 errors. It seemed to satisfy the execution of the Mage utility.
- Clean up the temporary files (i.e., the files without the .deploy extension).
- Update the deployment manifest with the hash value of the application manifest file. Necessary since the application manifest file has changed.
- Update the certificate on the deployment manifest using the Mage utility.
- Copy the deployment manifest back to the binary location to update that copy.
That is basically all that needs to be completed for this example. Once the basics can be understood, you can build out other similar deployments. I have included some links that helped me to understand those basics more as well.
Other Related Articles
History
- 31st December, 2018: Initial version