-
|
|
My book on MSBuild and Team Build
| Archives and Categories
Monday, December 12, 2011
Please read my updated post, the info below is no longer required for some scenarios SlowCheetah build server support updated
A few months ago I published a Visual Studio add-in,
SlowCheetah – XML Transforms, which enables you to transform any XML file during a build in the same way that you can transform the web.config during publish/package for web projects. Since then I’ve received numerous requests to demonstrate how the transforms can be kicked in from a CI server. It’s actually pretty easy because all the logic from a transform perspective is contained inside of MSBuild tasks/targets. The Visual Studio add-in itself is only delivering the support for gestures/preview.
Before I dive into how to enable this for a CI server let me explain how the plugin works and then the CI integration will be come much clearer. When you load a project/solution in Visual Studio for the first time after you installed SlowCheetah a set of files will be written to %localappdata%\Microsoft\MSBuild\SlowCheetah\v1. Those file are:
| Name | Description |
| Install-Manifest.xml | An XML file which describes the files that are installed. This is used by the plugin itself, you should never have to do anything with this file. |
| SlowCheetah.Tasks.dll | The assembly which contains the MSBuild task which transforms XML files. |
| SlowCheetah.Transforms.targets | The MSBuild file which enables XML file transformation |
The add-in adds the following menu command to any XML file for supported project types.

When you click on this for the first time the add-in needs to make some changes to your project file so you are prompted to accept that. After you do the following changes are applied to your project file.
- A new MSBuild property SlowCheetahTargets is added to the project and it points to the SlowCheetah.transforms.targets file in %localappdata%
- An import for that file is added
More specifically the elements added to your project file are.
<PropertyGroup>
<SlowCheetahTargets Condition=" '$(SlowCheetahTargets)'=='' ">$(LOCALAPPDATA)\Microsoft\MSBuild\SlowCheetah\v1\SlowCheetah.Transforms.targets</SlowCheetahTargets>
</PropertyGroup>
<Import Project="$(SlowCheetahTargets)" Condition="Exists('$(SlowCheetahTargets)')" />
Note: If you look at your project file they will not be next to each other. The property will be towards the top of the project file and the import towards the bottom.
So now you might have an idea of why this won’t work on your CI server, the file SlowCheetah.Transforms.targets will not exist in the %localappdata% folder. There is an easy to way to fix this which doesn’t require any changes to your CI server. Check in the files and update your import. Here is what I did:
- Create a folder named SlowCheetah in the same folder as my solution
- Added SlowCheetah.Tasks.dll and SlowCheetah.Transforms.targets to that folder
- Create a solution folder named SlowCheetah in the solution
- Drag and drop the files from your SlowCheetah folder into the SolutionFolder
- Updated my project file to point to these files
Now your solution should look roughly like the following image.

Note: Steps #3/#4 are not strictly required
For #5 you’ll have to edit your project file, don’t worry its an easy modification. Just follow these steps:
- Right click on the project and select unload
- Right click on the unloaded project and select Edit
- Update the value for the SlowCheetahTargets import
In my case the folder that I placed the files into is 1 directory above the project itself. So the new value for the property is as follows.
<SlowCheetahTargets>$(MSBuildProjectDirectory)\..\SlowCheetah\SlowCheetah.Transforms.targets</SlowCheetahTargets>
After that I checked in all the files, kicked off a build and viola the files are transformed. Let me know if you need any more info.
Sayed Ibrahim Hashimi – @SayedIHashimi
Note: I work for Microsoft, but these are my own opinions
Wednesday, November 16, 2011
I get a lot of questions regarding how to publish your web plus your database incrementally. The users of Database projects in Visual Studio 2010 are especially curious about this and how we can integrate Web Deploy with VSDDcmd.exe. I’ve put together a sample which shows how you can use Web Deploy to deploy your web content and VSDBcmd.exe to incrementally publish your database, and the sample also shows how to do this from Team Build.
I am working on creating some more formal documentation for this, but I thought you guys might find this useful before we are able to formalize it. At the end of the post you will the link to download the sample. What you are going to find below is an email which I sent to a tech writer who will be creating the formalized documentation.
What you are going to find here is a bit different from other references which you may have seen but I think that this solution is going to work nicely for enterprise customers.
Here were my guiding principles when creating this:
- · You should be able to easily publish from the dev machine in a similar way that Team Build will
- Publish process should not be drastically different when publishing locally versus Team Build, if it is then it makes it easier for devs to make things work locally but not in the team env.
- · Typical Team Build customers like to build the .sln file (instead of hand crafting .proj files which are responsible for building the individual projects)
- · We should be able to publish both Web + Web Service + DB in a single shot
- · We should make it easy to define new environments
- · You should be able to use the same outputs to publish to multiple environments if you want to
- · Admins should not have to give developers settings to target environments
- i.e. connection strings for prod cannot be checked in via the developer. This rules out using web.config transforms
Let me break down the assets quickly:
| Asset name | Description |
| ContactManager-WCF.sln | The solution file which will be built both locally and from Team Build |
| Publish\Publish.proj | The MSBuild project file which contains the logic to publish |
| Publish\EnvConfig\Env-Dev.proj | The MSBuild project file which contains the properties for the Dev environment. |
| Publish\Publish-Dev.cmd | A script file which developers can double click to invoke a deploy. It just calls out to MSBuild with the correct properties set. |
| Publish\Reset-Local.cmd | You can ignore this, it’s just a script I was using to reset my target env so that I could test publishing. |
| Publish\Reset-Database.sql | Same as previous, you can ignore it just used for my own testing. |
| Pubish\Out | The folder where the build output will be placed when building locally. |
Here is what happens:
Publish.proj has a set of targets which perform the deployment. If you want to publish locally you will not use Visual Studio (because if you did you wouldn’t be able to publish all projects at the same time), instead the developer will simply double click the Publish\Publish-Dev.cmd file. When it is executed locally the Publish.proj file will build the solution and then publish all the projects.
At the very top of that file you will find the following Import statement
<Import Project="$(TargetEnvPropsFile)"/>
This import will use the property TargetEnvPropsFile to import the environment specific properties. This gives the hook to easily define environment specific properties. In my example case I have placed those properties in the file Publish\EnvConfig\Env-Dev.proj. When publishing when I call msbuild.exe I specify the property /p:TargetEnvPropsFile=EnvConfig\Env-Dev.proj which will import all the properties from that file. You can imagine that we can create different files for different environments and we just specify a different property value and the rest of the process stays the same.
For environments which devs don’t have access to admins can create these property files and just pass a different value for TargetEnvPropsFile.
After the solution is built the Publish.proj file will perform the following:
- Publish the database using vsdbcmd.exe and pass in the .deploymanifest file which was generated from the database project
- Update the SetParameters.xml file to contain the env specific values for both web projects
- Execute the deploy.cmd file for both web projects to perform the publish
Note: I had to create a parameters.xml file for the ContactManager.Mvc project in order to enable me to easily update the WCF endpoint value for the service.
Team Build
In Team Build here is what we are going to do:
- Create a Team Build definition which will first build the .sln file and then the Publish.proj file
- Specify the properties which will cause the web packages to be created and also specify the property for the target environment
- See the image below for the Team Build setup
In the Team Build situation there are these customizations that need to be made to the Publish.proj file:
- Pick up the output location from what Team Build specifies
- Ensure that Clean in not executed (taken care of by Team Build)
- Ensure that the .sln is not built (taken care of by Team Build)

Note: You can also specify the property /p:Whatif=true if you want to simulate a publish (MSDeploy updates are spewed to the command line, and DB operations written to a .sql file).
You can download the samples below, please send me any questions/concerns with this approach.
https://github.com/sayedihashimi/sayed-samples/raw/master/ContactManager-publish-example/ContactManager-publish-example-v01.zip
You can find the latest sources for this at my github repository: https://github.com/sayedihashimi/sayed-samples/tree/master/ContactManager-publish-example
Sayed Ibrahim Hashimi – @SayedIHashimi
Wednesday, November 16, 2011 10:32:56 PM (GMT Standard Time, UTC+00:00)

|
Tuesday, November 08, 2011
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:
- By default, deployment packages are created as .zip files. You don't need to change this setting.
- By default, deployment packages are created in the project's obj\Test\Package folder. You don't need to change this setting.
- The default IIS web application name is the name of the project with "_deploy" appended to it. Remove that suffix. You want the application to be named just ContosoUniversity in IIS on your computer.
- For this tutorial you're not deploying IIS settings, so you don't need to enter a password for that.
The Package/Publish Web tab now looks like this:

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.

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.

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.

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.)

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.)

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

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.

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

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.

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

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.)

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.

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

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.

In the Specify a Connection Name step, click Finish.

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.

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.

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.

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

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

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

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

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:

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

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.

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
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
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.
- Add tooling to desktop project to create XDT transforms
- Transform app.config for desktop projects based on build configuration
- Transform any XML file to the output folder base on build configuration
- Add tooling to enable previewing XDT transforms
- 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.

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

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.

After you click it you should see the following.

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
Monday, March 21, 2011
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
Friday, February 25, 2011
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.

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
Saturday, January 08, 2011
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:
- web.conig (XDT) transforms
- How to publish to local file system using Visual Studio
- How to publish to a 3rd party host using Visual Studio via MSDeploy
- How to publish to local IIS server using the .cmd file generated by Visual Studio
- How to use msdeploy.exe to delete IIS applications
- How to use the IIS Manager to import web packages
- How to use msdeploy.exe to deploy a web package to the local IIS server
- How to use msdeploy.exe to deploy a web package to a remove IIS server
- How to use msdeploy.exe to deploy a web package & set parameters using SetParameters.xml to a remote IIS server
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
Saturday, December 11, 2010
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.
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).

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.

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.

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 (
). 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.
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.

There are a few notable values here
- The URL for each hosted application
- The Runtime version for the application
- The path to the application
- The path to the applicationHost.config file
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.
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.
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.
- Profiling is not supported
- If you add a Web Deployment Project (WDP) to an IIS Express web site you will have to remove the ‘:’ from the name of the WDP
- Some issues using WDP for IIS Express sites with sub-webs
Resources
Sayed Ibrahim Hashimi @sayedihashimi
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.
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).

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.

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.

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 (
). 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.
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.

There are a few notable values here
- The URL for each hosted application
- The Runtime version for the application
- The path to the application
- The path to the applicationHost.config file
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.
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.
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.
- Edit and continue is not supported
- Profiling is not supported
- If you add a Web Deployment Project (WDP) to an IIS Express web site you will have to remove the ‘:’ from the name of the WDP
- Some issues using WDP for IIS Express sites with sub-webs
Resources
Sayed Ibrahim Hashimi @sayedihashimi
| On this page |
Archives |
Categories |
|
| June, 2013 (4) |
| May, 2013 (2) |
| March, 2013 (3) |
| February, 2013 (3) |
| January, 2013 (3) |
| December, 2012 (3) |
| November, 2012 (1) |
| October, 2012 (3) |
| August, 2012 (6) |
| July, 2012 (2) |
| June, 2012 (5) |
| May, 2012 (6) |
| April, 2012 (3) |
| March, 2012 (1) |
| February, 2012 (3) |
| January, 2012 (1) |
| December, 2011 (3) |
| November, 2011 (3) |
| August, 2011 (1) |
| March, 2011 (1) |
| February, 2011 (1) |
| January, 2011 (1) |
| December, 2010 (2) |
| November, 2010 (3) |
| October, 2010 (3) |
| September, 2010 (2) |
| August, 2010 (2) |
| July, 2010 (2) |
| June, 2010 (3) |
| May, 2010 (3) |
| April, 2010 (5) |
| March, 2010 (8) |
| February, 2010 (2) |
| January, 2010 (2) |
| December, 2009 (2) |
| November, 2009 (4) |
| October, 2009 (3) |
| September, 2009 (2) |
| August, 2009 (5) |
| July, 2009 (2) |
| June, 2009 (7) |
| May, 2009 (4) |
| April, 2009 (5) |
| March, 2009 (1) |
| February, 2009 (2) |
| January, 2009 (2) |
| December, 2008 (2) |
| November, 2008 (1) |
| October, 2008 (1) |
| September, 2008 (1) |
| July, 2008 (1) |
| June, 2008 (5) |
| May, 2008 (2) |
| April, 2008 (2) |
| March, 2008 (1) |
| February, 2008 (5) |
| January, 2008 (2) |
| November, 2007 (2) |
| October, 2007 (1) |
| September, 2007 (1) |
| June, 2007 (1) |
| April, 2007 (1) |
| February, 2007 (1) |
| January, 2007 (1) |
| November, 2006 (2) |
| August, 2006 (3) |
| July, 2006 (2) |
| June, 2006 (5) |
| May, 2006 (2) |
| April, 2006 (2) |
| March, 2006 (6) |
| February, 2006 (2) |
| January, 2006 (8) |
| December, 2005 (4) |
|
|