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.
- 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 !)
- 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.
- 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.
- 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”).
- Next, we want to replace the parameters.xml file which resides in the LightSwitch web deploy package (the .zip) with our own version.
- 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.
Obviously, we want to create a package on disk rather than publishing it directly from visual studio.
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:\VS2010\temp\Application2\Application2\Publish\application2.zip" "C:\Program Files\IIS\Microsoft Web Deploy\msdeploy.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:\\VS2010\\temp\\Application2\\Application2\\Bin\\Debug\\app\.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:\VS2010\temp\Application2\Application2\Publish\application2.zip" SET _targetpackagePath="D:\VS2010\temp\Application2\Application2\Publish\application2_ReadyToDeploy.zip" "C:\Program Files\IIS\Microsoft Web Deploy\msdeploy.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:\VS2010\temp\Application2\Application2\Publish\application2.zip" SET _targetpackagePath="D:\VS2010\temp\Application2\Application2\Publish\application2_ReadyToDeploy.zip" "C:\Program Files\IIS\Microsoft Web Deploy\msdeploy.exe" -verb:sync -source:package=%_sourcePackagePath% -dest:package=%_targetPackagePath% -declareParamFile="declareparameters.xml" "C:\Program Files\IIS\Microsoft Web Deploy\msdeploy.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.



Brillient article – thanks for sharing Paul!
Thanks a lot Tim !
Keep up the good work Paul; there is a lot of extremely useful content in these posts for people like me who are trying to get up to speed with LightSwitch in a serious enterprise environment.
Thanks Nigel.
Thanks for the share! Very useful info!
Hi Paul. Your article is extremely useful to me since it showed me how to deploy using msdeploy.exe. You are saying that you “prefer to create this via a simple sql script”. Could you give me an example of such a script? I have deployed the application, setup the databases but the security tables are empty! I’m using windows authentication.
Thanks!
Hi Kostas,
Here is my quick and dirty version of the script to create the security admin.
Note, that I’m using password format = clear, otherwise I need to calculate the hash over the password.
Drop me a line in case you need a way to encrypt the password, I have a reference to the algorithm somewhere.
DECLARE @ApplicationNameParam nvarchar(256)
SET @ApplicationNameParam = ‘MyApp xyz’
DECLARE @UserNameParam nvarchar(256)
DECLARE @UserFullNameParam nvarchar(256)
DECLARE @ProfilePropertyNameParam nvarchar(256)
DECLARE @RoleParam nvarchar(256)
DECLARE @UserId uniqueidentifier
DECLARE @currentDate DateTime
DECLARE @REturn_value int
SET @RoleParam = ‘MyRole’
SET @UserFullNameParam = ‘the admins fullname’
SET @UserNameParam = ‘admins name’
SET @ProfilePropertyNameParam = ‘FullName:S:0:’ +LTRIM( STR(LEN(@UserFullNameParam)))
SET @currentDate = GetDate()
EXEC @return_value = [dbo].[aspnet_Roles_CreateRole]
@ApplicationName = @ApplicationNameParam,@RoleName = @RoleParam
EXEC @return_value = [dbo].[aspnet_Membership_CreateUser]
@ApplicationName = @ApplicationNameParam,@UserName = @UserNameParam, @Password = N’mypassword’,@PasswordSalt = N’.',@Email = N’.',@PasswordQuestion = NULL,@PasswordAnswer = N’.',@IsApproved = True,@CurrentTimeUtc = @currentDate, @CreateDate = @currentDate, @UniqueEmail = 0,@PasswordFormat = 0, @UserId = @UserId OUTPUT
SELECT @UserId as N’@UserId’
EXEC @return_value = [dbo].[aspnet_Profile_SetProperties]
@ApplicationName = @ApplicationNameParam,@PropertyNames = @ProfilePropertyNameParam, @PropertyValuesString = @UserFullNameParam, @PropertyValuesBinary=0x, @UserName = @UserNameParam, @IsUserAnonymous = False, @CurrentTimeUtc = @currentDate
EXEC @return_value = [dbo].[aspnet_UsersInRoles_AddUsersToRoles]
@ApplicationName = @ApplicationNameParam, @UserNames = @UserNameParam, @RoleNames = @RoleParam,@CurrentTimeUtc = @currentDate
GO
Very informative Paul!
I don’t currently do web any development (LightSwitch or otherwise), so it’s like learning a foreign language.
But I’m sure it’ll come in handy if/when I do start LS web development.
Thanks!
[...] [...]