Saturday, July 03, 2010

Web Deploy Parameterization in Action

A few weeks back I explained the key differences between Web.Config Transformation and Web Deploy (aka MSDeploy) Parameterization…  Before you read this post I recommend that you read Transformation vs. Parameterization post, it is tiny and will clear few fundamentals…

Automatic Parameterization

Before I dive in more let me clarify that if you are using VS 2010 then in common scenarios you may not even need to do custom parameterization, coz VS 2010 already parameterizes things like IIS Application Name, Application physical installation directory and connectionStrings… So when you actually create a web deploy .zip package you can easily build it once and deploy it several number of times by changing the parameters values…

Custom Parameterization

The typical scenarios where you will need to do custom parameterization is for scenarios like:

  • You have an appSetting  which needs to be changed by server admin at install time…
  • You are using a WCF Service and you want to change the end point at install time…
  • You have created re-usable web package for community apps and have bunch of questions to ask the users before they install the app (similar to all of the apps that you find in Application Gallery eg ScrewturnWiki, DNN, etc…)

Scenario

In today’s post I am going to use Parameterization with VS 2010 for changing an appSetting & WCF Service end point… Before we do that let us look at the web.config settings which we want to change at install time…

App Settings  - The Log Folder location here (“value” attribute) is something which I want my admin to be able to change at production install time to a shared location so that if my app is virtualized and put on a web farm then I have a common place to go and look at the log…

appSettings

WCF Endpoint Below is my WCF EndPoint URL (“address” attribute) which I would like my admin to change to the servers/sites on which the WCF Services will get deployed…

WCF endpoint

Install Time Experience

Installation of Web Packages can be done in couple of different ways:

We want to make sure that no matter which direction our IT Admin takes he/she gets an opportunity to provide values for the above two parameters…

Parameters.xml File format

As I explained in the earlier post Parameters.xml file can be passed to Web Deploy when your .zip Web package is being created and that allows Web Deploy to determine what items in your web should it mark as “changeable” at install time…  VS 2010 makes your life easier by allowing you to simply drop the Parameters file in the root of your web project and if a file with the name Parameters.xml is found in the root of your project it passes it to Web Deploy which then parameterizes your web…

The Parameters.xml file follows a specific format, the key attributes to note for each parameter that you declare within parameters.xml file are:

name  Required unique name to identify the parameter with e.g. “Service 1 endpoint address”

description – This text shows up in the UI of IIS Manager to help the user fill in the value so anything clarifying the parameter is cool…

defaultValue - Optionally you can specify a default value for a parameter so that while installing the package a user may know what kind of values are permissible…

scope – Regular Expression to determine what entities (files e.g. web.config, DBs, Web Deploy providers etc) does the parameter apply to

kind – There are several kinds of parameters but the key ones to remember are:

  • XmlFile – Use this for web.config, any settings XML files etc where you can make replacements using XPath
  • TextFile – Use this for non-XML file where you can make replacements by looking for fixed text or token within a file. e.g. you can put @@replaceme@@ in the a settings.ini file and during installation that text can be replaced

match -  This depends upon the parameter kind… For e.g. for XmlFile parameter the match expression would be a XPath… For TextFile the match expression could be @@replaceme@@ which you might pre-place in the file…

Declaring Parameters using Parameters.xml

Below is the content of Parameters.xml file that I dropped to the root of my MVC Application

<?xml version="1.0" encoding="utf-8" ?>
<
parameters
>

<
parameter name="Log Folder Location" description="Please provide a shared location where the app can write log files to" defaultValue="\\Logs\MvcApp\Logs\" tags=""
>
<
parameterEntry kind="XmlFile" scope="\\web.config$" match="/configuration/appSettings/add[@key='LogFolder']/@value"
/>
</
parameter
>

<
parameter name="WCF Service1 Endpoint Address" description="Please provide the Endpoint address for Service1 that this MVC App needs to call" defaultValue="http://localhost:61938/Service1.svc" tags=""
>
<
parameterEntry kind="XmlFile" scope="\\web.config$" match="//system.serviceModel/client/endpoint/@address"
/>
</
parameter
>

</
parameters
>


 



If you notice each of the Parameter above you can see that I am using XmlFile parameter kind with Xpath as the match syntax…  After adding this Parameters.xml file into my project my solution explorer looks as below:



parameters.xml



Now I can simply right click on my MvcApplication and hit “Build Deployment Package”…  The resultant .zip file should be created at obj\Debug\Package\MvcApplication1.zip…



Validating that Parameters really worked




  • Now to validate whether the parameters really worked you can very quickly open IIS Manager (Start –> Run –> InetMgr) and select your Default Web Site


  • You can now click the “Import Application” command on the right side bar and pass the newly created .zip package to it.



IIS Manager Import Application




  • On Hitting  next on the Import Application wizard you will be able to see the “Parameterization” screen which user will be able to pass values to the parameters.  Notice even our defaultValues provided in the Parameters.xml show up:



Parameters Import Application




  • I am now changing the value of these variables as shown below:



image




  • When I now go ahead and finish the wizard by clicking “Next” and go ahead and inspect the Web.Config file in the deployed location, I can see that the changed parameter values were applied to the web.config file seamlessly…



Parameterized Web.config




  • Also do note that in the process above “Parameters.xml” will also get deployed with your web application, in reality you do not need that file… To avoid that file from getting deployed you can go to its properties (select the file and hit F4) and set “Build Action” = None as shown below:



Build Action None



 



There is a ton more power of parameters.xml file that you can explore via Technet Article on Web Deploy Parameters or IIS.NET Articles about Parameters.xml but for scenario like ours the above information should hopefully suffice…



Thanks for reading :-)



-Vishal

51 comments:

Anonymous said...

Thanks for this intro. You realy give me the first tipps for going into this topic.

Pavel Chuchuva said...

Is there a way to disable Automatic Parameterization?

Vishal R Joshi said...

Hi Pavel, you can disable automatic parameterization by setting following two properties in your project file or by passing them via commandline...

AutoParameterizationWebConfigConnectionStrings - This is will stop VS from parameterizing connection Strings

DisableAllVSGeneratedMSDeployParameter - This will stop VS from parameterizing all parameters including IIS application name, physical path etc...

-Vishal

Pavel Chuchuva said...

Great! Thank you very much Vishal.

Anonymous said...

Is it possible to provide mutltiple default values based on what enviornment the package is deployed ?

Vishal R Joshi said...

You can set MSBuild property ProjectParametersXMLFile and provide different parameters.xml per build configuration... That way the correct Parameters.xml file will get picked up when you create a debug vs release build...

You can set this property by hand editing your project file or even passing it from command line...

Hope this helps
-Vishal

Anonymous said...

Is there any way to parameterize the actual deploy contents (not the web.config) after a deployment package is created? In other words, we would like our build process to create a single deployment package, from which we can install multiple customized customer applications. Simply put, the only thing that differentiates each customer application (aside from the web.config, which your article on parameterization handles perfectly) is a single resource folder (the folder contains a couple of customer specific images). I realize that our actual install/deploy application could bind the resource folder to a generic build, thus creating a customer specific deployment package before calling the web deploy api; however, I would like, if at all possible, to initially limit the scope of my deployment application, thus relying on our original build artifacts.

Vishal R Joshi said...

Hi Anonymous :-)
Re: Parameterization after package is constructed...

I do not think there is an easy way to do this although do drop me an email and I will connect you with my team and you can perhaps have an indept conversation...
After that do update the results of the conversation here for others to benefit from...

Thanks
Vishal

Anonymous said...

Great work Vishal.

I have a problem however when trying to parameterize a value in another xml formatted file (nhibernate.config).

I can confirm the Xpath is correct however msdeploy doesnt seem to attempt to modify the nhibernate.config, yet can work for the web.config file.

Any ideas why it cant work for other xmlfiles?

Vishal R Joshi said...

Q. Re: nhibernate.config
A. The other day I too faced a similar problem not with nHibernate.config but with something else... Can you possibly send the config file and the exact parameters you are using to Vishal.Joshi@Microsoft.com. We will try to see if we can reproduce the issue locally.
Thanks
Vishal

Vishal R Joshi said...

Q.RE: nhibernate.config
A. Owais on our team investigated this and as nhibernate.config contains namespace declaration at the top (I guess you have the same) then the XPath used should be namespace agnostic. For instance the XPath for connectionString would look something like

//*[local-name()='connectionStrings']/*[local-name()='add'][@name='csnameformycase']/@connectionString

Let us know if this helps...
Thanks
Vishal

Anonymous said...

Vishal i've emailed you a follow up.

Anonymous said...

It was indeed namespace agnostic.

I was able to parameterize the connection string in the nhibernate.config file by using the following xpath match.

//*[local-name()='hibernate-configuration']/*[local-name()='session-factory']/*[local-name()='property'][@name='connection.connection_string']/text()

Anonymous said...

Vishal,

We're doing automated deployments from VS 2010 using WMSvc to our testing environment, and are preparing to do our first deployments to production. We would like to use an existing package (from the most recent drop folder) to deploy to production, but want to utilize our release config transform. We do not want to re-run the build since there are changesets that have been checked in since the last deployment that are not ready for production. What's the best way to accomplish this?

We would rather not manage the parameters in two places (config transforms and parameters.xml) since we have already defined the necessary values in our web.release.config, and don't have a separate server admin role on our team that needs the ability to set config values on the fly at install time.

Any suggestions you can offer would be greatly appreciated. I would think this is a common deployment scenario, but am not seeing how it can be accomplished.

Thanks!
DanO

Vishal R Joshi said...

Q. We do not want to re-run the build since there are changesets that have been checked in since the last deployment that are not ready for production. What's the best way to accomplish this?
A. Dan, when you create a package to your existing drop location doesn't web.config transform & parameters.xml already work at that time. If yes then wouldn't the right web.config already exist?
Are you aware about SetParameters.xml file which gets generated. Usually you can run the package with a new SetParameters.xml and that would set the new value even if the package does not contain the correct value.
The scenario feels very specific if you would like to drop me an email at Vishal.Joshi@Microsoft.com or call me at 435-705-2031 then I will be happy to dive into details and we can come up with a design recommendation.
Thanks
Vishal

Anonymous said...

Thanks Vishal! I just sent you a follow up email.

Jim Geurts said...

No offense, but for simple cases it would be nice to just use web.config transforms for connectionStrings... now I have to use a separate file just to set that value. Is there a way to do that without using Parameters.xml?

btw, I'm publishing the project using WebDeploy via MSBuild. It's all automatic and I do not go through the IIS gui to import the package.

Thanks for your help,

Jim

Vishal R Joshi said...

Q: Jim asked: No offense, but for simple cases it would be nice to just use web.config transforms for connectionStrings... now I have to use a separate file just to set that value. Is there a way to do that without using Parameters.xml?
A. Jim, if you are publishing directly to the server via Web Deploy and MsBuild and you just want to set connectionString values you should be to do those using Web.Config transforms. You just need web.configuration.config and from team build just publish that configuration. The steps are explained at http://vishaljoshi.blogspot.com/2010/11/team-build-web-deployment-web-deploy-vs.html. In your case I do not think you need parameters.xml unless I am mis understanding.
thanks
Vishal

Jim Geurts said...

Thanks for the reply Vishal. I think I'm passing an incorrect config to my build step and that is causing the transforms to not be applied.

Jim Geurts said...

I've double checked things and I'm still getting connectionStrings with "$(ReplacableToken_Review-Web.config Connection String_0)" in them instead of the value in web.Review.config or just the default value in web.config. Any hints/suggestions?

Vishal R Joshi said...

Jim, are you trying to package your application as a .zip file or actually publishing to the web server directly? Can you share the msbuild commandline you are using? ideally you should have /t:MSDeployPublish and not /t:Package. The package is an intermediate destination which has the tokens in it. Publishing already replaces the token.
If you are intentionally creating the .zip package then there is a setParameters.xml file which is created with the package. Do you see your final values in it?
thanks
Vishal

Jim Geurts said...

Hi Vishal,

I'm packaging as a zip file, but rather publishing directly to the web server. My msbuild config is:

/P:Configuration=Review /P:DeployOnBuild=True /P:DeployTarget=MSDeployPublish /P:MsDeployServiceUrl=https://%env.TargetServer%/MsDeploy.axd /P:AllowUntrustedCertificate=True /P:MSDeployPublishMethod=WMSvc /P:CreatePackageOnPublish=True /P:UserName=deployUser /P:Password=myPassword

Hope that helps & thank you for your help!

Vishal R Joshi said...

Hi Jim,
thanks for the response. Are you suggesting that your deployed web.config contains the token or the one in .zip file contains the token? If it is later than it is expected as SetParameters.xml and deploy.cmd file will have the correct values. If the deployed web.config on the server has tokens then there is certainly something wrong.
Can you try removing /p:CreatePackageOnPublish and try if your deployed web.config has the tokens.
Thanks
Vishal

Jim Geurts said...

Yep, the deployed web.config contains the token. Removing CreatePackageOnPublish didn't help, unfortunately. Any other ideas? Otherwise, maybe we can work this out via email tomorrow and I could share my screen so you can see more details. jim@biacreations.com if you're interested

Vishal R Joshi said...

Hi Jim,
I am responding here so that in case anyone else lands up into anything similar then we would have a trail. But in anycase let us give it one more try.
As I understand you do not care about parameters and are not really using them in anyway. Rather you have all your transforms already set in web.configuration.config file. If that is the case then you can pass /p:DisableAllVSGeneratedMSDeployParameter=False from MsBuild, that should turn off all the tokenizations. I am sorry you have to use these, these properties were just meant to be backups in case something did not work out of the box.
Btw, if you get a chance can you try your scenario with File--> New Project instead of your existing project, I have not seen this issue before on my machine so am still curious what might be going wrong.
Thanks
Vishal

Jim Geurts said...

Thanks for your help Vishal. Adding that flag still didn't fix the problem. I'm going to recreate the solution/project files and see if that helps things. This was an mvc2 project upgraded to mvc3. I'll let you know if the new solution has the same issues.

Jim Geurts said...

Good news, sort of :) Recreating the solution and project files solved my mystery. I no longer get tokens showing up in the deployed web.config. Vishal, thank you for your help with this and if you would like a copy of the files for analysis, please let me know.

Vishal R Joshi said...

Hey Jim,
this is great to hear. Yes please do send me the project at Vishal.Joshi@Microsoft.com and we will take a look.
thx
Vishal

Peter Gfader said...

Thanks for this!!

Had big problems to configure log4net config settings in web.config
But adding a parameters.xml to the project works as expected!!!!

THANKS heaps

Pavel Husakouski said...

Is there a way to fix the order of parameters ? I.e. I'd like to have "host1", "port1","suffix1", "host2" and etc. But VS breaks the order and it becomes "host1" "suffix1" "suffix2" ... and in the end "host2"

Vishal R Joshi said...

Hi Mad,
Yes the priority of the parameters generated can be controlled. It is defined by following msbuild properties which can be changed in your project file.

The default setting is
***************************************************************
Global setting on MSDeployParameterPriority to change the Parameter order show up in inetmgr UI
The lower the number, the earlier it show up in the Inetmgr UI (-100, -80, -70, ....60, 100)
Default priority is 0. Any integer number is allowed.
***************************************************************
&ltPropertyGroup&gt
&ltVsIisAppParametersPriority Condition="'$(VsIisAppParametersPriority)'==''"&gt-100&lt/VsIisAppParametersPriority&gt
&ltVsContentPathParametersPriority Condition="'$(VsContentPathParametersPriority)'==''"&gt-80&lt/VsContentPathParametersPriority&gt
&ltVsDestinationVDirParametersPriority Condition="'$(VsDestinationVDirParametersPriority)'==''"&gt-70&lt/VsDestinationVDirParametersPriority&gt
&ltVsSetAclPriority Condition="'$(VsSetAclPriority)'==''"&gt-60&lt/VsSetAclPriority&gt
&ltUserParametersFileParametersPriority Condition="'$(UserParametersFileParametersPriority)'==''"&gt-50&lt/UserParametersFileParametersPriority&gt
&ltUserWebConfigParametersPriority Condition="'$(UserWebConfigParametersPriority)'==''"&gt-40&lt/UserWebConfigParametersPriority&gt
&ltVsSQLDatabaseScriptParametersPriority Condition="'$(VsSQLDatabaseScriptParametersPriority)'==''"&gt60&lt/VsSQLDatabaseScriptParametersPriority&gt
&ltVsWebConfigAutoCsParametersPriority Condition="'$(VsWebConfigAutoCsParametersPriority)'==''"&gt100&lt/VsWebConfigAutoCsParametersPriority&gt
&lt/PropertyGroup&gt

There is an Item collection called MsDeployDeclareParameters which when outputed can give you the priorities which is taken by default.

If you have more questions do not hesitate to write back at Vishal.Joshi@Microsoft.com

Btw, sorry for the delay I was out of office for a while.
Thx
Vishal

Anonymous said...

Hi Vishal,

I have been struggling to get this working for settings in the web.config file that are contained in the APPNAME.My.Settings section of the web.config file.

I have noticed that all the examples given are modifying an attribute, rather than an element (which it would need to do in this case), and wondered if this is a limitation of this functionality.

I believe my xpath syntax to be correct (match="/configuration/applicationSettings/MYAPP.My.MySettings/setting[@name='MYSETTING']/value", where MYAPP and MYSETTING correctly match the values in the file).

Thanks in advance for any help,

Mark Davies

Vishal R Joshi said...

Hey Mark,
There was a missing quote in my previous comment. Fixing the typo and resending.

Below is a note from Owais from our team, hope that helps :-)
It might be because the file is using namespaces. You can check at the top of the file if that is the case.
In case it is then change the xpath to be namespace agnostic.
//*[local-name()='setting’][@name='MYSETTING']/*[local-name()='value']

This xpath should work regardless of whether a namespace is being used. Make sure the case in the xpath matches the case in the file, since this is case senstive.
Thanks
Vishal

Anonymous said...

Vishal,

Thanks very much for your response.

There was no namespace in my file, but using the ns agnostic syntax, with the addition of '/text()' I have now got this to match correctly, as shown here;
match="//*[local-name()='setting'][@name='MYSETTING']/*[local-name()='value']/text()"

It seems that it was my incomplete knowledge of xpath syntax that caused my problems, so apologies for moving away from the original topic of this article.

Thank you for your help,

Mark

Nickolas said...

Great post, it has helped me a lot to streamline our release procedures.

Is there a way to declare some kind of validation on the input fields (regular expression)? Or is it possible to declare a fixed set of values that can be entered in the field. For example, if you have an appsetting "LogMethod", then a combobox appears for this field (rather than a textfield) with the values Information, Warning and Error.

Owais Shaikh said...

You can use validation to specify rules for the accepted values for any given parameter. For e.g if you want an enumeration then you can use:

Other validations are regularexpression, allowempty and boolean.

Vishal R Joshi said...

Owais meant to add but it got stipped due to HTML validation of the blog engine :-( Here is his sample.

&lt parameterValidation type="Enumeration" validationString="Information,Warning,Error" /&gt

just someone said...

I was using both web transforms and parameterization.
I planned on using web transforms for testing and local deployment, and I parameterization for packaging.

But I've finally figured out that if I put in a Parameters.xml file, and used Publish... the Parameters.xml values are the ones transmitted to the server.

I was thinking that Parameters.xml was only for the packaging, but
looks like it's applied when a package is sent.

Workaround: I'm using Parameters.Debug.xml
which is an empty files.

Amol Navare said...

Vishal,

We are using config transforms extensively. However we are facing 1 issue.
We are using SIT (Test) package for deployment in test environment and it has web.config generated by a config transform. Now for moving to UAT we will use signed off SIT package, but config file needs to be changed. And we dont want to queue one more build for UAT config.
Wa are exploring different ways of doing this using single build. Parameterization doesn't look to be working in this case.

Your directions will greatly help and would be appreciated very much.

Thanks,
Amol Navare

Anonymous said...

Hi Vishal,

Is it possible to REMOVE a section in web.config, rather than updating values via the parameters mechanism?
Thanks in advance.
paul van bladel

Anonymous said...

Vishal;
Is there a way to specify an Application Pool name and version (2 or 4)and if it is not there, to automatically create one with the desired parameters?

jinkskgpt@aol.com

Ambionix said...

I noticed that when publishing the parameterValidation node from the parameters.xml is lost in the deployment package. Any idea when this will be fixed?

Vishal R Joshi said...

Hi Ambionix,
Are you creating a package or installing the package? What are the steps you are following?
Thanks
Vishal

Anonymous said...

Hello Vishal,

I have question similar to paul van bladel. Is it possible to remove a XmlNode, rather than updating the values via parameters.xml?

In our case we're creating deployment package after successful integration tests and we're using same package to deploy to all environments. We change environment specific properties at deployment time by manipulating SetParameters.xml.

Gurmit Teotia

Vishal R Joshi said...

Hi Gurmit,
You will have to write a custom transform to accomplish this. You can read more on Sayed's blog at http://sedodream.com/2010/09/09/ExtendingXMLWebconfigConfigTransformation.aspx
Btw, if you get to writing your custom transform can you put it in some open source location so that the rest of the community can benefit from it as well.
Thanks
Vishal

Anonymous said...

Thanks Vishal. I'm behind it now.
Sure I'll put the link here for others to use.

Thanks,
Gurmit

Unknown said...

Can this be done with a console project? Console projects do not have the "Build deployment package" option

Stoffelche said...

Thanks for this interesting post. I have one question though:
Is it possible somehow, to keep the values of the deployment parameters across an update installation.
As it is now, when an update needs to be run, every parameter has to be entered again, the default values being the ones from the paramters.xml used when generatiing the deployment package.
It would be much easier, when the default values would be the ones entered during the previous installation. Is this possible somehow? There is the same question at http://stackoverflow.com/questions/16450553 but unfortunately it has not been answered.
Thanks in advance, Stefan

Sandip said...

hi Vishal,

I am trying to replace only server name and DB name from connection string and I dont want to display all connectionstring in IIS UI. How can I achieve the same.

Please help me.

Anonymous said...

Hi

I have an app setting



in web.config

and

in SetParameters.xml
when I executed the deploy.cmd the app setting is not getting updated in web.config.
Could you please let me know the missing pieces (asap).

Anonymous said...

Hi
I have parameters.xml in my web project. When I publish the project, Setparameters.xml is getting generated as per the parameters.xml.
The problem is, I used TFS build to generate package (using the parameters "DeployOnBuild=true;DeployTarget=MSDeployPublish;CreatePackageOnPublish=True;MSDeployPublishMethod=RemoteAgent;MSDeployServiceUrl=http://{myserver}/msdeployagentservice;UserName={username};Password={password};DefaultPackageFileName=PKG.zip"). The parameters.xml is not the same parameters.xml that I included in the project location, and SetParameters.xml is generating from that parameters.xml. Do you have any idea why TFS build is unable to pick the parameters.xml that I have included in project folder.