Wednesday, November 2, 2016

Automated Builds in TFS - Part I - Web Deploy with Web Config Transformations and Publish Profiles


Getting started

In order to publish a .Net application using Microsoft Web Deploy, your solution will need a couple things.
1.       Config transform
2.      Publish profile
Config transforms can be applied to both web and app .config files, although by far the most common use case is with web.config files. Detailed steps follow…
STEP
ACTION
1.    
Configure Solution
Every project comes with a default set of configurations, Debug and Release. We will be leveraging the Release configuration. If for some reason your solution does not contain a Release configuration, perform the following:
Right click on the solution and choose Configuration Manager from the context menu. Alternately, you can click the drop down arrow in the active configuration and then select Configuration Manager. Click on the drop-down arrow for the Active solution configuration and select
Enter a name of Release and copy from an existing configuration if you choose. The step to copy from an existing configuration is not critical as the config transform we create next will take precendence anyway.

2.    
Create transform
Expand the project containing the config file you need to transform, right click on the web.config file and choose Add Config Transform. If this option happens to be greyed out, it means your project already contains transform files for each defined configuration.

3.    
Common Transforms
Transforms can be used to dynamically alter the contents of your xml config files at compile time. Because we will be leveraging the results of the transform as part of a parameterized release pipeline, the values injected into your config file will simply be tokens as placeholders and identifiers for the values to be set at deploy time. Transforms are performed based on both a transform and a locator attribute, although in some cases, the locator attribute can be omitted. Below is a simple example for tokenizing the compilation debug setting under the system.web node:
Original setting:
<system.web>
   <compilation debug="true">
   ...
   </compilation>
</system.web>

Transform:
<system.web>
   <compilation xdt:Transform="SetAttributes" debug="false" />
   ...
</system.web>

Result:
<system.web>
   <compilation debug="false">
   ...
   </compilation>
</system.web>
In this case, the transform being used is SetAttributes and the attribute being set is debug. Alternatively, you could use the following transform to achieve equivalent results.

Transform:
<system.web>
   <compilation xdt:Transform="RemoveAttributes(debug)" />
   ...
</system.web>

Result:
<system.web>
   <compilation>
   ...
</system.web>
In this case, the RemoveAttributes transform is used and the specific attribute to be removed from the node is debug

Another example is the customErrors node.
Original setting:
<customErrors defaultRedirect="GenericError.htm" mode="On" />

Transform:
<customErrors xdt:Transform="Replace"
              mode="RemoteOnly"
              defaultRedirect="~/UI/ApplicationException.aspx">
   <error statusCode="500" redirect="InternalError.aspx"/>
   <error statusCode="404" redirect="NotFound.aspx"/>
</customErrors>

Result:
<customErrors mode="RemoteOnly" defaultRedirect="~/UI/ApplicationException.aspx">
   <error statusCode="500" redirect="InternalError.aspx"/>
   <error statusCode="404" redirect="NotFound.aspx"/>
</customErrors>
In this case, the Replace transform is used and as you can see, it replaces the entire contents of the customErrors node.

The last example uses the Remove transform. I find this useful in a number of scenarios, but the one I find most pertinent for us is in the system.serviceModel section. Whenever you add a new service reference to your application for a WCF or BizTalk hosted service, the wizard will automatically generate a new binding definition for you, even if every setting in it is identical to one you already have. In this case, I use the Remove transform to eliminate all bindings that I don’t need as shown here:

Original setting:
<binding name="WSHttpBinding_ITwoWayAsync"
         closeTimeout="00:01:00"
         openTimeout="00:01:00"
         receiveTimeout="00:10:00" sendTimeout="00:01:00"
         ...  
</binding>
<binding name="WSHttpBinding_ITwoWayAsync1"
         closeTimeout="00:01:00"
         openTimeout="00:01:00"
         receiveTimeout="00:10:00" sendTimeout="00:01:00"
         ...  
</binding>

Transform:
<binding xdt:Locator="Condition(@name='WSHttpBinding_ITwoWayAsync1')"
         xdt:Transform="Remove" />

Result:
<binding name="WSHttpBinding_ITwoWayAsync"
         closeTimeout="00:01:00"
         openTimeout="00:01:00"
         receiveTimeout="00:10:00" sendTimeout="00:01:00"
         ...  
</binding>
Note the use of the Locator attribute to specify which element to remove. The locator attribute is scoped to the node containing the transform, so the actual XPath expression that it resolves to would be as follows:
configuration/.../wsHttpBinding/binding[@name='WSHttpBinding_ITwoWayAsync1']

In addition to the Locator attribute, there is also a Match and an XPath attribute.

For full details on XML transform syntax and futher examples, please refer to…

4.    
Publish profiles
Now that we have created a transform file, we need a publish profile to create the deployment package. To do this, right click on your main project in the Solution Explorer (probably the one that has your web.config file).  From the popup menu choose the "Publish…" option.
5.    
Profile
From the "Publish Web" window that pops up, click the "Select or import a publish profile" drop down and choose "". Alternatively, you can click on Custom


In the "New Profile" window, enter a name for the Profile.  For our purposes in the context of an automated release pipeline, you’ll just need one for ‘Release’. 

  
Click the "OK" button.  This should take you to the Connection screen.
6.    
Connection
Choose “Web Deploy Package” for the Publish method and fill out the remaining information on this screen.

A good convention to follow is to specify a “drop” folder to contain your deployment packages whenever you perform a local publish operation, it just keeps things cleaner. If your solution contains multiple “web” applications (UI, API, etc…) you will want another folder level to hold the deployment artifacts for each individual app as shown as well. With a single app, the extra level isn’t needed. Don’t worry about creating the folder structure, the publish tool will take care of that for you.


As you can see in the above screenshot, the value entered for the Site name field is merely a tokenized placeholder, not the real value. We’ll get into tokens in a little bit. Click Next.

7.    
Settings
If your application contains any database connection strings defined in the web.config, they will be listed on this page. Again, you can see where the values have been tokenized.
Click Next

8.    
Preview
This final screen simply allows you to verify your settings so far before you actually publish the application. Click Publish to create the deployment package.

If you browse the contents of the publish location, you will see something like the following.

9.    
SetParameters.xml file
In the output above, you can see a file called .SetParameters.xml. This file is generated for you by the web deploy package publishing wizard. Looking inside that file will show you something like the following snippet.

xml version="1.0" encoding="utf-8"?>
<parameters>
  <setParameter name="IIS Web Application Name" value="__SiteName__" />

Each parameter in the file is driven by one of two things. Either you specify a parameter in the publish profile, or you create a special file called Parameters.xml and add it to your project. Go ahead and do that now. For each parameter that you define in the Parameters.xml file, the web deploy publish wizard will add a corresponding entry in the .SetParameters.xml file. The syntax for the Parameters.xml file is shown next.
10.    
Parameters.xml file
As mentioned above, you can define additional parameters in the Parameters.xml file to be included when you publish your web application. These are most commonly things like appSettings key/value pairs as shown in the below snippet.

xml version="1.0" encoding="utf-8" ?>
<parameters>
  <parameter name="Environment" description="Environment indicator string (DEV|TEST|PREP|PROD)" defaultValue="__Environment__" tags="">
    <parameterEntry kind="XmlFile" scope="\\web.config$" match="/configuration/appSettings/add[@key='Environment']/@value" />
  </parameter>

It is not necessary for the parameter “name” and “defaultValue” attributes to be consistent as shown above, but it is a good convention to follow and avoids unnecessary confusion. The “defaultValue” attribute is critical as it will define the token that will be replaced with the actual value at deploy time and the tokenization task in TFS/Release Management must be able to find an exact match based on that token.

The “parameterEntry” node defines how your parameter can be correctly identified in your web.config file. Even though there are other valid options for the “kind” attribute, the only one we will concern ourselves with is “XmlFile” as that sets the provider for Web Deploy to use.

The “scope” attribute specifies a RegEx to identify the path to the file containing the parameters.

The  “match” parameter is the XPath expression to the specific value in the config file.
For more information on Parameters.xml, please refer to…




Next:
Automated Builds in TFS - Part II - Creating Build Definitions in TFS