How can I apply configuration transforms to EntLib.config? - visual-studio-2010

I had a large Silverlight project with an unwieldy web.config, which used transforms against the web.debug.config, web.uat.config, and web.release.config files.
I've not separated out my EntLib configuration into EntLib.config, with matching EntLib.debug.config, EntLib.uat.config and EntLib.release.config files. I've edited the .csproj file and used DependentUpon so that the files are nested under EntLib.config. Now I'm trying to get VS2010 to apply the transforms when I use the Publish... menu option to publish the files straight to the test server.
I've been trying to apply this as shown below but it doesn't seem to work. I can see the transformed EntLib.config file in obj\$(Configuration)\TransformWebConfig\transformed but it isn't deployed. I've also tried using Project > Build Deployment Package which I've then run on another machine. Both leave me with EntLib.config in its original form plus each of the EntLib.($Configuration).config files alongside it. Should it work? Any help anyone can offer would be appreciated.
<PropertyGroup>
<ConfigFileName>EntLib.config</ConfigFileName>
</PropertyGroup>
<PropertyGroup>
<!-- This property is used to handle circular dependency between
TransformWebConfig and our custom target TransformAppConfig -->
<FirstRun Condition="$(FirstRun) == ''">true</FirstRun>
</PropertyGroup>
<!-- This target will be called one time after a call to TransformWebConfig -->
<Target Name="TransformAppConfig" AfterTargets="TransformWebConfig" Condition="$(FirstRun) == 'true'">
<MSBuild Projects="$(MSBuildProjectFile)" Targets="TransformWebConfig" Properties="ProjectConfigFileName=$(ConfigFileName);
Configuration=$(Configuration);
FirstRun=false" />
</Target>
<!-- This target will be called one time before PreAutoParameterizationWebConfigConnectionStrings
to add $(ConfigFileName) to autoparameterization step -->
<Target Name="AddToAutoParameterizationStep" BeforeTargets="PreAutoParameterizationWebConfigConnectionStrings">
<ItemGroup>
<_WebConfigsToAutoParmeterizeCS Include="#(FilesForPackagingFromProject)" Condition="('%(FilesForPackagingFromProject.Filename)%(FilesForPackagingFromProject.Extension)'=='$(ConfigFileName)') And !%(FilesForPackagingFromProject.Exclude)">
<TransformOriginalFile>$(AutoParameterizationWebConfigConnectionStringsLocation)\original\%(DestinationRelativePath)</TransformOriginalFile>
<TransformOutputFile>$(AutoParameterizationWebConfigConnectionStringsLocation)\transformed\%(DestinationRelativePath)</TransformOutputFile>
<TransformScope>$(_PackageTempDir)\%(DestinationRelativePath)</TransformScope>
</_WebConfigsToAutoParmeterizeCS>
<_WebConfigsToAutoParmeterizeCSOuputFiles Include="#(_WebConfigsToAutoParmeterizeCS->'%(TransformOutputFile)')">
</_WebConfigsToAutoParmeterizeCSOuputFiles>
</ItemGroup>
</Target>

I use T4 and TextTransform.exe to create different configs based on build configuration. You can take a look on my snippets for app.config, but the same technique can be applied for web.config.
1) Project structure
ProjectDir
App_Config
Configuration.tt // template for all configs
Debug.App.tt // settings for Debug
Release.App.tt // settings for Release
ProductDeploy.App.tt // settings for deploy
App.config // autogenerated. Ignored in SVN
project.csproj
2) project.csproj modification allows to have up-to-date config for specified Platform/Configuration.
<PropertyGroup>
<T4Template>$(ProjectDir)\App_Config\$(Configuration).App.tt</T4Template>
<T4CommonTemplate>$(ProjectDir)\App_Config\Configuration.tt</T4CommonTemplate>
<T4Config>$(ProjectDir)\App.config</T4Config>
<T4LastConfiguration>$(BaseIntermediateOutputPath)\$(Configuration).t4lastbuild</T4LastConfiguration>
</PropertyGroup>
<Target Name="BeforeBuild" DependsOnTargets="ExecuteT4Templates" />
<Target Name="ExecuteT4Templates" Inputs="$(T4Template);$(T4CommonTemplate);$(T4LastConfiguration)" Outputs="$(T4Config)">
<MakeDir Directories="$(BaseIntermediateOutputPath)" Condition="!Exists('$(BaseIntermediateOutputPath)')" />
<ItemGroup>
<T4ConfigFlags Include="$(BaseIntermediateOutputPath)\*.t4lastbuild" />
</ItemGroup>
<Delete Files="#(T4ConfigFlags)" />
<WriteLinesToFile File="$(T4LastConfiguration)" Lines="T4 Succeeded" Overwrite="true" />
<Exec Command="TextTransform "$(T4Template)" -out "$(T4Config)"" WorkingDirectory="C:\Program Files\Common Files\microsoft shared\TextTemplating\1.2\" />
</Target>
<Target Name="AfterClean">
<ItemGroup>
<T4ConfigFlags Include="$(BaseIntermediateOutputPath)\*.t4lastbuild" />
</ItemGroup>
<Delete Files="#(T4ConfigFlags)" />
</Target>
3) Configuration.tt sample
<## template language="C#"#>
<## output extension= ".config"#>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name = "NameSpace.Properties.Settings.SomeConnectionString"
connectionString = "<#= this.SomeConnectionString #>"
providerName = "System.Data.SqlClient" />
</connectionStrings>
<applicationSettings>
<NameSpace.Properties.Settings>
<setting name="DefAppSetting" serializeAs="String">
<value><#= this.DefAppSetting #></value>
</setting>
</NameSpace.Properties.Settings>
</applicationSettings>
</configuration>
<#+
string SomeConnectionString = "default SomeConnectionString";
string DefAppSetting = "some_value";
#>
4) Debug.App.tt sample
<#
SomeConnectionString = "Data Source=.;Initial Catalog=SomeDB;Integrated Security=True";
DefAppSetting = "debug_some_value";
#>
<## include file="Configuration.tt" #>

I solved this using this article: Xml Document Transforms (XDT) for any XML file in your project by Vishal Joshi and posted the specifics here: How to apply transforms to EntLib.config.
My own solution followed Vishal's option to store his XDT targets file in the project so that it gets stored in source control and is available to everyone, rather than storing it locally on the machine.

Related

MSBuild: How to run custom target after _CopyFilesToPublishFolder?

I'm using Visual Studio 2013. I'm trying to publish a ClickOnce application from the command-line by passing /target:publish. However, I would like to do a few extra steps after MSBuild is done copying all the files to the publish folder. This is what I have come up so far:
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildCommunityTasksPath>$(SolutionDir)\.build</MSBuildCommunityTasksPath>
<MSBuildCommunityTasksLib>$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.dll</MSBuildCommunityTasksLib>
<ClickOnceBuildDirectory>$(ProjectDir)\bin\app.publish</ClickOnceBuildDirectory>
</PropertyGroup>
<Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.targets" />
<UsingTask TaskName="FileUpdate" AssemblyFile="$(MSBuildCommunityTasksLib)" />
<Target Name="CustomPostPublishActions" AfterTargets="PublishBuild" >
<!-- Create directory -->
<MakeDir Directories="$(ProjectDir)..\Deploy\Client\Application Files" />
<!-- Copy published website to deployment location -->
<ItemGroup>
<_CopyItems Include="$(ClickOnceBuildDirectory)\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(_CopyItems)" DestinationFolder="$(ProjectDir)..\Deploy\Client" />
<!-- Write publish.htm file for ClickOnce -->
<Copy SourceFiles="$(ProjectDir)\..\..\Build\publish.htm" DestinationFiles="$(ProjectDir)..\Deploy\Client\publish.htm" />
<FileUpdate Files="$(ProjectDir)..\Deploy\Client\publish.htm" Regex="{VERSION}" IgnoreCase="true" Multiline="true" Singleline="false" ReplacementText="$(ApplicationVersion)" />
</Target>
</Project>
Unfortunately, this is the order that the targets get run:
CustomPostPublishActions:
Creating directory "C:\MyProject\..\Deploy\Client\Application Files".
Copying file from "C:\MyProject\\bin\app.publish\DeploymentScheduler.exe" to "C:\MyProject\..\Deploy\Client\DeploymentScheduler.exe".
Copying file from "C:\MyProject\\..\..\Build\publish.htm" to "C:\MyProject\..\Deploy\Client\publish.htm".
Updating File "C:\MyProject\..\Deploy\Client\publish.htm".
_CopyFilesToPublishFolder:
Creating directory "bin\app.publish\Application Files\MyApplication_1_0_0_0".
Copying file from "bin\MyProject.exe.manifest" to "bin\app.publish\Application Files\MyProject_1_0_0_0\MyProject.exe.manifest".
Copying file from "bin\app.publish\MyProject.exe" to "bin\app.publish\Application Files\MyProject_1_0_0_0\MyProject.exe.deploy".
Copying file from "app.config" to "bin\app.publish\Application Files\MyProject_1_0_0_0\MyProject.exe.config.deploy".
Copying file from "obj\Debug\MyProject.pdb" to "bin\app.publish\Application Files\MyProject_1_0_0_0\MyProject.pdb.deploy".
1>Done Building Project
Basically, how do I get my target "CustomPostPublishActions" to run AFTER "_CopyFilesToPublishFolder"? Is it even possible? Any help is really appreciated!
There are predefined targets that can be overwritten to run before or after publish, update your script like this:
<Target Name="AfterPublish">
<!-- Create directory -->
<MakeDir Directories="$(ProjectDir)..\Deploy\Client\Application Files" />
<!-- Copy published website to deployment location -->
<ItemGroup>
<_CopyItems Include="$(ClickOnceBuildDirectory)\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(_CopyItems)" DestinationFolder="$(ProjectDir)..\Deploy\Client" />
<!-- Write publish.htm file for ClickOnce -->
<Copy SourceFiles="$(ProjectDir)\..\..\Build\publish.htm" DestinationFiles="$(ProjectDir)..\Deploy\Client\publish.htm" />
<FileUpdate Files="$(ProjectDir)..\Deploy\Client\publish.htm" Regex="{VERSION}" IgnoreCase="true" Multiline="true" Singleline="false" ReplacementText="$(ApplicationVersion)" />
</Target>
This link will give you an idea of the existing overridable targets and how you can work with them.

How to build code using MSBuild.exe from comand line without Visual Studio

Can you please explain step by step how to build code using command line without Visual Studio and generate DLL?
I am using Visual Studio 2010 SP1. I need sample file(.sln,.csproj) which is going to using with MSBuild and need command that I can compile code without Visual Studio and generate DLL.
Finally I got the solution.
Please do following steps to generate DLL from xml file without having Visual Studio IDE.
Basically, we require only two types files
1. class file like Helloworld.cs , Welcome.cs
2. XML file
Here below is the format of XML file which helps to generate DLL file
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- Generate DLL/Assembly Name -->
<AssemblyName>MSBuildSample</AssemblyName>
<OutputPath>Bin\</OutputPath>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data.Linq" />
</ItemGroup>
<ItemGroup>
<!-- Mentioned here class file to compile -->
<Compile Include="Helloworld.cs" />
<Compile Include="Welcome.cs" />
<!-- <Compile Include="C:\testing\test.Designer.cs" />
<EmbeddedResource Include="C:\testing\test.resx" /> -->
</ItemGroup>
<Target Name="Build">
<Csc Sources="#(Compile)"
Resources="#(EmbeddedResource)"
References="#(Reference)"
TargetType="library"
OutputAssembly="C:\testing\test.dll" />
</Target>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Save this file as MSBuild.xml in same folder where your class file reside and then you need to run following command from Command Line
MSBuild.exe MSBuild.xml /property:Configuration=Debug
This will helps you to compile and generate DLL from XML file without having Visual Studio.
This is very basic .xml file but you can complie and generate dll file from your project file (.csproj) file too. You just need to add following lines if not exists in that .csproj file.
<PropertyGroup>
<OutputType>Library</OutputType>
<OutputPath>Bin\</OutputPath>
</PropertyGroup>

msbuild transform one config few times

I have next config in my Web.config file
<Target Name="UpdateWebConfigForProjectsBeforeRun">
<ItemGroup>
<FilesToTransofm Include="ProjectsDeployBin\Web.*.$(Configuration).config"/>
</ItemGroup>
<Message Text="Transform file: %(FilesToTransofm.Identity)" />
<TransformXml Source="web.config"
Transform="%(FilesToTransofm.Identity)"
Destination="web.config" />
</Target>
What i am trying to do its get all configs from ProjectsDeployBin directory and apply each file to main web.config.
After first transformation main web.config locked by msbuild.
So how can i fix this issue? Is there any other way to transform my web.config by collection of files?
Thanks.
As you've noticed, the TransformXml task shipped with Visual Studio 2010 has a bug that leaves the source file locked.
To work around that, you can make a temporary copy of your source file before each transformation.
Since you'll then be executing multiple tasks for each transform file (copy and transform), you'll need to switch to Target Batching instead of Task Batching.
Example:
<ItemGroup>
<FilesToTransofm Include="ProjectsDeployBin\Web.*.$(Configuration).config"/>
</ItemGroup>
<Target Name="UpdateWebConfigForProjectsBeforeRun"
Inputs="#(FilesToTransofm)"
Outputs="%(Identity).AlwaysRun">
<Message Text="Transform file: %(FilesToTransofm.Identity)" />
<Copy SourceFiles="web.config"
DestinationFiles="web.pre-%(FilesToTransofm.Filename).temp.config" />
<TransformXml Source="web.pre-%(FilesToTransofm.Filename).temp.config"
Transform="%(FilesToTransofm.Identity)"
Destination="web.config" />
</Target>
From a quick test, it looks like this bug is fixed in Visual Studio 2012, but I'm not able to find a reference / source that documents that, and the original Connect bug isn't viewable anymore.

MSDeploy skip rules when using MSBuild PublishProfile with Visual Studio 2012

I'm trying to use WebDeploy to publish a website using custom MSDeploy skip rules and a publish profile saved in Visual Studio 2012.
I have the publish profile working from the command line, but the skip rule to skip deleting a folder isn't working.
I have an ErrorLog subfolder in my web app with a web.config file inside it to set the proper folder permissions. Without any skip rules, the ErrorLog folder and web.config file are published normally, but all existing error log files in the folder on the server are deleted on publish.
Error with <SkipAction>Delete</SkipAction>
When I add a custom skip rule to my wpp.targets file, the skip rule is no longer accepting a value for the <SkipAction> element. If I set <SkipAction>Delete</SkipAction>, I get the following error:
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.targets(4377,5): error : Web deployment task failed. (Unrecognized skip directive 'skipaction'. Must be one of the following: "objectName," "keyAttribute," "absolutePath," "xPath," "attributes.<name>.") [C:\inetpub\wwwroot\My.Website\My.Website\My.Website.csproj]
If I simply omit the <SkipAction> element, the ErrorLog folder is deleted when it would normally be published.
If I set <SkipAction></SkipAction>, again, the ErrorLog folder is deleted on publish.
If I set <KeyAttribute>Delete</KeyAttribute>, then ErrorLog and the web.config file are published normally.
My understanding is that in order to use custom skip rules, you need to call MSBuild from the command line instead of publishing from within VS 2012. I'd still like to use my saved publishing profiles, however, and I understand that's now possible as of VS 2012.
My MSBuild command line:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe My.Website.sln /p:Configuration=Release;DeployOnBuild=true;PublishProfile="Test Server - Web Deploy"
My.Website.wpp.targets:
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<AfterAddIisSettingAndFileContentsToSourceManifest>AddCustomSkipRules</AfterAddIisSettingAndFileContentsToSourceManifest>
</PropertyGroup>
<Target Name="AddCustomSkipRules">
<Message Text="Adding Custom Skip Rules" />
<ItemGroup>
<MsDeploySkipRules Include="SkipErrorLogFolder1">
<SkipAction></SkipAction>
<KeyAttribute>Delete</KeyAttribute>
<ObjectName>dirPath</ObjectName>
<AbsolutePath>$(_Escaped_WPPAllFilesInSingleFolder)\\ErrorLog$</AbsolutePath>
<XPath></XPath>
</MsDeploySkipRules>
</ItemGroup>
</Target>
</Project>
My MSBuild output showing the custom skip rule, but still deleting the files:
GenerateMsdeployManifestFiles:
Generate source manifest file for Web Deploy package/publish ...
AddCustomSkipRules:
Adding Custom Skip Rules
MSDeployPublish:
Start Web Deploy Publish the Application/package to http://testserver.domain.com/MSDEPLOYAGENTSERVICE ...
Starting Web deployment task from source: manifest(C:\inetpub\wwwroot\My.Website\My.Website\obj\Release\Package\My.Website.SourceManifest.xml) to Destination: auto().
Deleting filePath (MyWeb/ErrorLog\test.txt).
Updating setAcl (MyWeb/).
Updating setAcl (MyWeb/).
Updating filePath (MyWeb/ErrorLog\Web.config).
Updating filePath (MyWeb/Web.config).
Updating setAcl (MyWeb/).
Updating setAcl (MyWeb/).
Successfully executed Web deployment task.
Publish is successfully deployed.
Edit: It turns out you are right: the skip directive is ignored when executed from Visual Studio.
Fortunately, there's a workaround.
What you want is this:
<!-- Skip the deletion of any file within the ErrorLog directory -->
<MsDeploySkipRules Include="SkipErrorLogFolder1">
<SkipAction>Delete</SkipAction>
<ObjectName>filePath</ObjectName>
<AbsolutePath>ErrorLog</AbsolutePath>
</MsDeploySkipRules>
In addition, you need to prevent VS from using the UI-task (which appears to contain a bug regarding the skip rules). You can do this by declaring the following in your wpp.targets or pubxml:
<PropertyGroup>
<UseMsDeployExe>true</UseMsDeployExe>
</PropertyGroup>
I've tested this locally and I can confirm that it works as desired: the additional file is updated but no files in the directory are deleted.
For reference, here is my complete .wpp.targets file with working skip rule to skip deleting the ErrorLog folder and custom ACLs to make the ErrorLog folder writable on the server.
As of VS 2012 Update 3, this only works when publishing with MSBuild from the command line with the DeployOnBuild=true;PublishProfile="Test Server - Web Deploy" options passed to MSBuild. This will not work when publishing from within VS.
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<UseMsdeployExe>true</UseMsdeployExe> <!-- Required for the MSDeploySkipRules to work -->
<DeployManagedPipelineMode>Integrated</DeployManagedPipelineMode>
</PropertyGroup>
<PropertyGroup>
<AfterAddIisSettingAndFileContentsToSourceManifest>
$(AfterAddIisSettingAndFileContentsToSourceManifest);
AddCustomSkipRules;
</AfterAddIisSettingAndFileContentsToSourceManifest>
</PropertyGroup>
<Target Name="AddCustomSkipRules">
<Message Text="Adding Custom Skip Rules" />
<ItemGroup>
<MsDeploySkipRules Include="SkipErrorLogFolder">
<SkipAction>Delete</SkipAction>
<ObjectName>filePath</ObjectName>
<AbsolutePath>ErrorLog</AbsolutePath>
<XPath></XPath>
</MsDeploySkipRules>
</ItemGroup>
</Target>
<PropertyGroup>
<AfterAddIisSettingAndFileContentsToSourceManifest>
$(AfterAddIisSettingAndFileContentsToSourceManifest);
SetCustomACLs;
</AfterAddIisSettingAndFileContentsToSourceManifest>
<AfterAddDeclareParametersItemsForContentPath>
$(AfterAddDeclareParametersItemsForContentPath);
SetCustomAclParameters;
</AfterAddDeclareParametersItemsForContentPath>
</PropertyGroup>
<Target Name="SetCustomACLs">
<Message Text="Setting Custom ACLs" />
<ItemGroup>
<!--Make sure the application pool identity has write permission to the download folder-->
<MsDeploySourceManifest Include="setAcl"
Condition="$(IncludeSetAclProviderOnDestination) And Exists('$(_MSDeployDirPath_FullPath)\ErrorLog')">
<Path>$(_MSDeployDirPath_FullPath)\ErrorLog</Path>
<setAclAccess>Write</setAclAccess>
<setAclResourceType>Directory</setAclResourceType>
<AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<Target Name="SetCustomAclParameters">
<Message Text="Setting Custom ACL Parameters" />
<EscapeTextForRegularExpressions Text="$(_MSDeployDirPath_FullPath)">
<Output TaskParameter="Result" PropertyName="_EscapeRegEx_MSDeployDirPath" />
</EscapeTextForRegularExpressions>
<ItemGroup>
<MsDeployDeclareParameters Include="Add write permission to ErrorLog folder"
Condition="$(IncludeSetAclProviderOnDestination) and Exists('$(_MSDeployDirPath_FullPath)\ErrorLog')">
<Kind>ProviderPath</Kind>
<Scope>setAcl</Scope>
<Match>^$(_EscapeRegEx_MSDeployDirPath)\\ErrorLog$</Match>
<Description>Add write permission to ErrorLog folder</Description>
<DefaultValue>Default Web Site/ErrorLog</DefaultValue>
<Value>$(DeployIisAppPath)/ErrorLog</Value>
<Tags>Hidden</Tags>
<Priority>$(VsSetAclPriority)</Priority>
<ExcludeFromSetParameter>True</ExcludeFromSetParameter>
</MsDeployDeclareParameters>
</ItemGroup>
</Target>
</Project>
Another approach is to avoid the SkipAction tag, I've successfully used this setup directly from VS 2013:
<Target Name="AddCustomSkipRules"
AfterTargets="AddIisSettingAndFileContentsToSourceManifest">
<Message Text="Adding Custom Skip Rules" />
<ItemGroup>
<MsDeploySkipRules Include="SkipMedia">
<objectName>dirPath</objectName>
<absolutePath>media</absolutePath>
</MsDeploySkipRules>
<MsDeploySkipRules Include="SkipUpload">
<objectName>dirPath</objectName>
<absolutePath>upload</absolutePath>
</MsDeploySkipRules>
</ItemGroup>
</Target>
Only caveat as far as I can tell is that, it will ignore both update, delete and add operations.
After many hours looking through the net. i created this file as {myprojectname}.wpp.targets under the site root folder. it works when publishing with visual studio. the media folder is ignored. i am using VS 2010.
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<UseMsdeployExe>true</UseMsdeployExe>
<!-- Required for the MSDeploySkipRules to work -->
<DeployManagedPipelineMode>Integrated</DeployManagedPipelineMode>
</PropertyGroup>
<PropertyGroup>
<AfterAddIisSettingAndFileContentsToSourceManifest>
$(AfterAddIisSettingAndFileContentsToSourceManifest);
AddCustomSkipRules;
</AfterAddIisSettingAndFileContentsToSourceManifest>
</PropertyGroup>
<Target Name="AddCustomSkipRules">
<Message Text="Adding Custom Skip Rules - WPP Targets 2" />
<ItemGroup>
<MsDeploySkipRules Include="SkipErrorLogFolder">
<SkipAction>Delete</SkipAction>
<ObjectName>dirPath</ObjectName>
<AbsolutePath>media</AbsolutePath>
<XPath></XPath>
<Apply>Destination</Apply>
</MsDeploySkipRules>
</ItemGroup>
</Target>
</Project>
I think the problem is in incorrect AbsolutePath. It should be a regular expression to match file or folder. so it should be properly escaped. Below is the sample which worked for me (I wanted to skip removal of app_offline.htm to make delivery part of larger deployment)
<PropertyGroup>
<PackageUsingManifestDependsOn>$(PackageUsingManifestDependsOn);AddCustomSkipRules</PackageUsingManifestDependsOn>
</PropertyGroup>
<Target Name="AddCustomSkipRules">
<ItemGroup>
<MsDeploySkipRules Include="SkipAppOfflineOnDeploy">
<SkipAction></SkipAction>
<ObjectName>filePath</ObjectName>
<AbsolutePath>app_offline\.htm</AbsolutePath>
<Apply>Destination</Apply>
<XPath></XPath>
</MsDeploySkipRules>
</ItemGroup>
</Target>
Works for me: My Full prepprod.pubxml file in my App_Data/PublishProfiles folder in my web solution. Web Deploy no longer deletes files out of the cachefiles folder on webdeploy from VS 2015. The first PropertyGroup was auto-generated by using the web publishing gui in Visual Studio. I added the second PropertyGroup, and the Target section from previous comments.
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit http://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>MSDeploy</WebPublishMethod>
<LastUsedBuildConfiguration>Production</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish>{masked}</SiteUrlToLaunchAfterPublish>
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<ExcludeApp_Data>False</ExcludeApp_Data>
<MSDeployServiceURL>{masked}</MSDeployServiceURL>
<DeployIisAppPath>{masked}</DeployIisAppPath>
<RemoteSitePhysicalPath />
<SkipExtraFilesOnServer>False</SkipExtraFilesOnServer>
<MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
<MSDeployUseChecksum>true</MSDeployUseChecksum>
<EnableMSDeployBackup>True</EnableMSDeployBackup>
<UserName>{masked}</UserName>
<_SavePWD>True</_SavePWD>
<PublishDatabaseSettings>
<Objects xmlns="">
</Objects>
</PublishDatabaseSettings>
<ExcludeFilesFromDeployment>packages.config;*.bat;*.sln;*.suo,*.p4ignore</ExcludeFilesFromDeployment>
<ExcludeFoldersFromDeployment>packages;cachefiles;.ebextensions</ExcludeFoldersFromDeployment>
</PropertyGroup>
<PropertyGroup>
<AfterAddIisSettingAndFileContentsToSourceManifest>
$(AfterAddIisSettingAndFileContentsToSourceManifest);
AddCustomSkipRules;
</AfterAddIisSettingAndFileContentsToSourceManifest>
</PropertyGroup>
<Target Name="AddCustomSkipRules">
<Message Text="Adding Custom Skip Rules" />
<ItemGroup>
<MsDeploySkipRules Include="SkipcachefilesFolder">
<objectName>dirPath</objectName>
<absolutePath>cachefiles</absolutePath>
</MsDeploySkipRules>
</ItemGroup>
</Target>
</Project>
This worked for me in vs 2015, website project type:
<!--Added inside existing <ProjectGroup> tag-->
<AfterAddIisSettingAndFileContentsToSourceManifest>AddCustomSkipRules</AfterAddIisSettingAndFileContentsToSourceManifest>
<!--Added new ProjectGroup tag inside <Project></Project>-->
<PropertyGroup>
<WebPublishMethod>MSDeploy</WebPublishMethod>
</PropertyGroup>
<!--Added inside existing <Project> tag at the bottom-->
<Target Name="AddCustomSkipRules">
<Message Text="Adding Custom Skip Rules" />
<ItemGroup>
<MsDeploySkipRules Include="SkipConfigFolder">
<SkipAction></SkipAction>
<!--<KeyAttribute>Delete</KeyAttribute>-->
<ObjectName>dirPath</ObjectName>
<AbsolutePath>App_Data\\Composite\\Logfiles</AbsolutePath>
<XPath>
</XPath>
</MsDeploySkipRules>
</ItemGroup>
</Target>

How to use the new VS 2010 configuration transforms and apply them to other .config files?

I have setup some configuration transforms in my web.config for my connectionStrings, etc. But I have separated out some areas of my web.config into separate files, ex) appSettings.config.
How can I configure Visual Studio and MSBuild to perform config transformations on these additional config files?
I have already followed the approach of the web.config to relate the files together within my web application project file, but transformations are not automatically applied.
<ItemGroup>
<Content Include="appSettings.Debug.config">
<DependentUpon>appSettings.config</DependentUpon>
</Content>
</ItemGroup>
By default the target managing the transformation (TransformWebConfig) works only on web.config file.
To make it work on your appSettings.config file you'll have to :
Set the Build Action of your file to Content
Call the MSBuild target TransformWebConfig with ProjectConfigFileName=appSettings.config and Configuration=$(Configuration).
To call MSBuild TransformWebConfig target for appSettings.config just after the transformation of web.config files, you need to add this at the end of your project file :
<PropertyGroup>
<!-- Name of your custom config file -->
<ConfigFileName>appSettings.config</ConfigFileName>
</PropertyGroup>
<PropertyGroup>
<!--
This property is used to handle circular dependency between
TransformWebConfig and our custom target TransformAppConfig
-->
<FirstRun Condition="$(FirstRun) == ''">true</FirstRun>
</PropertyGroup>
<!-- This target will be called one time after a call to TransformWebConfig -->
<Target Name="TransformAppConfig"
AfterTargets="TransformWebConfig"
Condition="$(FirstRun) == 'true'">
<MSBuild Projects="$(MSBuildProjectFile)"
Targets="TransformWebConfig"
Properties="ProjectConfigFileName=$(ConfigFileName);
Configuration=$(Configuration);
FirstRun=false"/>
</Target>
<!--
This target will be called one time before PreAutoParameterizationWebConfigConnectionStrings
to add $(ConfigFileName) to autoparameterization step
-->
<Target Name="AddToAutoParameterizationStep"
BeforeTargets="PreAutoParameterizationWebConfigConnectionStrings">
<ItemGroup>
<_WebConfigsToAutoParmeterizeCS Include="#(FilesForPackagingFromProject)"
Condition="('%(FilesForPackagingFromProject.Filename)%(FilesForPackagingFromProject.Extension)'=='$(ConfigFileName)') And !%(FilesForPackagingFromProject.Exclude)">
<TransformOriginalFile>$(AutoParameterizationWebConfigConnectionStringsLocation)\original\%(DestinationRelativePath)</TransformOriginalFile>
<TransformOutputFile>$(AutoParameterizationWebConfigConnectionStringsLocation)\transformed\%(DestinationRelativePath)</TransformOutputFile>
<TransformScope>$(_PackageTempDir)\%(DestinationRelativePath)</TransformScope>
</_WebConfigsToAutoParmeterizeCS>
<_WebConfigsToAutoParmeterizeCSOuputFiles Include="#(_WebConfigsToAutoParmeterizeCS->'%(TransformOutputFile)')">
</_WebConfigsToAutoParmeterizeCSOuputFiles>
</ItemGroup>
</Target>
Something that makes this a lot easier, take a look at the SlowCheetah VS add-in at ... visualstudiogallery
Here is the code that works for me:
<PropertyGroup>
<!-- Name of your custom config file -->
<ConfigFileName>ConnectionStrings.config</ConfigFileName>
<ConfigTransformFileName>ConnectionStrings.$(Configuration).config</ConfigTransformFileName>
</PropertyGroup>
<PropertyGroup>
<!--
This property is used to handle circular dependency between
TransformWebConfig and our custom target TransformAppConfig
-->
<FirstRun Condition="$(FirstRun) == ''">true</FirstRun>
</PropertyGroup>
<Target Name="AddConfigToTransform" AfterTargets="CollectWebConfigsToTransform">
<ItemGroup>
<WebConfigsToTransform Include="#(FilesForPackagingFromProject)" Condition="'%(FilesForPackagingFromProject.Filename)%(FilesForPackagingFromProject.Extension)'=='$(ConfigFileName)'">
<TransformFile>%(RelativeDir)$(ConfigTransformFileName)</TransformFile>
<TransformOriginalFile>$(TransformWebConfigIntermediateLocation)\original\%(DestinationRelativePath)</TransformOriginalFile>
<TransformOutputFile>$(TransformWebConfigIntermediateLocation)\transformed\%(DestinationRelativePath)</TransformOutputFile>
<TransformScope>$([System.IO.Path]::GetFullPath($(_PackageTempDir)\%(DestinationRelativePath)))</TransformScope>
</WebConfigsToTransform>
</ItemGroup>
</Target>
<!--
This target will be called one time before PreAutoParameterizationWebConfigConnectionStrings
to add $(ConfigFileName) to autoparameterization step
-->
<Target Name="AddToAutoParameterizationStep" BeforeTargets="PreAutoParameterizationWebConfigConnectionStrings">
<ItemGroup>
<_WebConfigsToAutoParmeterizeCS Include="#(FilesForPackagingFromProject)" Condition="('%(FilesForPackagingFromProject.Filename)%(FilesForPackagingFromProject.Extension)'=='$(ConfigFileName)') And !%(FilesForPackagingFromProject.Exclude)">
<TransformOriginalFile>$(AutoParameterizationWebConfigConnectionStringsLocation)\original\%(DestinationRelativePath)</TransformOriginalFile>
<TransformOutputFile>$(AutoParameterizationWebConfigConnectionStringsLocation)\transformed\%(DestinationRelativePath)</TransformOutputFile>
<TransformScope>$(_PackageTempDir)\%(DestinationRelativePath)</TransformScope>
</_WebConfigsToAutoParmeterizeCS>
<_WebConfigsToAutoParmeterizeCSOuputFiles Include="#(_WebConfigsToAutoParmeterizeCS->'%(TransformOutputFile)')">
</_WebConfigsToAutoParmeterizeCSOuputFiles>
</ItemGroup>
</Target>

Resources