The other day I saw a question posted on StackOverflow (link to question below in resources section) asking if it was possible to update web.config using MSDeploy. I actually used a technique where I updated a single file in one of my previous posts at How to take your web app offline during publishing but it wasn’t called out too much. In any case I’ll show you how you can update a single file (in this case web.config) using MSDeploy.

You can use the contentPath provider to facilitate updating a single file. Using contentPath you can sync either a single file or an entire folder. You can also use IIS app paths to resolve where the file/folder resides. For example if I have a web.config file in a local folder named “C:\Data\Personal\My Repo\sayed-samples\UpdateWebConfig” and I want to update my IIS site UpdateWebCfg running in the Default Web Site on my folder I would use the command shown below.

%msdeploy% -verb:sync -source:contentPath="C:\Data\Personal\My Repo\sayed-samples\UpdateWebConfig\web.config" -dest:contentPath="Default Web Site/UpdateWebCfg/web.config"

From the command above you can see that I set the source content path to the local file and the dest content path using the IIS path {SiteName}/{AppName}/{file-path}. In this case I am updating a site running in IIS on my local machine. In order to update one that is running on a remote machine you will have to add ComputerName and possibly some other values to the –dest argument.

You can view the latest sources for this sample at my github repo, link is below.

 

Hope that helps!

Sayed Ibrahim Hashimi – @SayedIHashimi

Resources:


Comment Section

Comments are closed.


I blogged in December about SlowCheetah which is a Visual Studio add in which can be used to transform app.config (or any XML file for that matter) in ways similar to web.config. A few days back we released an update to this adding with a few key items which are listed below.

  1. Support for ClickOnce
  2. Support for Setup projects
  3. Support for F# projects
  4. Support for a custom diff viewer
  5. Support for XP/Win server 2003
  6. Better error handling, logging

I’ve created a set of samples for SlowCheetah which I’ll use to describe these new features. Download links for the samples, as well as pointers to the latest versions are at the bottom of this page.

Support for ClickOnce

If you are building a windows client application (Win forms or WPF) then you can use ClickOnce to publish this application. When you publish a project using ClickOnce a setup.exe will be generated as well as an HTML page which can be served by a web server to enable users to download it. When this setup.exe is generated all of the files needed to run your application (including app.config) are placed inside of that executable. When you have a project using SlowCheetah and you publish using a specific configuration it will now publish the transformed app.config (as well as any other transformed files).

In order to demonstrate this I’ve created a sample WPF application which I will publish with ClickOnce (download link is at the bottom). This project has an app.config file as well as app.Debug.config and app.Release.config.

Below is what the original app.config file looks like.



  
    
    
    
  
  
  
    
      
  
  

And here is app.Debug.Config



  
  
    
    
    
  

  
    
  
  

ddd

As you can see I am transforming both the appSettings as well as the connection string. The application itself is pretty simple, it has a single page which shows the following values.

  • App settings
  • Connection strings
  • Content of app.config

When I run this application in Debug mode I will get the following result.

image

In previous versions of SlowCheetah when you published your application with ClickOnce the original version of app.config was getting published instead of the transformed one. This was actually a bug in the .targets files for ClickOnce, but we have now worked around it in the SlowCheetah .targets files. So let’s say that you have a ClickOnce application and you need to publish different versions which have config changes then you can now easily do that with SlowCheetah.

Support for Setup projects

Visual Studio 2010 also has Setup projects, which can be used to generate an MSI. When you create the MSI you can specify that it includes the outputs of a project in the solution. We had a similar story here in which the original app.config was being placed into the MSI instead of the transformed on in the output folder. We have now fixed that and if you have a Setup project which reference a project with SlowCheetah transforms then the transformed files will endup in the resulting MSI instead of the original one.

Support for F# projects

If you have F# project and tried SlowCheetah you probably noticed that the “Add Transforms” menu item didn’t show up for those project types. They now do.

image

After you invoke this command you will see a transform created for each configuration defined for the project. In other project types these transform appear as child items, but F# projects don’t support this in the same way as C#/VB projects so they will not be nested. So they will show up as the following.
image

I created a very simple F# application to demonstrate the behavior, below is the code.

// A simple application to create the config and show results in the console
let settingOne = System.Configuration.ConfigurationManager.AppSettings.["settingOne"];
let settingTwo = System.Configuration.ConfigurationManager.AppSettings.["settingTwo"];
let settingthree = System.Configuration.ConfigurationManager.AppSettings.["settingThree"];


printfn "settingOne: %s" settingOne
printfn "settingTwo: %s" settingTwo
printfn "settingThree: %s" settingthree

printfn " "
printfn "Press any key to close"
System.Console.ReadKey(true)

As you can see this app just reads a few config values from app.config and displays them on the console. Here is the original app.config file.



  
    
    
    
  

Here is app.Debug.config



  
    
    
    
    
    
  

There is a similar app.Release.config as well for this project.

When I run this in Debug mode below is the result

image

When I run this in Release mode below is the result.

image

As you can see F# project will now pickup up the transformed web.config file instead of the original one when running.

Support for a custom diff viewer

As you may know SlowCheetah has a Preview Transform functionality that allows you to quickly see the difference between the source file and the transformed one. For example in my Wpf.Transform project I have a app.config file and app.Debug.config when I select one of the transforms I can see this menu item.

image
When that menu item is invoked we open the Visual Studio diff viewer to show you the difference between these two files. We have received a number of requests to support different diff viewers and we have enabled this in the latest release.

After installing SlowCheetah you can go to Tools->Options and then pick SlowCheetah on the left hand side. You will see the following.

image

Here we have three settings which you can customize, they are outlined below.

Setting

Description

Preview Tool Command Line This is a string.Format string that should be used to construct the command to be executed. Here {0} will be replaced with the full path to the original file and {1} with the full path to the transformed file.
Preview Tool Executable Path This is the full path to the .exe which should be invoked during preview.
The default value for this is “C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\diffmerge.exe” which is the diff viewer that VS uses by default.
Run Preview Tool If this is set to False then a diff viewer will not be shown, instead the transformed file will just be opened.

If you want to use KDiff with SlowCheetah then you need it to invoke the following command:

“C:\Program Files (x86)\KDiff3\kdiff3.exe” {Path-to-original-file} {Path-to-transformed-file}

So in this case I just have to update the value for Preview Tool Executable Path to be C:\Program Files (x86)\KDiff3\kdiff3.exe. After that when I perform a transform I will see something like.

image

Support for XP/Win server 2003

After publishing the original version of SlowCheetah users started complaining that it didn’t work for either Windows XP or Windows Server 2003. This is because SlowCheetah writes file under the %LocalAppData% folder. What I didn’t realize was that XP and Win server 2003 don’t have this environment variable defined! Because of this I had to update the logic of where to write the files if that environment variable didn’t exist. Now everything works seamlessly.

Better error handling, logging

We had some issues in previous builds and they were difficult to diagnose because we didn’t have any logging support. We have now updated this and we should be able to diagnose user issues much easier.

 

Below are a list of resources and pointers, I hope that you guys like these updates and please do continue giving us feedback on this!

Resources

Sayed Ibrahim Hashimi @SayedIHashimi

Note: I work for Microsoft but this add in was not created nor is supported by Microsoft.


Comment Section

Comments are closed.


I received a customer email asking how they can take their web application/site offline for the entire duration that a publish is happening from Visual Studio. An easy way to take your site offline is to drop an app_offline.htm file in the sites root directory. For more info on that you can read ScottGu’s post, link in below in resources section. Unfortunately Web Deploy itself doesn’t support this Sad smile. If you want Web Deploy (aka MSDeploy) to natively support this feature please vote on it at http://aspnet.uservoice.com/forums/41199-general/suggestions/2499911-take-my-site-app-offline-during-publishing.

Since Web Deploy doesn’t support this it’s going to be a bit more difficult and it requires us to perform the following steps:

  1. Publish app_offline.htm
  2. Publish the app, and ensure that app_offline.htm is contained inside the payload being published
  3. Delete app_offline.htm

#1 will take the app offline before the publish process  begins.
#2 will ensure that when we publish that app_offline.htm is not deleted (and therefore keep the app offline)
#3 will delete the app_offline.htm and bring the site back online

Now that we know what needs to be done let’s look at the implementation. First for the easy part. Create a file in your Web Application Project (WAP) named app_offline-template.htm. This will be the file which will end up being the app_offline.htm file on your target server. If you leave it blank your users will get a generic message stating that the app is offline, but it would be better for you to place static HTML (no ASP.NET markup) inside of that file letting users know that the site will come back up and whatever other info you think is relevant to your users. When you add this file you should change the Build Action to None in the Properties grid. This will make sure that this file itself is not published/packaged. Since the file ends in .htm it will by default be published. See the image below.

image

Now for the hard part. For Web Application Projects we have a hook into the publish/package process which we refer to as “wpp.targets”. If you want to extend your publish/package process you can create a file named {ProjectName}.wpp.targets in the same folder as the project file itself. Here is the file which I created you can copy and paste the content into your wpp.targets file. I will explain the significant parts but wanted to post the entire file for your convince. Note: you can grab my latest version of this file from my github repo, the link is in the resource section below.



  
    
    
      $(MSDeployPath)msdeploy.exe
        
  

  
    
      InitalizeAppOffline;
    
  

  

  
  
    
      <_AoPubAppOfflineSourceProviderSetting Include="contentPath">
        $(MSBuildProjectDirectory)\app_offline-template.htm
        $(DeployEncryptKey)
        $(_MSDeploySourceWebServerAppHostConfigDirectory)
        $(_MSDeploySourceWebServerManifest)
        $(_MSDeploySourceWebServerDirectory)
      

      <_AoPubAppOfflineDestProviderSetting Include="contentPath">
        "$(DeployIisAppPath)/app_offline.htm"
        $(_PublishMsDeployServiceUrl)
        $(UserName)
        $(Password)
        $(DeployEncryptKey)
        False
        $(AuthType)
        $(_MSDeployDestinationWebServerAppHostConfigDirectory)
        $(_MSDeployDestinationWebServerManifest)
        $(_MSDeployDestinationWebServerDirectory)
      
    

    
  

  
  
  
    
    
      app_offline.htm
    

    
    
      filePath
      app_offline-template.htm
    
  

  
  
    
    
    
    

    
      <_AoDeleteAppOfflineDestProviderSetting Include="contentPath">
        $(DeployIisAppPath)/app_offline.htm
        $(_PublishMsDeployServiceUrl)
        $(UserName)
        $(Password)
        $(DeployEncryptKey)
        $(AuthType)
        $(_MSDeployDestinationWebServerAppHostConfigDirectory)
        $(_MSDeployDestinationWebServerManifest)
        $(_MSDeployDestinationWebServerDirectory)
      
    
    
    
    
      <_Cmd>"$(MSDeployExe)" -verb:delete -dest:contentPath="%(_AoDeleteAppOfflineDestProviderSetting.Path)"
      <_Cmd Condition=" '%(_AoDeleteAppOfflineDestProviderSetting.ComputerName)' != '' ">$(_Cmd),computerName="%(_AoDeleteAppOfflineDestProviderSetting.ComputerName)"
      <_Cmd Condition=" '%(_AoDeleteAppOfflineDestProviderSetting.UserName)' != '' ">$(_Cmd),username="%(_AoDeleteAppOfflineDestProviderSetting.UserName)"
      <_Cmd Condition=" '%(_AoDeleteAppOfflineDestProviderSetting.Password)' != ''">$(_Cmd),password=$(Password)
      <_Cmd Condition=" '%(_AoDeleteAppOfflineDestProviderSetting.AuthType)' != ''">$(_Cmd),authType="%(_AoDeleteAppOfflineDestProviderSetting.AuthType)"
    

    
    

#1 Publish app_offline.htm

The implementation for #1 is contained inside the target PublishAppOfflineToDest. The msdeploy.exe command that we need to get executed is.

msdeploy.exe
    -source:contentPath='C:\Data\Personal\My Repo\sayed-samples\AppOfflineDemo01\AppOfflineDemo01\app_offline-template.htm'
    -dest:contentPath='"Default Web Site/AppOfflineDemo/app_offline.htm"',UserName='sayedha',Password='password-here',ComputerName='computername-here',IncludeAcls='False',AuthType='NTLM' -verb:sync -enableRule:DoNotDeleteRule

In order to do this I will leverage the MSDeploy task. Inside of the PublishAppOfflineToDest target you can see how this is accomplished by creating an item for both the source and destination.

#2 Publish the app, and ensure that app_offline.htm is contained inside the payload being published

This part is accomplished by the fragment

  
  
  
    
    
      app_offline.htm
    

    
    
      filePath
      app_offline-template.htm
    
  

The item value for FilesForPackagingFromProject here will convert your app_offline-template.htm to app_offline.htm in the folder from where the publish will be processed. Also there is a condition on it so that it only happens during publish and not packaging. We do not want app_offline-template.htm to be in the package (but it’s not the end of the world if it does either).

The element for MsDeploySkiprules will make sure that app_offline-template.htm itself doesn’t get published. This may not be required but it shouldn’t hurt.

#3 Delete app_offline.htm

Now that our app is published we need to delete the app_offline.htm file from the dest web app. The msdeploy.exe command would be:

%msdeploy%
      -verb:delete
      -dest:contentPath="{IIS-Path}/app_offline.htm",computerName="...",username="...",password="..."

This is implemented inside of the DeleteAppOffline target. This target will automatically get executed after the publish because I have included the attribute AfterTargets=”MSDeployPublish”. In that target you can see that I am building up the msdeploy.exe command directly, it looks like the MSDeploy task doesn’t support the delete verb.

If you do try this out please let me know if you run into any issues. I am thinking to create a Nuget package from this so that you can just install that package. That would take a bit of work so please let me know if you are interested in that.

Resources

  1. The latest version of my AppOffline wpp.targets file.
  2. ScottGu’s blog on app_offline.htm

 

Sayed Ibrahim Hashimi @SayedIHashimi


Comment Section

Comments are closed.


Today I just saw a question posted on StackOverflow asking how to update an XML file using MSBuild during a CI build executed from Team City.

There is not correct single answer, there are several different ways that you can update an XML file during a build. Most notably:

  1. Use SlowCheetah to transform the files for you
  2. Use the TransformXml task directly
  3. Use the built in (MSBuild 4.0) XmlPoke task
  4. Use a third party task library

#1 Use SlowCheetah to transform the files for you

Before you start reading too far into this post let me go over option #3 first because I think it’s the easiest approach and the most easily maintained. You can download my SlowCheetah XML Transforms Visual Studio add in. Once you do this for your projects you will see a new menu command to transform a file on build (for web projects on package/publish). If you want to get this working from a CI server, its pretty easy see my post at http://sedodream.com/2011/12/12/SlowCheetahXMLTransformsFromACIServer.aspx.

#2 Use the TransformXml task directly

If you want a technique where you have a “main” XML file and you want to be able to contain transformations to that file inside of a separate XML file then you can use the TransformXml task directly. For more info see my previous blog post at http://sedodream.com/2010/11/18/XDTWebconfigTransformsInNonwebProjects.aspx

#3 Use the built in XmlPoke task

Sometimes it doesn’t make sense to create an XML file with transformations for each XML file. For example if you have an XML file and you want to modify a single value but to create 10 different files the XML transformation approach doesn’t scale well. In this case it might be easier to use the XmlPoke task. Note this does require MSBuild 4.0.

Below are the contents of sample.xml (came from the SO question).


  
    IsTestEnvironment
    True
    False
  
  
    HlrFtpPutDir
    C:\DevPath1
    False
  
  
    HlrFtpPutCopyDir
    C:\DevPath2
    False
  

So in this case we want to update the values of the value element. So the first thing that we need to do is to come up with the correct XPath for all the elements which we want to update. In this case we can use the following XPath expressions for each value element.

  • /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutDir']/value
  • /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutCopyDir']/value

I’m not going to go over what you need to do to figure out the correct XPath because that’s not the purpose of this post. There are a bunch of XPath related resources on the interwebs. In the resources section I have linked to the online XPath tester which I always use.

Now that we’ve got the required XPath expressions we need to construct our MSBuild elements to get everything updated. Here is the overall technique:

  1. Place all info for all XML updates into an item
  2. Use XmlPoke along with MSBuild batching to perform all the updates

For #2 if you are not that familiar with MSBuild batching then I would recommend buying my book or you can take a look at the resources I have online relating to batching (the link is below in resources section). Below you will find a simple MSBuild file that I created, UpdateXm01.proj.



  
    $(MSBuildProjectDirectory)\sample.xml
    $(MSBuildProjectDirectory)\result.xml
  

  
    
    
      /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutDir']/value
      H:\ReleasePath1
    
    
    
      /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutCopyDir']/value
      H:\ReleasePath2
    
  
  
  
    
    
    
    
  

The parts to pay close attention to is the XmlConfigUpdates item and the contents of the UpdateXml task itself. Regarding the XmlConfigUpdates, that name is arbitrary you can use whatever name you want, you can see that the Include value (which typically points to a file) is simply left at ConfigUpdates-SampleXml. The value for the Include attribute is not used here. I would place a unique value for the Include attribute for each file that you are updating. This just makes it easier for people to understand what that group of values is for, and you can use it later to batch updates. The XmlConfigUpdates item has these two metadata values:

  • XPath
    • This contains the XPath required to select the element which is going to be updated
  • NewValue
    • This contains the new value for the element which is going to be updated

Inside of the UpdateXml target you can see that we are using the XmlPoke task and passing the XPath as %(XmlConfigUpdate.XPath) and the value as %(XmlConfigUpdates.NewValue). Since we are using the %(…) syntax on an item this start MSBuild batching. Batching is where more than one operation is performed over a “batch” of values. In this case there are two unique batches (1 for each value in XmlConfigUpdates) so the XmlPoke task will be invoked two times. Batching can be confusing so make sure to read up on it if you are not familiar.

Now we can use msbuild.exe to start the process. The resulting XML file is:


  
    IsTestEnvironment
    True
    False
  
  
    HlrFtpPutDir
    H:\ReleasePath1
    False
  
  
    HlrFtpPutCopyDir
    H:\ReleasePath2
    False
  

So now we can see how easy it was to use the XmlPoke task. Let’s now take a look at how we can extend this example to manage updates to the same file for an additional environment.

How to manage updates to the same file for multiple different results

Since we’ve created an item which will keep all the needed XPath as well as the new values we have a bit more flexibility in managing multiple environments. In this scenario we have the same file that we want to write out, but we need to write out different values based on the target environment. Doing this is pretty easy. Take a look at the contents of UpdateXml02.proj below.



  
  
    $(MSBuildProjectDirectory)\sample.xml
    $(MSBuildProjectDirectory)\result.xml
  

  
    
    Env01
  
  
  
    
    
      /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutDir']/value
      H:\ReleasePath1
    
    
    
      /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutCopyDir']/value
      H:\ReleasePath2
    
  

  
    
    
      /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutDir']/value
      G:\SomeOtherPlace\ReleasePath1
    

    
      /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutCopyDir']/value
      G:\SomeOtherPlace\ReleasePath2
    
  
  
  
    
    
    
    
  

The differences are pretty simple, I introduced a new property, TargetEnvName which lets us know what the target environment is. (note: I just made up that property name, use whatever name you like). Also you can see that there are two ItemGroup elements containing different XmlConfigUpdate items. Each ItemGroup has a condition based on the value of TargetEnvName so only one of the two ItemGroup values will be used. Now we have a single MSBuild file that has the values for both environments. When building just pass in the property TargetEnvName, for example msbuild .\UpdateXml02.proj /p:TargetEnvName=Env02. When I executed this the resulting file contains:


  
    IsTestEnvironment
    True
    False
  
  
    HlrFtpPutDir
    G:\SomeOtherPlace\ReleasePath1
    False
  
  
    HlrFtpPutCopyDir
    G:\SomeOtherPlace\ReleasePath2
    False
  

You can see that the file has been updated with different paths in the value element.

#4 Use a third party task library

If you are not using MSBuild 4 then you will need to use a third party task library like the MSBuild Extension Pack (link in resources).

Hope that helps.

Resources

Sayed Ibrahim Hashimi @SayedIHashimi


Comment Section

Comments are closed.


When I discuss the web package features in Visual Studio one item that always comes up is that people want to be able to create a single Web Deploy package and then publish that to may different environments. One reason why they have a difficult time in doing this is because when a web package is created the web.config transforms are executed before the package is created and then only the transformed web.config file makes it into the package. Also Web Deploy itself doesn’t have support for web.config transforms. Since I’ve heard this soo many times I decided to take matters into my own hands and address this for existing Visual Studio 2010 scenarios. Here is what I (note that this is not a Microsoft sponsored project, it’s a personal project) have done.

I have create a new Nuget package, PackageWeb, which you can use to address this. In this blog post I will focus on the user experience and not discuss too many details about the implementation, but I will have later blog posts to discuss how this was implemented. Since this is a Nuget package you have to have Nuget installed. If you don’t go to nuget.org and click the huge Install Nuget button. Here are the steps to get you started:

  1. Install the PackageWeb Nuget package
  2. Create a web deployment package
  3. Notice there is a Publish-Interactive.ps1 file in the package folder
  4. Execute the Publish-Interactive.ps1 file from the package folder

#1 Install the PacakgeWeb Nuget package

To install the package just right-click on the web project in the Solution Explorer and select Manage Nuget Packages. In the dialog that pops up just search for PackageWeb and click on the install button.

SNAGHTML6c282a

After you do this a few files will be added to your project and the package creation process will be extended to include those files. Stay tuned to this blog for more details on this part.

#2 Create a web deployment package

For most who are interested they will already know how to do this, but if not don’t worry its simple. Right-click on the project in solution explorer and pick Build Deployment Package.

#3 Notice there is a Publish-Interactive.ps1 file in the package folder

When the package is built (by default it is written to the obj\{Configuration}\Package\ folder. In that folder you will now see a Publish-Interactive.ps1 file.

image

#3 Execute the Publish-Interactive.ps1 file from the package folder

Now that we have everything setup let’s start publishing! First let’s take a look at the project which I am publishing, here is a view of the relevant portions of the project.

SNAGHTML7bc0ff

In this image you can see that I have 4 web.config transforms (web.debug.config,web.release.config,web.Prod.config and web.Test.config). I didn’t add any new build configurations to create the Test and Prod files. I just create them and added them to the project. Now open a PowerShell prompt and cd to the package folder. From there execute the command .\Publish-Interactive.ps1. Once you start it you will see that a file copy progress indicator starts. I’m extracting the package to a temp folder. The first thing that the script will do is to prompt you for the web.config transform to be executed.

image

In the image above you can see that it detected all 4 web.config transforms. At this point you just type in the name of the transform which you want to apply. I will pick Release here for this demo. After that some magic will happen and you will start to be prompted for some values. It will prompt you for Web Deploy endpoint info and for any parameters which exist in the package.

image

The first 5 prompts will always be the same, those are the Web Deploy endpoint values. Notice the whatif here, if you want to simulate a publish then for whatif pass in the value true. This will cause Web Deploy to go into a simulation mode, and it will tell you everything that it would do, but no changes will be made.

After the first 5 values you will be prompted for all the parameters which exist in the package. These usually have default values (you can see default values printed in cyan) so if you want to go with the defaults just hit the enter key.

After you enter all the parameter values you will see the message shown below.

image

You may not want to enter all these values everytime so to help with that when you invoke Publish-Interactive.ps1 it will write a file, PublishConfiguration.ps1.readme, which has all the parameter values (except password, you have to fill that one in).

image

If you want that file to be picked up, just remove the .readme from the extension and the next time you invoke Publish-Interactive.ps1 it will pick up the PublishConfiguration.ps1 file and not annoy you!

Currently I’m listing this project as Alpha quality, I will be able to promote it to beta/RTM only with your help. Please download it and try it out. Send the feedback to me either here or you can create an issue on the project’s issue page.

Note: As I stated previously this is not a Microsoft sponsored project, it is a personal project.

Sayed Ibrahim Hashimi @SayedIHashimi

Note: I work for Microsoft, but these are my own opinions


Comment Section

Comments are closed.


<< Older Posts | Newer Posts >>