- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Tuesday, November 08, 2011

Using a Web Deploy package to deploy to IIS on the dev box and to a third party host

Note: I’d like to thank Tom Dykstra for helping me put this together

Overview

In this tutorial you'll see how to use a web deployment package package to deploy an application. A deployment package is a .zip file that includes all of the content and metadata that's required to deploy an application.

Deployment packages are often used in enterprise environments. This is because a developer or a continuous integration server can create the package without needing to know things like passwords that are stored in Web.config files. Only the server administrator who actually installs the package needs to know those passwords, and that person can enter the details at installation time.

In a smaller organization that doesn't have separate people for these roles, there's less need for deployment packages. But you can also use deployment packages as a way to back up and restore the state of an application. After you use a deployment package to deploy, you can save the package,. Then if a subsequent deployment has a problem, you can quickly and easily restore the application state to the earlier state by reinstalling the earlier package. (This scenario is more complicated if database changes are involved, however.)

This tutorial shows how to use Visual Studio to create a package and IIS Manager to install it. For information about how to create and install packages using the command line, see ASP.NET Deployment Content Map on the MSDN web site.

To keep things relatively simple, this example assumes you have already deployed the application and its databases, and you only need to deploy a code update. You have made the code update, and you are ready to deploy it first to your test environment (IIS on your local computer) and then to your hosting provider. You have a Test build configuration that you use for the test environment and you use the Release build configuration for the production environment. In the example, the name of the Visual Studio project is ContosoUniversity, and instructions for its initial deployment can be found in a series of tutorials that will be published in December on the ASP.NET web site.

The hosting provider shown, Cytanium.com, is one of many that are available, and its use here does not constitute an endorsement or recommendation.

Note The following example uses separate packages for the test and production environments, but you can also create a single deployment package that can be used for both environments. This would require that you use Web Deploy parameters instead of Web.config transformations for Web.config file changes that depend on deployment destination. For information about how to use Web Deploy parameters, see How to: Use Parameters to Configure Deployment Settings When a Package is Installed.

Configuring the Deployment Package

In this section, you'll configure settings for the deployment package. Some of these settings are the same ones that you set also for one-click publish, others are only for deployment packages.

Open the Package/Publish Web tab of the Project Properties window and select the Test build configuration.

For this deployment you aren't making any database changes, so clear Include all databases configured in Package/Publish SQL tab. Make sure Exclude files from the App_Data folder is selected.

Review the settings in the section labeled Web Deployment Package Settings:

The Package/Publish Web tab now looks like this:

Package_Publish_Web_tab_Test

You also need to configure settings for deploying to the production environment. Select the Release build configuration to do that.

Change IIS Web site/application name to use on the destination server to a string that will serve as a reminder of what you need to do later when this value is displayed in the IIS Manager UI: "[clear this field]". The text box on this page won't stay cleared even if you clear it, so entering this note to yourself will remind you to clear this value later when you deploy. When you deploy to your hosting provider, you will connect to a site, not to a server, and in this case you want to deploy to the root of the site.

Package_Publish_Web_tab_Release

Creating a Deployment Package for the Test Environment

To create a deployment package, first make sure you've selected the right build configuration. In the Solution Configurations drop-down box, select Test.

Solution_Configurations_dropdown

In Solution Explorer, right-click the project that you want to build the package for and then select Build Deployment Package.

The Output window reports successful a build and publish (package creation) and tells you where the package was created.

Output_Window_package_creation

Installing the Deployment Package in the Test Environment

The next step is to install the deployment package in IIS on your development computer.

Run IIS Manager. In the Connections pane of the IIS Manager window, expand the local server node, expand the Sites node, and select Default Web Site. Then in the Actions pane, click Import Application. (If you don't see an Import Application link, the most likely reason is that you have not installed Web Deploy. You can use the Web Platform Installer to install both IIS and Web Deploy.)

Default_Web_Site_in_inetmgr

In the Select the Package wizard step, navigate to the location of the package you just created. By default, that's the obj\Test\Package folder in your ContosoUniversity project folder. (A package created with the Release build configuration would be in obj\Release\Package.)

Select_the_Package_dialog_box

Click Next. The Select the Contents of the Package step is displayed.

Select_the_Contents_of_the_Package_dialog_box

Click Next.

The step that allows you to enter parameter values is displayed. The Application Path value defaults to "ContosoUniversity", because that's what you entered on the Package/Publish Web tab of the Project Properties window.

Enter_Application_Package_Information_dialog_box

Click Next.

The wizard asks if you want to delete files at the destination that aren't in the source.

Overwrite_Existing_Files_dialog_box

In this case you haven't deleted any files that you want to delete at the destination, so the default (no deletions) is okay. Click Next.

IIS Manager installs the package and reports its status.

Installation_Progress_and_Summary_dialog_box

Click Finish.

Open a browser and run the application in test by going to the URL http://localhost/ContosoUniversity.

Instructors_page_with_separate_name_fields_Test

Installing IIS Manager for Remote Administration

The process for deploying to production is similar except that you create the package using the Release build configuration, and you install it in IIS Manager using a remote connection to the hosting provider. But first you have to install the IIS Manager feature that facilitates remote connections.

Click the following link to use the Web Platform Installer for this task:

Connecting to Your Site at the Hosting Provider

After you install the IIS Manager for Remote Administration, run IIS Manager. You see a new Start Page in IIS Manager that has several Connect to ... links in a Connection tasks box. (These options are also available from the File menu.)

IIS_Manager_Remote_Admin_Start_Page

In IIS Manager, click Connect to a site. In the Specify Site Connection Details step, enter the Server name and Site name values that are assigned to you by your provider, and then click Next. For a hosting account at Cytanium.com, you get the server name from Service URL in the Visual Studio 2010 section of the welcome email. The site name is indicated by "Site/application" in the same section of the email.

Specify_Site_Connection_Details_dialog_box

In the Provide Credentials step, enter the user name and password assigned by the provider, and then click Next:

Provide_Credentials_dialog_box

You might see a Server Certificate Alert dialog box. If you're sure that you've entered the correct server and site name, click Connect.

Server_Certificate_Alert_dialog_box

In the Specify a Connection Name step, click Finish.

Specify_a_Connection_Name_dialog_box

After IIS Manager connects to the provider's server, a New Feature Available dialog box might appear that lists administration features available for download. Click Cancel — you've already installed everything you need for this deployment.

New_Feature_Available_dialog_box_Cytanium

After the New Feature Available box closes, the IIS Manager window appears. There's now a node in the Connections pane for the site at the hosting provider.

Creating a Package for the Production Site

The next step is to create a deployment package for the production environment. In the Visual Studio Solution Configurations drop-down box, select the Release build configuration.

Solution_Configurations_dropdown_Release

In Solution Explorer, right-click the ContosoUniversity project and then select Build Deployment Package.

The Output window reports a successful build and publish (package creation), and it tells you that the package is created in the obj\Release\Package folder in your project folder.

Installing the Package in the Production Environment

Now you can install the package in the production environment. In the IIS Manager Connections pane, select the new connection you added earlier. Then click Import Application, which will walk you through the same process you followed earlier when you deployed to the test environment.

IIS_Manager_with_provider_site_selected

In the Select the Package step, select the package that you just created:

Select_the_Package_dialog_box_Prod

In the Select the Contents of the Package step, leave all the check boxes selected and click Next:

Select_the_Contents_of_the_Package_dialog_box_Prod

In the Enter Application Package Information step, clear the Application Path and click Next:

Enter_Application_Package_Information_dialog_box_Prod

The wizard asks if you want to delete files at the destination that aren't in the source.

Overwrite_Existing_Files_dialog_box

You don't need to have anything deleted, so just click Next.

When you get the warning about installing to the root folder, click OK:

Installation_in_root_folder_warning

Package installation begins. When it's done, the Installation Progress and Summary dialog box is shown:

Installation_Progress_and_Summary_dialog_box

Click Finish. Your application has been deployed to the hosting provider's server, and you can test by browsing to your public site's URL.

Instructors_page_with_separate_name_fields_Prod

You've now seen how to deploy an application update by manually creating and installing a deployment package. For information about how to create and install packages from the command line in order to be able to integrate them into a continuous integration process, see the ASP.NET Deployment Content Map on the MSDN web site.

Sayed Ibrahim Hashimi – @SayedIHashimi

ddd

IIS | msbuild | MSDeploy | web | Web Deployment Tool | Web Development | Web Publishing Pipeline Tuesday, November 08, 2011 5:11:43 AM (GMT Standard Time, UTC+00:00)  #     | 

Setting Folder Permissions on Web Publish

Note: I’d like to thank Tom Dykstra for helping me put this together

As part of the deployment process you might need to set permissions on one or more folders in the destination web application. For example, if your application allows users to upload files, it needs write access to a folder in order to store the uploaded files. By default, theweb publishing pipeline (the automated deployment process) automatically sets write permission on the App_Data folder in order to enable database updates in case you store databases in that folder, and you can use the same method to make the deployment process automatically set permissions on any folder you choose.

The <projectname>.wpp.targets File

The web publishing pipeline is controlled by MSBuild .targets files that are installed with Visual Studio. Rather than editing the Visual Studio .targets files, you can create a project-specific .targets file that Visual Studio will use to modify the deployment process for a specific project. To do that, you create an XML file in the project folder and name it < projectname>.wpp.targets. The following example shows XML markup that causes the web publishing pipeline to set write permissions for a folder named Elmah located in the root folder of the application. The sample is designed to work for both deployment methods: publish or web deployment package.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <Target Name="SetupCustomAcls" AfterTargets="AddIisSettingAndFileContentsToSourceManifest">   
    <ItemGroup>
      <MsDeploySourceManifest Include="setAcl">
        <Path>$(_MSDeployDirPath_FullPath)\Elmah</Path>
        <setAclAccess>Read,Write</setAclAccess>
        <setAclResourceType>Directory</setAclResourceType>
        <AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
      </MsDeploySourceManifest>
    </ItemGroup>
  </Target>

  <Target Name="DeclareCustomParameters" AfterTargets="AddIisAndContentDeclareParametersItems">
    <ItemGroup>
      <MsDeployDeclareParameters Include="ElmahSetAclParam">
        <Kind>ProviderPath</Kind>
        <Scope>setAcl</Scope>
        <Match>^$(_EscapeRegEx_MSDeployDirPath)\\Elmah$</Match>
        <Description>Add write permission to the Elmah folder.</Description>
        <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/Elmah</DefaultValue>
        <Value>$(_DestinationContentPath)/Elmah</Value>
        <Tags>Hidden</Tags>
        <Priority>$(VsSetAclPriority)</Priority>
        <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
      </MsDeployDeclareParameters>
    </ItemGroup>
  </Target>
  
</Project>

The first Target element ("SetupCustomAcls") causes a setAcl action to be added to the deployment package's source manifest when the package is created. The source manifest is a file that specifies what to include in the deployment package. The path to the folder is specified using an MSBuild variable (_MSDeployDirPath_FullPath) that specifies the path to the project root folder:

<?xml version="1.0" encoding="utf-8"?>
<sitemanifest>  <IisApp path="C:\ContosoUniversity\ContosoUniversity\obj\Release\Package\PackageTmp" managedRuntimeVersion="v4.0" />
  <setAcl path="C:\ContosoUniversity\ContosoUniversity\obj\Release\Package\PackageTmp" setAclResourceType="Directory" />
  <setAcl path="C:\ContosoUniversity\ContosoUniversity\obj\Release\Package\PackageTmp" setAclUser="anonymousAuthenticationUser" setAclResourceType="Directory" />
  <setAcl path="C:\ContosoUniversity\ContosoUniversity\obj\Release\Package\PackageTmp\App_Data" setAclResourceType="Directory" setAclAccess="Write" />
  <setAcl path="C:\ContosoUniversity\ContosoUniversity\obj\Release\Package\PackageTmp\Elmah" setAclResourceType="Directory" setAclAccess="Read,Write" />
</sitemanifest>

This isn't all you need to do, however, because the permissions actually need to be set on the folder in the destination application, not the folder in the deployment package. That's why the second Target element is needed. The second Target element ("DeclareCustomParameters") creates a user-defined Web Deploy parameter named ElmahSetAclParam. The value of this parameter will change the value of the setAcl path at deploy time. TheDefaultValue element of the ElmahSetAclParam parameter sets the value that will be used when you are deploying by creating and installing a deployment package; the Value element sets the value that will be used when you are publishing without using a deployment package.

If you are using a deployment package, the actual path to the destination folder isn't known until the package is installed. Therefore the default value of this parameter is set using an MSDeploy parameter that resolves to the name of a system-defined Web Deploy parameter:

<DefaultValue>{$(_MsDeployParameterNameForContentPath)}/Elmah</DefaultValue>

When the package is created, _MsDeployParameterNameForContentPath is translated into "{IIS Web Application Name}", so the actual default value becomes "{IIS Web Application Name}/Elmah". When the package is installed, the value of that Web Deploy parameter is the path of the destination root folder, and so as a result the ElmahSetAclParam parameter resolves to the physical path of the destination Elmah folder (for example: C:\inetpub\wwwroot\ContosoUniversity\Elmah). That path then replaces the package's physical path for the Elmah setAcl element in the source manifest.

When you publish the project rather than using a deployment package, the Value element instead of the DefaultValue element provides the value for the ElmahSetAclParam parameter. In that case, you are deploying from Visual Studio, and Visual Studio has the actual destination path value in an MSBuild property, so the Value element can specify that MSBuild property directly and does not need to use a Web Deploy variable:

<Value>$(_DestinationContentPath)/Elmah</Value>

The "Hidden" value in the Tags element prevents the ElmahSetAclParam parameter from being displayed in the IIS Manager UI if you use IIS Manager to install the package. The UI doesn't have to show the value, because a user never needs to change it manually.

<Tags>Hidden</Tags>

Adding More Folders

You can add more folders by adding ItemGroup elements. The following example builds on the earlier one by also granting Read permission for the bin folder to the NETWORK SERVICE account. (This is required in some cases for SQL Server Compact.) Notice that in order to specify the account to which access is granted, "setAclUser" was added to the < AdditionalProviderSettings> element, and a <SetAclUser> element specifying NETWORK SERVICE was added.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <Target Name="SetupCustomAcls" AfterTargets="AddIisSettingAndFileContentsToSourceManifest">
        <ItemGroup>
            <MsDeploySourceManifest Include="setAcl">
                <Path>$(_MSDeployDirPath_FullPath)\Elmah</Path>
                <setAclAccess>Read,Write</setAclAccess>
                <setAclResourceType>Directory</setAclResourceType>
                <AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
            </MsDeploySourceManifest>
            <MsDeploySourceManifest Include="setAcl">
                <Path>$(_MSDeployDirPath_FullPath)\bin</Path>
                <setAclUser>NETWORK SERVICE</setAclUser>
                <setAclAccess>Read</setAclAccess>
                <setAclResourceType>Directory</setAclResourceType>
                <AdditionalProviderSettings>setAclUser;setAclResourceType;setAclAccess</AdditionalProviderSettings>
            </MsDeploySourceManifest>
        </ItemGroup>
    </Target>

    <Target Name="DeclareCustomParameters" AfterTargets="AddIisAndContentDeclareParametersItems">
        <ItemGroup>
            <MsDeployDeclareParameters Include="ElmahSetAclParam">
                <Kind>ProviderPath</Kind>
                <Scope>setAcl</Scope>
                <Match>^$(_EscapeRegEx_MSDeployDirPath)\\Elmah$</Match>
                <Description>Add write permission to the Elmah folder.</Description>
                <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/Elmah</DefaultValue>
                <Value>$(_DestinationContentPath)/Elmah</Value>
                <Tags>Hidden</Tags>
                <Priority>$(VsSetAclPriority)</Priority>
                <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
            </MsDeployDeclareParameters>
            <MsDeployDeclareParameters Include="BinSetAclParam">
                <Kind>ProviderPath</Kind>
                <Scope>setAcl</Scope>
                <Match>^$(_EscapeRegEx_MSDeployDirPath)\\Bin$</Match>
                <Description>Add read permission to the bin folder.</Description>
                <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/bin</DefaultValue>
                <Value>$(_DestinationContentPath)/bin</Value>
                <Tags>Hidden</Tags>
                <Priority>$(VsSetAclPriority)</Priority>
                <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
            </MsDeployDeclareParameters>
        </ItemGroup>
    </Target>
    
</Project>

.targets File Caching in Visual Studio

Setting up .targets files is like writing code -- rarely do you get everything right the first time. If you do make a mistake that you have to fix, you should be aware that Visual Studio caches target files. This means that every time you change a .targets file, you must exit Visual Studio and restart it before you rebuild the package in order to see the effect of your change. This is not an issue if you create packages using the command line.

Making Changes that Affect Parameters in Packages

Visual Studio will not rebuild a package if it cannot determine that a change has been made, and changes that affect Web Deploy parameters are sometimes not recognized as changes. Therefore, if you make any changes that affect parameters in the source manifest, it's best to select Clean Solution from the Build menu in order to delete the contents of the Package folder before you rebuild the package.

Sayed Ibrahim Hashimi – @SayedIHashimi

Tuesday, November 08, 2011 4:49:54 AM (GMT Standard Time, UTC+00:00)  #     | 
Wednesday, August 17, 2011

App.config transform Visual Studio Add in

When I talk to people about the XDT (web.config) transforms that web projects support for package/publish one of the most common questions is “Does this work for non-web projects?” Unfortunately the answer is No, but myself and a friend of mine (Chuck England) have created a Visual Studio add in which enables just this. You can find it in the Visual Studio gallery under the name SlowCheetah – XML Transforms. Here is an overview of the features of the add in.

  1. Add tooling to desktop project to create XDT transforms
  2. Transform app.config for desktop projects based on build configuration
  3. Transform any XML file to the output folder base on build configuration
  4. Add tooling to enable previewing XDT transforms
  5. For web projects easily transform other XML files during package/publish

After you install the add in you will get the following menu item when you right click any XML file.

Add-Transform01

For each build configuration defined you will get a transform created as a child item.

App-Config

These files are stub XDT transforms. You can place your transform content in those files and they will be executed when you debug/run your application. For example if your app.config contained the following.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="appName" value="WPF Demo-Debug-default"/>
    <add key="url" value="http://localhost:8080/Default/"/>
    <add key="email" value="demo-default@contoso.com"/>
  </appSettings>

  <connectionStrings>
    <clear />
    <add name="RecordsDb" connectionString=".\SQLExpress;Initial Catalog=RecordsDb-Default;Integrated Security=true"/>
  </connectionStrings>

</configuration>

And your app.debug.config contained

<?xml version="1.0" encoding="utf-8" ?>
<!-- For more information on using transformations 
     see the web.comfig examples at http://go.microsoft.com/fwlink/?LinkId=214134. -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">

  <appSettings>
    <add key="appName" value="WPF Demo-Debug" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
    <add key="url" value="http://localhost:8080/" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
    <add key="email" value="debug@contoso.com" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
  </appSettings>

  <connectionStrings>
    <add name="RecordsDb" connectionString=".\SQLExpress;Initial Catalog=RecordsDb;Integrated Security=true"
         xdt:Transform="Replace" xdt:Locator="Match(name)"/>
  </connectionStrings>

</configuration>

When you run your application the config file created will contain the following.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="appName" value="WPF Demo-Debug"/>
    <add key="url" value="http://localhost:8080/"/>
    <add key="email" value="debug@contoso.com"/>
  </appSettings>

  <connectionStrings>
    <clear/>
    <add name="RecordsDb" connectionString=".\SQLExpress;Initial Catalog=RecordsDb;Integrated Security=true"/>
  </connectionStrings>

</configuration>

Transforming other files

You are not limited to transforming app.config, you can transform any XML file. When you create a transform for an arbitrary XML file when you run your application the transformed XML file will be dropped into the output (bin) folder of your application.

Previewing Transforms

You can easily preview transform results as well. After you have created the transform file you can right click on it and select Preview Transform.

Preview-Transform-Command

After you click it you should see the following.

Preview-Transform-Diff

 

I hope you guys find this useful. Let me know if you have any feedback on this!

Note: This add in was created by me and a friend of mine, these are not created nor supported by Microsoft.

Sayed Ibrahim Hashimi – @sayedihashimi

msbuild | Visual Studio 2010 | XDT Wednesday, August 17, 2011 6:25:37 AM (GMT Daylight Time, UTC+01:00)  #     | 
Monday, March 21, 2011

Property Functions: GetFolderPath

Today someone sent me an email asking how to call the System.Environment.GetFolderPath method passing in the value of MyDocuments for the value of the folder parameter. I was expecting the below to do the trick.

<Project ToolsVersion="4.0" 
         DefaultTargets="Demo" 
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>
    <Tempfile02>$([System.Environment]::GetFolderPath(System.Environment.SpecialFolder.MyDocuments))</Tempfile02>
  </PropertyGroup>

  <Target Name="Demo">
    <Message Text="TempFile01: $(TempFile02)"/>
  </Target>

</Project>

To my surprise I was faced with the following error.

Build started 3/20/2011 6:20:36 PM.

Project "C:\temp\_NET\msbuild\PropFunction01.proj" on node 1 (default targets).

C:\temp\_NET\msbuild\PropFunction01.proj(20,5): error MSB4186: Invalid static method invocation syntax: "[System.Environment]::GetFolderPath(System.Environment.Spec ialFolder.MyDocuments)". Requested value 'System.Environment.MyDocuments' was not found. Static method invocation should be of the form: $([FullTypeName]::Method()), e.g. $([System.IO.Path]::Combine(`a`, `b`)).

Done Building Project "C:\temp\_NET\msbuild\PropFunction01.proj" (default targets) -- FAILED.

Build FAILED.

So I sent an email to the MSBuild team asking “WTF why doesn’t this work?!”, and Dan Moseley (a lead dev there) sent me a snippet that worked, its below.

<Project ToolsVersion="4.0" DefaultTargets="Demo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>
    <Tempfile02>$([System.Environment]::GetFolderPath(SpecialFolder.MyDocuments))</Tempfile02>
  </PropertyGroup>

  <Target Name="Demo">
    <Message Text="TempFile01: $(TempFile02)"/>
  </Target>

</Project>

 

In that snippet instead of using the fully qualified class name of System.Environment.SpecialFolder.MyDocuments, for some reason you have to use the shortened name of just SpecialFolder.MyDocuments. It seems like a bug to me, but at least there is a work around!

Resources

Sayed Ibrahim Hashimi – @sayedihashimi

msbuild | MSBuild 4.0 | Visual Studio 2010 Monday, March 21, 2011 1:27:44 AM (GMT Standard Time, UTC+00:00)  #     | 
Friday, February 25, 2011

How to compress CSS/JavaScript before publish/package

Today I saw a post on stackoverflow.com asking Using Microsoft AJAX Minifier with Visual Studio 2010 1-click publish. This is a response to that question. The Web Publishing Pipeline is pretty extensive so it is easy for us to hook in to it in order to perform operation such as these. One of those extension points, as we’ve blogged about before, is creating a .wpp.targets file. If you create a file in the same directory of your project with the name {ProjectName}.wpp.targets then that file will automatically be imported and included in the build/publish process. This makes it easy to edit your build/publish process without always having to edit the project file itself. I will use this technique to demonstrate how to compress the CSS & JavaScript files a project contains before it is published/packaged.

Eventhough the question specifically states Microsoft AJAX Minifier I decided to use the compressor contained in Packer.NET (link in resources section). I did this because when I looked at the MSBuild task for the AJAX Minifier it didn’t look like I could control the output location of the compressed files. Instead it would simply write to the same folder with an extension like .min.cs or .min.js. In any case, when you publish/package your Web Application Project (WAP) the files are copied to a temporary location before the publish/package occurs. The default value for this location is obj\{Configuration}\Package\PackageTmp\ where {Configuration} is the build configuration that you are currently using for your WAP. So what we need to do is to allow the WPP to copy all the files to that location and then after that we can compress the CSS and JavaScript that goes in that folder. The target which copies the files to that location is CopyAllFilesToSingleFolderForPackage. (To learn more about these targets take a look at the file %Program Files (x86)%\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets.) To make our target run after this target we can use the MSBuild AfterTargets attribute. The project that I created to demonstrate this is called CompressBeforePublish, because of that I create a new file named CompressBeforePublish.wpp.targets to contain my changes.



  

  
  
    
    
      <_JavaScriptFiles Include="$(_PackageTempDir)\Scripts\**\*.js" />
      <_CssFiles Include="$(_PackageTempDir)\Content\**\*.css" />
    

    
    
    

    
    
    
  

Here I’ve created one target, CompressJsAndCss, and I have included AfterTargets=”CopyAllFilesToSingleFolderForPackage” which causes it to be executed after CopyAllFilesToSingleFolderForPackage. Inside this target I do two things, gather the files which need to be compressed and then I compress them.

1. Gather files to be compressed

  <_JavaScriptFiles Include="$(_PackageTempDir)\Scripts\**\*.js" />
  <_CssFiles Include="$(_PackageTempDir)\Content\**\*.css" />

Here I use an item list for both JavaScript files as well as CSS files. Notice that I am using the _PackageTempDir property to pickup .js & .css files inside the temporary folder where the files are written to be packaged. The reason that I’m doing that instead of picking up source files is because my build may be outputting other .js & .css files and which are going to be published. Note: since the property _PackageTempDir starts with an underscore it is not guaranteed to behave (or even exist) in future versions.

2. Compress files

I use the Packer task to compress the .js and .css files. For both sets of files the usage is pretty similar so I will only look at the first usage.

Here the task is fed all the .js files for compression. Take a note how I passed the files into the task using, %(_JavaScriptFiles.Identity), in this case what that does is to cause this task to be executed once per .js file. The %(abc.def) syntax invokes batching, if you are not familiar with batching please see below. For the value of the output file I  use the _PackageTempDir property again. In this case since the item already resides there I could have simplified that to be @(_JavaScriptFiles->’%(FullPath)’) but I thought you might find that expression helpful in the case that you are compressing files which do not already exist in the _PackageTempDir folder.

Now that we have added this target to the .wpp.targets file we can publish/package our web project and it the contained .js & .css files will be compressed. Note: Whenever you modify the .wpp.targets file you will have to unload/reload the web project so that the changes are picked up, Visual Studio caches your projects.

In the image below you can see the difference that compressing these files made.

image

You can download the entire project below, as well as take a look at some other resources that I have that you might be interested in.

Sayed Ibrahim Hashimi

Resources
Deployment | MSDeploy | Web Publishing Pipeline Friday, February 25, 2011 5:21:44 AM (GMT Standard Time, UTC+00:00)  #     | 
Saturday, January 08, 2011

Video on Web Deployment using Visual Studio 2010 and MSDeploy

Back in November I participated in Virtual Tech Days which is an online conference presented by Microsoft. In the session I discussed the enhancements to web deployment using Visual Studio 2010 and MSDeploy. Some of the topics which I covered includ:

You can download the video & all of my sample files at http://virtualtechdays.com/pastevents_2010november.aspx. In the samples you will find all of the scripts that I used and a bunch of others which I didn’t have time to cover. Enjoy!

Sayed Ibrahim Hashimi @sayedihashimi

Config-Transformation | IIS | msbuild | MSDeploy | speaking | Visual Studio | Visual Studio 2010 | web | Web Deployment Tool | Web Development | Web Publishing Pipeline Saturday, January 08, 2011 8:34:08 PM (GMT Standard Time, UTC+00:00)  #     | 
Saturday, December 11, 2010

Visual Studio 2010 SP1 Beta IIS Express Integration

A few days ago Jason Zander blogged about the availability of Visual Studio SP1 Beta (all links below in resources section). I am happy to let you guys know that with SP1 Beta we have enabled integration of IIS Express into Visual Studio. After you have installed SP1 Beta you have to download IIS Express separately for the option to “light up”. After you’ve downloaded and installed IIS Express you can now create Web projects and Websites which use IIS Express instead of Cassini (the default web server used in Visual Studio). For more information regarding the difference between IIS Express and IIS/Cassini please see Scott’s blog linked below. Visual Studio 2010 has two types of web projects; Web Application Project (WAP) and Website projects. You can use either project type with IIS Express.

Enabling IIS Express for web projects

For both WAP and Website projects which were using Cassini we have a very simply method for you to use IIS Express. You can just right click then select the context menu option “Use IIS Express”. Take a look at the screen shot that follows for that command.

image

After you do this your project will use IIS Express instead of Cassini going forward. If you wish to revert back to using Cassini you can just right click on the project and select “Use Visual Studio Development Server…” You can also make IIS Express your default web server for new projects as well.

For Web site projects when you create a new site you can create it such that IIS Express hosts it instead of Cassini. From Visual Studio when you select File->New-> Web Site you will see the New Web Site dialog. On that dialog you can click on the Browse button (see image below).

SNAGHTML108dba11

After you click that you will be prompted to enter the location where the site will be located. From there you can select “Local IIS” and the create a new IIS Express site for it.

SNAGHTML108f284e

In this dialog you need to select IIS Express Sites then click on the Create New Site button located towards the upper right hand corner. Once you do this it will create a new IIS Express site for your Web site.

Making IIS Express your default web server

In order to make IIS Express for new file based web projects you can go to the menu option Tools->Options->Projects and Solutions->Web Projects and check the “Use IIS Express for new file-based web sites and projects” check box. You can see this option in the next image.

SNAGHTML10527286

After you have done this  new projects/sites will use IIS Express by default. Note: if you have any existing projects/sites you will still have to “opt-in” to using IIS Express for each project.

Using IIS Express

After you have enabled IIS Express to be your projects web server, when you start debugging, or just start running your app, from Visual Studio you will see a new system tray icon appear (image). You can right click on this icon to to quickly see what applications it is hosting. Each hosted application will have its own context menu. From this menu you can browse out to the application as well as stop it quickly.

image

You can also click on “Show All Applications” to open a dialog which give you more information about the sites that IIS Express is hosting. For example I opened this dialog and selected a running application and the result is shown in the image below.

SNAGHTML105b4a00

There are a few notable values here

The default location for the applicationHost.config file is your users’ directory. This enables IIS Express to run without requiring admin privileges. As you may know IIS (i.e. the full IIS) uses the applicationHost.config file to store its configuration. IIS Express is similar to IIS in that it uses a file also named applicationHost.config (not the same file as IIS though). The file that IIS uses is stored under the System32 directory and is shared across all users. The IIS Express config is specific to the current user. Regarding the runtime version, this is the version of the CLR which your application pool will use. For IIS Express application pools will, by default, use CLR 4.0. You can change this default in the applicationHost.config file for IIS Express. Visual Studio will assign the correct CLR version to your app pool based on the target framework version.

You can customize a few options for your site directly from Visual Studio. If you select your web project/site in the Solution Explorer and then open the Properties pane (you can right-click and select Properties on the project/site if it is not visible) you should see something similar to the following.

image

These settings will change how IIS Express hosts your site. One of the missing features of Cassini was the ability to host SSL sites. In IIS Express you can enable this. For example for this site I changed the value for SSL Enabled to be True, then a new URL was assigned so that I can use SSL to browse to the site. Please note that IIS Express will install a self-signed cert and you will see the following security warning from Internet Explorer when you browse to an https URL hosted by IIS Express.

image

One thing to make a note of is that when you change the settings for IIS Express here these settings are stored in the applicationHost.config file for IIS Express and not with the project/site itself. So if you are working in a team then your other team members will have to make the same changes. If you want to edit a setting which is not shown on the properties grid then you can edit the applicationHost.config file directly. You can easily open this file by clicking on the applicationHost.config link in the IIS Express dialog shown previously. For WAP projects you can change the port that is used from the Web tab in the project properties page.

Known issues

There are currently some known issues with IIS Express which are listed below. If you run into any more please let us know.

 

Resources

Sayed Ibrahim Hashimi @sayedihashimi

IIS | IIS Express | Visual Studio 2010 Saturday, December 11, 2010 11:22:22 PM (GMT Standard Time, UTC+00:00)  #     | 

Visual Studio 2010 SP1 Beta IIS Express Integration

A few days ago Jason Zander blogged about the availability of Visual Studio SP1 Beta (all links below in resources section). I am happy to let you guys know that with SP1 Beta we have enabled integration of IIS Express into Visual Studio. After you have installed SP1 Beta you have to download IIS Express separately for the option to “light up”. After you’ve downloaded and installed IIS Express you can now create Web projects and Websites which use IIS Express instead of Cassini (the default web server used in Visual Studio). For more information regarding the difference between IIS Express and IIS/Cassini please see Scott’s blog linked below. Visual Studio 2010 has two types of web projects; Web Application Project (WAP) and Website projects. You can use either project type with IIS Express.

Enabling IIS Express for web projects

For both WAP and Website projects which were using Cassini we have a very simply method for you to use IIS Express. You can just right click then select the context menu option “Use IIS Express”. Take a look at the screen shot that follows for that command.

image

After you do this your project will use IIS Express instead of Cassini going forward. If you wish to revert back to using Cassini you can just right click on the project and select “Use Visual Studio Development Server…” You can also make IIS Express your default web server for new projects as well.

For Web site projects when you create a new site you can create it such that IIS Express hosts it instead of Cassini. From Visual Studio when you select File->New-> Web Site you will see the New Web Site dialog. On that dialog you can click on the Browse button (see image below).

SNAGHTML108dba11

After you click that you will be prompted to enter the location where the site will be located. From there you can select “Local IIS” and the create a new IIS Express site for it.

SNAGHTML108f284e

In this dialog you need to select IIS Express Sites then click on the Create New Site button located towards the upper right hand corner. Once you do this it will create a new IIS Express site for your Web site.

Making IIS Express you default web server

In order to make IIS Express for new file based web projects you can go to the menu option Tools->Options->Projects and Solutions->Web Projects and check the “Use IIS Express for new file-based web sites and projects” check box. You can see this option in the next image.

SNAGHTML10527286

After you have done this  new projects/sites will use IIS Express by default. Note: if you have any existing projects/sites you will still have to “opt-in” to using IIS Express for each project.

Using IIS Express

After you have enabled IIS Express to be your projects web server, when you start debugging, or just start running your app, from Visual Studio you will see a new system tray icon appear (image). You can right click on this icon to to quickly see what applications it is hosting. Each hosted application will have its own context menu. From this menu you can browse out to the application as well as stop it quickly.

image

You can also click on “Show All Applications” to open a dialog which give you more information about the sites that IIS Express is hosting. For example I opened this dialog and selected a running application and the result is shown in the image below.

SNAGHTML105b4a00

There are a few notable values here

The default location for the applicationHost.config file is your users’ directory. This enables IIS Express to run without requiring admin privileges. As you may know IIS (i.e. the full IIS) uses the applicationHost.config file to store its configuration. IIS Express is similar to IIS in that it uses a file also named applicationHost.config (not the same file as IIS though). The file that IIS uses is stored under the System32 directory and is shared across all users. The IIS Express config is specific to the current user. Regarding the runtime version, this is the version of the CLR which your application pool will use. For IIS Express application pools will, by default, use CLR 4.0. You can change this default in the applicationHost.config file for IIS Express. Visual Studio will assign the correct CLR version to your app pool based on the target framework version.

You can customize a few options for your site directly from Visual Studio. If you select your web project/site in the Solution Explorer and then open the Properties pane (you can right-click and select Properties on the project/site if it is not visible) you should see something similar to the following.

image

These settings will change how IIS Express hosts your site. One of the missing features of Cassini was the ability to host SSL sites. In IIS Express you can enable this. For example for this site I changed the value for SSL Enabled to be True, then a new URL was assigned so that I can use SSL to browse to the site. Please note that IIS Express will install a self-signed cert and you will see the following security warning from Internet Explorer when you browse to an https URL hosted by IIS Express.

image

One thing to make a note of is that when you change the settings for IIS Express here these settings are stored in the applicationHost.config file for IIS Express and not with the project/site itself. So if you are working in a team then your other team members will have to make the same changes. If you want to edit a setting which is not shown on the properties grid then you can edit the applicationHost.config file directly. You can easily open this file by clicking on the applicationHost.config link in the IIS Express dialog shown previously. For WAP projects you can change the port that is used from the Web tab in the project properties page.

Known issues

There are currently some known issues with IIS Express which are listed below. If you run into any more please let us know.

 

Resources

 

Sayed Ibrahim Hashimi @sayedihashimi

IIS | IIS Express | Visual Studio 2010 Saturday, December 11, 2010 11:02:53 PM (GMT Standard Time, UTC+00:00)  #     | 
Thursday, November 18, 2010

XDT (web.config) Transforms in non-web projects

One of the really cool features that we shipped for Visual Studio 2010 was web.config (XDT) transformations. Because the transformations are so simple and straightforward one of the first questions that someone asks after using it is “how can I use this in my other projects?” Unfortunately this feature is only built into the Web Application Projects (WAP). But it is very easy to reuse this because we just rely on an MSBuild task to do the heavy lifting for us. I received an email from that basically went like this

“Hi, I would like to use XDT transformations on my WPF project for both the app.config file as well as my unity.xml file. How can I do this?”

So one answer is to modify your project file to use the TransformXml task as I have blogged previously about (link below). But I thought that since this was such a common problem that I should go ahead and create a .targets file which would solve the above problem and could be re-used by anyone.

Let me clarify the scenario a bit before we dive into the details of the solution. I have create a sample Wpf project, named Wpf01. Inside of that project I have created these files:

Take a look at the image below (note: I manually edited the project file to make the file nest under each other, I will explain that shortly)

image

The files with .debug/.release are transform files. When I build I expect the following to happen:

  1. Transform app.config with app.{Configuration}.config and write file to output folder with the correct name i.e. Wpf01.exe.config instead of just app.config
  2. Transform Sample01.xml with Sample01.{Configuration}.config and write it to output folder with the name Sample01.config
  3. Transform Sub\Sub\Sub01.xml with Sub\Sub\Sub01.{Configuration}.config and write it to the output folder with the name Sub\Sub\Sub01.xml
  4. None of my source files should change

Usage

Before I get into the solution let me explain how to use the solution first because if you are not interested in the MSBuild details you can skip over that Smile

  1. You must have installed Web projects with Visual Studio on the machine (it contains the TransformXmll task).
  2. Create the folder %ProgramFiles (x86)%\MSBuild\Custom. If you want to share this across team members then see my note at the end of this blog.
  3. Download TransformFiles.targets (link below) and place the file into the folder %ProgramFiles (x86)%\MSBuild\Custom.
  4. Edit your project file (right click on the project Unload Project, right click again and pick edit)
  5. At the end of the project file place the element <Import Project="$(MSBuildExtensionsPath)\Custom\TransformFiles.targets" /> immediately above the closing </Project> tag
  6. For files that you want transformed a metadata value of TransformOnBuild to true. See below on what this means.
  7. Build and take a look at files in your output directory

For #5 lets examine the sample that I created. In this sample I had an app.config file. When I first created the project the entry in the project file for app.config looked like the following.

<None Include="app.config" />

So what you need to do is to add a new metadata value as described above for that. So it will turn into the following.

<None Include="app.config">
  <TransformOnBuild>true</TransformOnBuild>
</None>

The transform targets will look for items that have this value declared on them and then during build it will transform them, if the transform file exists in the same folder as the file itself. You will need to add TransfromOnBuild to all the files that you want to transform. So in my case I added it to app.config, Sample01.xml and Sub01.xml. Note you should not add this to the transform files themselves because you will just waste your own time. After you do this you should perform a build then take a look at the output directory for your transformed files. The app.config should write out the the correct file and the others as expected.

Nest transforms under the source file

You might have noticed that in the image above that the transform files are nested under the files themselves. To do this you need to add the DependentUpon metadata value to the child items. For instance for app.config the child items look like the following.

<None Include="app.debug.config">
  <DependentUpon>app.config</DependentUpon>
</None>
<None Include="app.release.config">
  <DependentUpon>app.config</DependentUpon>
</None>

Implementation

If you are wondering how this works then this is the section for you. TransformFile.targets has 2 targets; DiscoverFilesToTransform and TransformAllFiles. DiscoverFilesToTransform looks through a set of items (None, Content, and Resource). Inside of DiscoverFilesToTransform I look for values with the %(TransformOnBuild)==true. After all of those are collected I identify if there is an app.config file being transformed and if so it is placed into a specific item list and all others go into another item list.

Inside of TransformAllFiles the TransformXml task is used to transform all of the files. This target injects itself into the build process by having the attribute AfterTargets="Build;_CopyAppConfigFile". So whenever the Build or _CopyAppConfigFile targets are called the TransformAllFiles target will execute.

Here if the full code for this file.

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="TransformXml"
         AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
  
  <ItemDefinitionGroup>
    <!-- Set the default value to false here -->
    <None>
      <TransformOnBuild>false</TransformOnBuild>
    </None>    
    <Content>
      <TransformOnBuild>false</TransformOnBuild>
    </Content>    
    <Resource>
      <TransformOnBuild>false</TransformOnBuild>
    </Resource>
    <EmbeddedResource>
      <TransformOnBuild>false</TransformOnBuild>
    </EmbeddedResource>
    
    <_FilesToTransform>
      <IsAppConfig>false</IsAppConfig>
    </_FilesToTransform>
  </ItemDefinitionGroup>

  <PropertyGroup>
    <TransformAllFilesDependsOn>
      DiscoverFilesToTransform;
    </TransformAllFilesDependsOn>
  </PropertyGroup>
  <Target Name="TransformAllFiles" DependsOnTargets="$(TransformAllFilesDependsOn)" AfterTargets="Build;_CopyAppConfigFile">
    <!-- Now we have the item list _FilesToTransformNotAppConfig and _AppConfigToTransform item lists -->
    <!-- Transform the app.config file -->    
    <ItemGroup>
      <_AppConfigTarget Include="@(AppConfigWithTargetPath->'$(OutDir)%(TargetPath)')" />
    </ItemGroup>
    
    <PropertyGroup>
      <_AppConfigDest>@(_AppConfigTarget->'%(FullPath)')</_AppConfigDest>
    </PropertyGroup>

    <MakeDir Directories="@(_FilesToTransformNotAppConfig->'$(OutDir)%(RelativeDir)')"
             Condition="Exists('%(RelativeDir)%(Filename).$(Configuration)%(Extension)')"/>
    
    <TransformXml Source="@(_AppConfigToTransform->'%(FullPath)')"
                  Transform="%(RelativeDir)%(Filename).$(Configuration)%(Extension)"
                  Destination="$(_AppConfigDest)"
                  Condition=" Exists('%(RelativeDir)%(Filename).$(Configuration)%(Extension)') " />

    
    <TransformXml Source="@(_FilesToTransformNotAppConfig->'%(FullPath)')"
                  Transform="%(RelativeDir)%(Filename).$(Configuration)%(Extension)"
                  Destination="@(_FilesToTransformNotAppConfig->'$(OutDir)%(RelativeDir)%(Filename)%(Extension)')"
                  Condition=" Exists('%(RelativeDir)%(Filename).$(Configuration)%(Extension)') " />
  </Target>
  
  <Target Name="DiscoverFilesToTransform">
    <!-- 
    This will look through items list: None & Content for those
    with Metadata <TransformOnBuild>True</TransformOnBuild>
    -->
    <ItemGroup>
      <_FilesToTransform Include="@(None);@(Content);@(Resource);@(EmbeddedResource)"
                         Condition=" '%(TransformOnBuild)' == 'true' "/>
    </ItemGroup>    

    <PropertyGroup>
      <_AppConfigFullPath>@(AppConfigWithTargetPath->'%(RootDir)%(Directory)%(Filename)%(Extension)')</_AppConfigFullPath>
    </PropertyGroup>

    <!-- Now look to see if any of these are the app.config file -->
    <ItemGroup>
      <_FilesToTransform Condition=" '%(FullPath)'=='$(_AppConfigFullPath)' ">
        <IsAppConfig>true</IsAppConfig>
      </_FilesToTransform>
    </ItemGroup>
          
    <ItemGroup>
      <_FilesToTransformNotAppConfig Include="@(_FilesToTransform)"
                                     Condition=" '%(IsAppConfig)'!='true'"/>
      
      <_AppConfigToTransform  Include="@(_FilesToTransform)"
                              Condition=" '%(IsAppConfig)'=='true'"/>
    </ItemGroup>
  </Target>
</Project>

Gaps

With most things found on blogs there are some gaps Those are described here.

Clean build => It’s a best practice to delete files upon clean, but in this case I am not. This would be pretty easy to add, if you are interested let us know and I will update the sample.

Incremental build => The transforms will run every time you build even if the outputs are up to date, if this is an issue for you let us know and I will update the sample.

Sharing with team members

If you want to share with team members instead of placing this into %ProgramFiles (x86)% just place it into a folder in version control then change the <Import statement to point to that file instead of using MSBuildExtensionPath.

 

If you end up using this please let us know what is your experience with it.

Resources

Sayed Ibrahim Hashimi @sayedihashimi

msbuild | MSBuild 4.0 | MSDeploy | Web Publishing Pipeline Thursday, November 18, 2010 5:41:09 AM (GMT Standard Time, UTC+00:00)  #     | 
Thursday, November 11, 2010

ASP.NET Web Application: Publish/Package Tokenizing Parameters

Today I just saw a question posted on stackoverflow.com asking Why are some Web.config transforms tokenised into SetParameters.xml and others are not? Let me give some background on this topic for those who are not aware of what the question is.

With Visual Studio 2010 when you package your application using the Build Deployment Package context menu option, see image below.

image

When build the package by default the package will be created in obj\{Configuration}\Package\{ProjectName}.zip where {Configuration} is the current build configuration, and {ProjectName} is the name of the project. So in this case I since I’m building with Debug and the project name is MvcApplication1 the package will be placed at obj\Debug\Package\MvcApplication1.zip. If you take this package and then import into IIS 7 with the “Import Application” option shown below. Note: The machine must have the Web Deployment Tool (aka MSDeploy) installed.

image

Once you click on Import Application then browse out to the package you will be shown a screen which prompts your for parameters. Its shown below.

SNAGHTML2d2664

On this screen you can see that we are prompting for a couple parameter values here. One is an IIS setting, Application Path, and the other is a connection string which will be placed inside the web.config file. If your Web Application Project (WAP)  had 5 different connection strings then they would automatically show up here on this page. Since connection strings are replaced so often we create parameters for all connection strings by default. You can define new parameters on your own, quite easily actually, but that is the topic for another blog post.

Now back to the question. He is asking why do we “tokenize” the connection strings in web.config. To clarify take a look at my web.config file below.

<configuration>
  <appSettings>
    <add key="setting01" value="value01"/>
  </appSettings>
  
  <connectionStrings>
    <add name="ApplicationServices"
         connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
         providerName="System.Data.SqlClient" />
  </connectionStrings>
  
</configuration>

After I perform a package this will get changed. Take a look @ the web.config file which resides in the package (you can get to the file at obj\{CofigurationName}\Package\PackageTmp\web.config). You will see what is shown below.

<configuration>
  <appSettings>
    <add key="setting01" value="value01"/>
  </appSettings>
  <connectionStrings>
    <add name="ApplicationServices"
         connectionString="$(ReplacableToken_ApplicationServices-Web.config Connection String_0)"
         providerName="System.Data.SqlClient" />
  </connectionStrings>

</configuration>

So his question is why is the connection string replaced with $(ReplacableToken_ApplicationServices-Web.config Connection String_0) and nothing else is? We do this because we do not want you to accidently copy your web to a location and have it executing SQL statements against a SQL server which you did not intend. The idea is that you will create a package that you can deploy to many different environments. So the value that was in your web.config (or web.debug.config/web.release.config if you are using a web.config transformation) will not be placed inside the web.config in the package. Instead those values will be used as defaults in the package itself. We also create a SetParameters.xml file for you so that you can tweak the values. For my app see the MvcApplication1.SetParameters.xml file below.

<?xml version="1.0" encoding="utf-8"?>
<parameters>
  <setParameter name="IIS Web Application Name" 
                value="Default Web Site/MvcApplication1_deploy" />
  <setParameter name="ApplicationServices-Web.config Connection String" 
                value="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" />
</parameters>

The idea is that you can deploy your package in 2 ways. Through the IIS Manager which will prompt you for the parameters or you can deploy using msdeploy.exe with the –setParamFile switch to specify the path to the SetParameters.xml file. In this case I could create a QA01.SetParameters.xml file along with a QA02.SetParameters.xml file to deploy my web to my two QA servers. How do we do this?

How connection strings are tokenized

You might be wondering how the connection strings are tokenized to begin with. With Visual Studio 2010 we released web.config transformations, which all you to write terse web.config transformations inside of files like web.debug.config/web.release.config. When you package/publish your web these transform files are used to transform your web.config based on what you expressed in the appropriate transform file. We have an MSBuild task TransformXml which performs the transformation. We use that same task to tokenize the connection strings. If you are interested in the details take a look at %ProgramFiles32%\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets in the AutoParameterizationWebConfigConnectionStringsCore target.

Now what if you do not want the connection string tokenized?

Prevent tokenizing connection strings

If you want to prevent your web.config connection strings from being tokenized it’s pretty easy. All we need to do is the add a property to the build/package/publish process. We can do that in 2 ways. Edit the project file itself or create a file with the name {ProjectName}.wpp.targets where {ProjectName} is the name of your project. The second approach is easier so I use that. In my case it would be MvcApplication1.wpp.targets. The contents of the file are shown below.

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>
    <AutoParameterizationWebConfigConnectionStrings>false</AutoParameterizationWebConfigConnectionStrings>
  </PropertyGroup>
  
</Project>

Note: You may need to reload the project in Visual Studio for this to take effect.

Inside of this file I have declared the property, AutoParameterizationWebConfigConnectionStrings, to be false. This is telling the Web Publishing Pipeline (WPP) that it should not replace replace the connection strings with tokens, instead leave them as they are.

Questions/Comments???

Other Resources

asp.net | Deployment | msbuild | MSBuild 4.0 | MSDeploy Thursday, November 11, 2010 5:41:09 AM (GMT Standard Time, UTC+00:00)  #     |