Tweaking the LightSwitch webdeploy package with a simple script.

Tweaking the LightSwitch webdeploy package with a simple script.

Introduction

Visual studio 2010 contains powerful features when it comes to tweaking the generated web deploy package with respect to the web.config file and the parameters involved. Basically, there is functionality for changing the shape of the web.config file (this is called web.config transformations) during build time and functionality for making environment specific (e.g. different values in staging and production) web.config values injected on deploy time. This last feature is called: web deploy parameterization. In you want to compare both techniques, read this post: http://vishaljoshi.blogspot.com/2010/06/parameterization-vs-webconfig.html

web.config transformations

Here you can find more information about web.config transformations: http://go.microsoft.com/fwlink/?LinkId=125889.  You often find “environment specific connection strings” as an example of a web.config transformation. In my view, that’s not the true purpose of a web.config transformation. You should better use web deploy parameterization for doing this. The point is that the web.config transformation happens at build time. So imagine you have 3 environments (staging, acceptance, production), you would have to create 3 packages where depending on the solution configuration (debug, release, etc. …) a dedicated web.config is generated. Very powerful, but there is no possibility to tweak afterwards the package since your connection string is hard coded in the web.config file. Well, what then the true purpose of the web.config file? The answer is: changing the shape of your web.config file. So, all types of changes which are not “environment value specific”. In most cases this boils down to removing sections in the web.config file which are only applicable in the development environment and which should be removed for security reasons.

In case you want to use web.config transformations in a LightSwitch project, I have some very bad new for you: that’s NOT possible.

Web Deploy Parameterization in a visual studio web application project

Here you can find an excellent introduction on web deploy parameterization: http://vishaljoshi.blogspot.com/2010/07/web-deploy-parameterization-in-action.html. I ‘m a big fan of web deploy. Ok, it has a steep learning curve and there is a very high level of abstraction involved, but it is so powerful.

In case web deploy (parameterization) is completely new for you, I recommend that your first read the post of Vishal Joshi. Based on that, you will understand that there is a difference between the declaration of parameters (which can be done by adding a parameters.xml file in your solution) and actually injection environment specific parameter values during deploy time (either via entering the values when you deploy manually from IIS, either via attaching a SetParameterValues.xml file to the msdeploy command when you deploy via de command prompt). In vishal’s post, all this is explained in the context of a normal web application project. Note also that the content of parameter file is not restricted to parameters which involve a transformation in the web.config file. Also IIS infrastructure related parameters (like setting the application pool) can be handled via the parameter.xml file.

Here again, this is not working in LightSwitch, but fortunately we still can use a lot of the functionality when we deploy via the command line. I will explain in the next paragraph how all this can be done.

Web Deploy Parameterization in a LightSwitch project via MsDeploy from the command line.

I’ll demonstrate the power of web deploy parameterization in the context of a LightSwitch project with following goals in mind.

  1. Although LightSwitch allows to deploy the database from IIS, I’m not using this because (because my Hosting provider doesn’t support this) and as a result I want to remove all database related things from my web deploy package. Neither I care about creating a security admin during deployment, I prefer to create this via a simple sql script, rather than installing all LightSwitch Server Prereqs (which is basically doing not more than installing the security admin !)
  2. I want to be able to set my _instrinsicData connection string during deploy time and I want to read the actual connection string value from a SetParameterValues.xml file rather than typing it over and over again.
  3. I want to the extend the standard set of web deploy parameters of a LightSwitch project with a new parameter. I use a dedicated security database (apart from the “application database”); as a result I need in my app also the connection string towards the security database.
So, we assume that the database has been deployed already and a security admin is already in place. If you have problems creating these yourself I have following suggestion: deploy ONCE your test application as you normally do and make sure it works (both database and security admin are installed). You can remove then the application from IIS but leave the database intact. By doing so, you have clean starting point for what’s coming in this blog post.
The above defined goals, should normally cover everything you want to do with a LightSwitch project when it comes to web deploy parameterization.  We’ll proceed as follows:
  1. As a starting point, I’ll explain first how you can simply deploy from the command line a vanilla Lightswitch project (without the above mentioned “tweaks”).
  2. Next, we want to replace the parameters.xml file which resides in the LightSwitch web deploy package (the .zip) with our own version.
  3. Finally, we want to inject our own (environment-specific) values during deployment to IIS.

How to deploy a vanilla LightSwitch project from the command line.

We make the following assumption here: we presume that you have full access as an administrator to your IIS server and that you execute everything in a command line box with administrator rights. Obviously, you can use webdeploy also remotely and even configure IIS in such a way that a non-admin can deploy packages. That’s all cool, but it involves a lot of things which are not making the subject of this post more clear.

Furthermore, you may use any version of web deploy (1.1 or above), and the LightSwitch Server Prereqs does not have to be installed on the server !

First make sure you have a LightSwitch project (which compiles nicely) and for which you want to generate a web deploy package.

Since we want to use an additional parameter during deployment (see goal 3), we need to slightly adapt the web.config file. Open the web.config file which can be found in the ServerGenerated project and add a second connection string. (_SecurityData)

  <connectionStrings>
    <add name="_IntrinsicData" connectionString="Data Source=|SqlExpressInstanceName|;AttachDbFilename=|ApplicationDatabasePath|;Integrated Security=True;Connect Timeout=30;User Instance=True;MultipleActiveResultSets=True" />
    <add name="_SecurityData" connectionString="Data Source=|SqlExpressInstanceName|;AttachDbFilename=|ApplicationDatabasePath|;Integrated Security=True;Connect Timeout=30;User Instance=True;MultipleActiveResultSets=True" />
  </connectionStrings>

The precise value of the connection string is completely irrelevant, because we’ll set the value during deploy time. The only thing that matters is that there is at least the <add> node with the attribues name and ConnectionString.

Make sure to unselect “IIS has LightSwith prereqs installed”. We don’t need these because we don’t generate an admin user during deployment.

 

Unselect IIS has LightSwitch server prereqs installed.

 

Obviously, we want to create a package on disk rather than publishing it directly from visual studio.

 

Select Create a package on disk during Publish

 

 That’s all on the visual studio side. So, click publish and locate the .zip file on disk because that’s what we need.

 Now, drop the following lines in a .cmd file and adjust the _sourcePackagePath to the path where your .zip is located.

SET _sourcePackagePath="D:VS2010tempApplication2Application2Publishapplication2.zip"

"C:Program FilesIISMicrosoft Web Deploymsdeploy.exe" -source:package=%_sourcePackagePath%  -dest:auto,IncludeAcls='False',AuthType='Basic' -verb:sync -allowUntrusted skip:ObjectName=dbFullSql

pause

 Open a command line prompt (and use run as administrator !), and just run the script. Note the skip dbFullSql verb. This avoids that a database is installed. We will remove this skip verb later, when we completely tweaked the parameter file.

Tweak the existing parameters.xlm of the original .zip package

We first need to replace the existing parameters.xml file with our own version. This is done by creating a new .zip based on the old one. The two .zip files are completely identical except for the parameter file.

So, first create in the same folder where your .cmd file is a new xlm file and call it exactly “declareparameters.xml”.

Give it following content:

<parameters>
  <parameter name="ApplicationDataConnectionString" defaultValue="No default value" tags="Hidden">
    <parameterEntry kind="XmlFile" scope="web.config" match="//connectionStrings/add[@name='_IntrinsicData']/@connectionString" />
  </parameter>
<parameter name="SecurityDataConnectionString" defaultValue="No default value" tags="Hidden">
    <parameterEntry kind="XmlFile" scope="web.config" match="//connectionStrings/add[@name='_SecurityData']/@connectionString" />
  </parameter>
  <parameter name="IisWebApplication" description="IIS Web Application content location" defaultValue="NO default" tags="IisApp">
    <parameterEntry kind="ProviderPath" scope="IisApp" match="^.*app.publish$" />
  </parameter>
</parameters>

Compare this file with the original parameters.xml file present in the .zip package:

<parameters>
  <parameter name="DatabaseAdministratorConnectionString" description="Connection used to create or update the application database." defaultValue="" tags="SQLConnectionString" />
  <parameter name="DatabaseServer" description="Name of the server that hosts the application database. Must match the server specified in the connection string." defaultValue="" tags="SQL" />
  <parameter name="DatabaseName" description="Name of the application database. Must match the database specified in the connection string." defaultValue="Application2" tags="SQL">
    <parameterEntry kind="SqlCommandVariable" scope="Application2.sql" match="DatabaseName" />
  </parameter>
  <parameter name="DatabaseUserName" description="User name that the application will use to connect to the application database." defaultValue="" tags="SQL">
    <parameterEntry kind="SqlCommandVariable" scope="Application2.sql" match="DatabaseUserName" />
  </parameter>
  <parameter name="DatabaseUserPassword" description="Password for the database user name." defaultValue="" tags="SQL,Password,New">
    <parameterEntry kind="SqlCommandVariable" scope="Application2.sql" match="DatabaseUserPassword" />
  </parameter>
  <parameter name="dbFullSql_Path" defaultValue="{DatabaseAdministratorConnectionString}" tags="Hidden">
    <parameterEntry kind="ProviderPath" scope="dbFullSql" match="Application2.sql" />
  </parameter>
  <parameter name="Update web.config connection string" defaultValue="Data Source={DatabaseServer};Database={DatabaseName};uid={DatabaseUserName};Pwd={DatabaseUserPassword};" tags="Hidden">
    <parameterEntry kind="XmlFile" scope="web.config" match="//connectionStrings/add[@name='_IntrinsicData']/@connectionString" />
  </parameter>
  <parameter name="Application2_IisWebApplication" description="IIS Web Application content location" defaultValue="Default Web Site/Application2" tags="IisApp">
    <parameterEntry kind="ProviderPath" scope="IisApp" match="^d:VS2010tempApplication2Application2BinDebugapp.publish$" />
  </parameter>
</parameters>

Note that everything (except the connection string) related to the database deployment has disappeared.  I renamed also the “Application2_IisWebApplication parameter to something more generic (by doing so, I can use the same script for all my lightswitch projects) and tweaked also the match attribute to something more generic. Basically, the match will be done now only based on

^.*app.publish$

rather than the full path which is too application specific. Also our second connection string is now a parameter!

Ok we can now update our original .cmd file as follows (clean first the complete file):

SET _sourcePackagePath="D:VS2010tempApplication2Application2Publishapplication2.zip"
SET _targetpackagePath="D:VS2010tempApplication2Application2Publishapplication2_ReadyToDeploy.zip"

"C:Program FilesIISMicrosoft Web Deploymsdeploy.exe" -verb:sync -source:package=%_sourcePackagePath% -dest:package=%_targetPackagePath% -declareParamFile="declareparameters.xml"

So, this is doing nothing more than throwing away the original parameters.xml file and replace it by declareParameters.xml. Currently, I didn’t find a more elegant way to do this. I admit, we have now 2 .zip files. Note that so far, nothing is deployed !

The last step is now to inject our own parameter values during deployment.

Injecting environment specific values during deployment.

In order to do this, we need a second .xml file for storing the environment specific values. So, create a new .xml file and call it SetParameterValues.xml and give it following content:

<?xml version="1.0" encoding="utf-8"?>
<parameters>
  <setParameter name="IisWebApplication" value="Default Web Site/Application2" />
  <setParameter name="ApplicationDataConnectionString" value="Data Source=.sqlexpress;Initial Catalog=Application2;Integrated security=true" />
 <setParameter name="SecurityDataConnectionString" value="this is my second connection string" />
</parameters>

As you see, the 3 parameter values are there. One for the IisWebApplication and the two connection strings. For the securityDataConnectionString, I just used a dummy value, but it should have of course the shape of the other connection string.

Finally, we have to adapt our .cmd file to do the actual deployment based on the parameter values in this xml file.

SET _sourcePackagePath="D:VS2010tempApplication2Application2Publishapplication2.zip"
SET _targetpackagePath="D:VS2010tempApplication2Application2Publishapplication2_ReadyToDeploy.zip"
"C:Program FilesIISMicrosoft Web Deploymsdeploy.exe" -verb:sync -source:package=%_sourcePackagePath% -dest:package=%_targetPackagePath% -declareParamFile="declareparameters.xml"
"C:Program FilesIISMicrosoft Web Deploymsdeploy.exe" -source:package=%_targetPackagePath%  -dest:auto,IncludeAcls='False',AuthType='Basic' -verb:sync -allowUntrusted -setParamFile:"SetParametervalues.xml"
pause

 

As you see, we just added the last line which is doing the actual deployment to IIS. It uses of course the .._ReadyToDeploy package rather than the original one and injects the SetParameterValues.xml file during deployment. Note also that the -skip verb for database deployment is gone now, this could be done because there are no database deployment params any longer.

Conclusion

LightSwitch has unfortunately not the same flexibility to tweak package generation as a normal web application project (but has so many other nice things !). Luckily, we can perfectly tweak things via command line deployment.

You can use these scripts “manually” or from a TFS build server, so that a new version can automatically be pushed towards a staging environment.

Hope this helps.

-paul.