MsDeploySkipRules in wpp.targets not working with PublishProfiles - asp.net

I am publishing my MVC project with the PublishProfile through visual studio, (through UI, right click project, publish) and ticking the option to clear the destination folder.
But I do not want a specific folder "Downloads" to be cleared
I have spent countless hours trying to make this work, I think I have the exact same code as the person explained here but it still deletes the Downloads folder
Also as example in the below I have the ExcludeFromPackageFiles of "favicon" which works if I deselect the deletion of the destination folder (just to show that my wpp targets is in fact running).
Below is my projectname.wpp.targets file
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- this doesnt work -->
<PropertyGroup>
<AfterAddIisSettingAndFileContentsToSourceManifest>AddCustomSkipRules</AfterAddIisSettingAndFileContentsToSourceManifest>
</PropertyGroup>
<PropertyGroup>
<UseMsDeployExe>true</UseMsDeployExe>
</PropertyGroup>
<Target Name="AddCustomSkipRules">
<Message Text="Adding Custom Skip Rules" />
<ItemGroup>
<MsDeploySkipRules Include="SkipErrorLogFolder1">
<SkipAction>Delete</SkipAction>
<ObjectName>filePath</ObjectName>
<AbsolutePath>ErrorLog</AbsolutePath>
</MsDeploySkipRules>
</ItemGroup>
</Target>
<!-- this works! -->
<ItemGroup>
<ExcludeFromPackageFiles Include="favicon.ico">
<FromTarget>ContactManager.Mvc.wpp.targets</FromTarget>
</ExcludeFromPackageFiles>
</ItemGroup>
</Project>
Any Ideas?

(1) The AbsolutePath value in your example is copy/pasted from my skip rule example. You need to change that value to the path of your Downloads folder.
<Target Name="AddCustomSkipRules">
<Message Text="Adding Custom Skip Rules" />
<ItemGroup>
<MsDeploySkipRules Include="SkipDownloadsFolder">
<SkipAction>Delete</SkipAction>
<ObjectName>filePath</ObjectName>
<AbsolutePath>Downloads</AbsolutePath>
</MsDeploySkipRules>
</ItemGroup>
</Target>
(2) You cannot use a custom skip rule when publishing from within Visual Studio. You must publish using MSBuild from a command prompt. The nature of my question was about combining the convenience of the publishing profile managed within VS, with the utility of a custom skip rule (which requires the command line), because as of VS 2012 Update 3, the limitation of the command line has not been lifted.
My MSBuild command looks like this:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe My.Website.sln /p:Configuration=Release;DeployOnBuild=true;PublishProfile="Test Server - Web Deploy"
If I attempt to publish from within VS 2012, I get the following error, even though "-verb:sync" is clearly visible in the output:
2>Start Web Deploy Publish the Application/package to http://my.website.example.com/MSDEPLOYAGENTSERVICE ...
2>C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe
-source:manifest='C:\inetpub\wwwroot\My.Website\obj\Release\Package\My.Website.SourceManifest.xml'
-dest:auto,ComputerName='http://my.website.example.com/MSDEPLOYAGENTSERVICE',UserName='...',Password="...",IncludeAcls='False',AuthType='NTLM'
-verb:sync
-disableLink:AppPoolExtension
-disableLink:ContentExtension
-disableLink:CertificateExtension
-skip:skipaction='Delete',objectname='filePath',absolutepath='ErrorLog'
-skip:objectname='dirPath',absolutepath='obj\\Release\\Package\\PackageTmp\\App_Data$'
-skip:objectname='dirPath',absolutepath='MyWebsite/\\App_Data$'
-setParamFile:"C:\inetpub\wwwroot\My.Website\obj\Release\Package\My.Website.Publish.Parameters.xml"
-retryAttempts=2
2>MSDEPLOY(0,0): Error : The verb must be specified by using the -verb argument.
2>MSDEPLOY(0,0): Error count: 1.

Related

How to sign binaries during publishing a single file dotnet core app?

I would like to sign binaries that go inside a single-file published .net core application. This is because I would like the libraries, when unpacked into %temp%\.net\%app_name%\%random_dir%, to be digitally signed. Here's my shortened version of the project file.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<PublishTrimmed>false</PublishTrimmed>
<PublishReadyToRun>false</PublishReadyToRun>
<Configuration>Release</Configuration>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
</PropertyGroup>
<Target Name="SignPrePublishedFiles" AfterTargets="ComputeAndCopyFilesToPublishDirectory">
<ItemGroup>
<FileToSign Include="$(OutDir)Foo.*.dll" />
</ItemGroup>
<Exec Command="jsign ~~~params removed for brevity~~~ %(FileToSign.Identity)" />
</Target>
</Project>
The SignPrePublishedFiles target signs required files in the $(OutDir) dir, but the published app contains unsigned binaries. I think this is because the incorrect timing of: AfterTargets="ComputeAndCopyFilesToPublishDirectory" or incorrect folder, I assumed $(OutDir) is used for publishing. Here, I use jsign because the build runs on Linux, but the signing is done for Windows binaries.
How do you sign your published files, specifically those inside the single-file app?
You can sign the output dll and apphost wrapper for a project while it's in obj/ - this is what eventually gets published. Try something like this:
<Target Name="SignIntermediates" BeforeTargets="GetCopyToOutputDirectoryItems">
<ItemGroup>
<FileToSign Include="$(IntermediateOutputPath)$(TargetFileName)" />
<FileToSign Condition="'$(TargetExt)' == '.dll' And '$(OutputType)' == 'WinExe'" Include="$(IntermediateOutputPath)apphost.exe" />
</ItemGroup>
<Exec Command="jsign ~~~params removed for brevity~~~ %(FileToSign.Identity)" />
</Target>
GetCopyToOutputDirectoryItems is the last public target before apphost.exe is copied and renamed to $(TargetName).exe.

.wpp.targets file is ignored during build

I'm trying to apply custom ACLs using webdeploy (set Write permission to a folder - the folder is empty but added to the project and correctly deployed).
I'm building a VS solution on my CI server with the following command line:
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
/consoleloggerparameters:ErrorsOnly
/maxcpucount
/nologo
/property:Configuration=Release
/p:Platform=x86
/verbosity:quiet
MySolution.sln
/p:DeployOnBuild=True
/p:PublishProfile="ci"
/p:ProfileTransformWebConfigEnabled=False
/p:WebPublishPipelineCustomizeTargetFile="MyProject\MyProject.wpp.targets"
It builds correctly but it completely ignores the content of my MyProject.wpp.targets. I tried to write wrong XML but it gets silently ignored, the build is successful but clearly without my wpp.targets applied. The /p:WebPublishPipelineCustomizeTargetFile parameter doesn't have any effect.
This is the content of MyProject.wpp.targets:
<?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)\Content\cache</Path>
<setAclAccess>Read,Write</setAclAccess>
<setAclResourceType>Directory</setAclResourceType>
<AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<Target Name="DeclareCustomParameters" AfterTargets="AddIisAndContentDeclareParametersItems">
<ItemGroup>
<MsDeployDeclareParameters Include="ContentCacheSetAclParam">
<Kind>ProviderPath</Kind>
<Scope>setAcl</Scope>
<Match>^$(_EscapeRegEx_MSDeployDirPath)\\Content\\cache$</Match>
<Description>Add write permission to the Content/cache folder.</Description>
<DefaultValue>{$(_MsDeployParameterNameForContentPath)}/Content/cache</DefaultValue>
<Value>$(_DestinationContentPath)/Content/cache</Value>
<Tags>Hidden</Tags>
<Priority>$(VsSetAclPriority)</Priority>
<ExcludeFromSetParameter>True</ExcludeFromSetParameter>
</MsDeployDeclareParameters>
</ItemGroup>
</Target>
</Project>
The project is a net641 csproj <Project Sdk="Microsoft.NET.Sdk.Web">.
What's wrong with my configuration? Thank you for your help.

How to prevent deleting of a folder while publishing an ASP.Net project

I'm working on an ASP.NET Project which can be published on a server by right-clicking the project in the project explorer of Visual Studio and selecting "Publish".
On the server is a folder called "pictures" which shall not be removed while publishing.
So i added a wpp.targets file to my project (in the same folder as the csproj file), but it seems to be that this file doesn't work when i use the publish function of VS.
What can be the cause for it?
This is my wpp.targets file:
<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<IncludeSetACLProviderOnDestination>False</IncludeSetACLProviderOnDestination>
</PropertyGroup>
<Target Name="AddCustomSkipRules">
<Message Text="Adding Custom Skip Rules" />
<ItemGroup>
<MsDeploySkipRules Include="SkipDeletingPicturesFolder">
<SkipAction>Delete</SkipAction>
<ObjectName>filePath</ObjectName>
<AbsolutePath>pictures</AbsolutePath>
</MsDeploySkipRules>
</ItemGroup>
</Target>
</Project>
drag and drop a file into the pictures folder in solution explorer.then publish. your folder will now be available after publishing. I usually just drop a random image into the folder. remember the file has to be dropped into the folder in the solution explorer and not using your windows file explorer. having a file in your folder basically tell that this folder needs to be published.

How to get aspnet_compiler invoked from Visual Studio during build?

I want Visual Studio to precompile my ASP.NET application which is used as an Azure web role payload. So I've found this post that explains how to call aspnet_compiler to validate views.
I tried to add the following to "post-build event" of my ASP.NET application:
call "%VS100COMNTOOLS%\vsvars32.bat"
aspnet_compiler -v / -p $(ProjectDir)
or alternatively this (application name specified explicitly):
call "%VS100COMNTOOLS%\vsvars32.bat"
aspnet_compiler -v /ASP.NET-Application-ProjectNameHere -p $(ProjectDir)
In both cases when the build runs I see the following in the build output:
Setting environment for using Microsoft Visual Studio 2010 x86 tools.
Utility to precompile an ASP.NET application
Copyright (C) Microsoft Corporation. All rights reserved.
and clearly no precompilation happens because if I change any .aspx or .cshtml file "Build Action" to "None" it doesn't get to the Azure service package and the view no longer opens once the package is deployed to Azure.
How do I setup aspnet_compiler for precompiling from within Visual Studio?
If you want to use Asp.NET Compiler within your Visual Studio / msbuild then you can add
AspNetCompiler Task to your project file (.csproj/.vbproj) and set MvcBuildViews to true.
Example:
<Project>
<PropertyGroup>
<MvcBuildViews>true</MvcBuildViews>
</PropertyGroup>
<!-- ... -->
<Target Name="PrecompileWeb" AfterTargets="build" Condition="'$(MvcBuildViews)'=='true'">
<Message Text="Starting AspNetCompiler for $(ProjectDir)" Importance="high" />
<AspNetCompiler
VirtualPath="temp"
PhysicalPath="$(WebProjectOutputDir)"
Force="true"
/>
</Target>
<!-- ... -->
</Project>
You may also set TargetPath attribute to specify destination directory.
AfterTargets="build" is similar to "post-build event". See Target Build Order for more.
Integrate ASPX compilation into Visual Studio
One of the principles I insist on is to always try my build on a clean environment and simulate installation as if it was done by QA. Lately I've noticed that I keep falling on errors hidden deep in the aspx files. So, why not using the old and familiar aspnet_compiler.exe tool? It is located at C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727 and it is quite easy to use.
As a VS add-ins freak I've started thinking on an amazing add-in that will integrate to the VS and will listen to build events and display the results at the output pane. Heck, why not add some coffee serving capabilities?
It took me about 10 minutes of googling to stumble on this blog. Mike Hadlow had a genius in its simplicity idea. Use the POST BUILD EVENT!
All I need to do is put the following line in the post build event: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_compiler.exe -v / -p "$(ProjectDir)\"
Now, All that is left is to make the process of adding this line to each and every web project in our team to be automatic.
I have just the add-in for that :)
enter link description here
The answer from Matej was helpful for me, but I was not able to use it as-is and still get it to work for both local builds within Visual Studio and automated builds via TFS.
I had to add some extra msbuild settings. Actually, there were 2 different scenarios that I had. One project was an Web App that built into the _PublishedWebsites folder and one was an MVC Web App that did not build into the _PublishedWebsites folder.
First, add the following if it is not already in your project file:
<PropertyGroup>
<MvcBuildViews>true</MvcBuildViews>
</PropertyGroup>
For the one WITH _PublishedWebsites:
<Choose>
<When Condition="'$(BuildingInsideVisualStudio)' == true">
<PropertyGroup>
<AspNetCompilerPhysicalPath>$(ProjectDir)</AspNetCompilerPhysicalPath>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<AspNetCompilerPhysicalPath>$(WebProjectOutputDir)</AspNetCompilerPhysicalPath>
</PropertyGroup>
</Otherwise>
</Choose>
<Target Name="PrecompileWeb" AfterTargets="build" Condition="'$(MvcBuildViews)'=='true'">
<!-- aspnet_compiler.exe needs to be run on the folder that has the aspx files and the "bin" subfolder.
When running locally, the value needs to be the project directory, which is $(ProjectDir).
When running the TFS build, the value needs to be (BuildFolder)\(ProjectName)\_PublishedWebsites\(ProjectName).
The $(AspNetCompilerPhysicalPath) will hold the correct value for both types of builds.
-->
<Message Text="Starting AspNetCompiler for $(ProjectName) at $(AspNetCompilerPhysicalPath)" Importance="high" />
<AspNetCompiler
VirtualPath="/"
PhysicalPath="$(AspNetCompilerPhysicalPath)"
TargetPath="$(AspNetCompilerPhysicalPath)\bin_precompile"
Force="true"
/>
</Target>
For the one WITHOUT _PublishedWebsites:
<Choose>
<When Condition="'$(BuildingInsideVisualStudio)' == true">
<PropertyGroup>
<AspNetCompiler_CopyFilesFirst>false</AspNetCompiler_CopyFilesFirst>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<AspNetCompiler_CopyFilesFirst>true</AspNetCompiler_CopyFilesFirst>
</PropertyGroup>
<ItemGroup>
<AllOutputFiles Include="$(OutDir)\\**\*.*" />
</ItemGroup>
</Otherwise>
</Choose>
<Target Name="PrecompileWeb" AfterTargets="build" Condition="'$(MvcBuildViews)'=='true'">
<!-- aspnet_compiler.exe needs to be run on the folder that has the cshtml files and the "bin" subfolder. I could not find a setting that was appropriate for both.
When running locally, the value needs to be the project directory, which is $(ProjectDir).
When running the TFS build, there is no folder that matches both of those criteria.
So first we will copy the output into the source code folder's "bin" subfolder,
then run it against the source $(ProjectDir), the same as if we were building locally.
-->
<Message Text="Before running AspNetCompiler, copy files from $(OutDir) to $(ProjectDir)\bin" Importance="high" />
<Exec Command="( robocopy.exe /mir $(OutDir) $(ProjectDir)\bin ) ^& IF %25ERRORLEVEL%25 LEQ 1 exit 0" Condition="'$(AspNetCompiler_CopyFilesFirst)'=='true'" />
<Message Text="Starting AspNetCompiler for $(ProjectName) at $(ProjectDir)" Importance="high" />
<AspNetCompiler
VirtualPath="/"
PhysicalPath="$(ProjectDir)"
TargetPath="$(ProjectDir)\bin_precompile"
Force="true"
/>
</Target>

How do you use Web.config Transformation?

When I create a deployment package web.config is changed, but I don't understand this part:
I have two web.config transformation files, web.debug.config and web.release.config.
Are these transformation files only available or working when we make a web deployment or make a deployment package? Are the web.config transformations not used when the project runs locally from visual studio (e.g. via IIS Express)?
You are correct.
Config transformations are applied when you deploy or run a deployment package.
They do not transform on compilation.
If you need the transformed config file during compilation, you can get it by editing the project file (.csproj) and adding the below code.
<Target Name="AfterBuild">
<TransformXml Source="$(SolutionDir)WCFServices\Web.config"
Transform="$(SolutionDir)WCFServices\Web.Release.config"
Destination="$(OutDir)WebRelease.config"
StackTrace="true" />
</Target>
Multiple TransformXml tags can be added to get all the required config files. Also, This can be done before or after build.
You can invoke it using MSBuild and an extension called SlowCheetah.
There is an other VS extension called Configuration Transform good for this. If you don't want to install it, but to achieve this, just follow the examples shown in the demo solution to add different build config files and add some new MSBuild tasks in the project files. The download link for the demo solution can be found on the extension's Visual Studio Gallery webpage. This approach doesn't require any extra packages since MSBuild uses XSLT to do the XML tranformation.
Below are the MSBuild tasks added into a project file from the demo solution. In my case, when I followed it for a VS2015 ASP.NET MVC project, I didn't have to put <UsingTask TaskName="TransformXml" AssemblyFile=... in.
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterCompile" Condition="Exists('App.$(Configuration).config')">
<!--Generate transformed app config in the intermediate directory-->
<TransformXml Source="App.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="App.$(Configuration).config" />
<!--Force build process to use the transformed configuration file from now on.-->
<ItemGroup>
<AppConfigWithTargetPath Remove="App.config" />
<AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
<TargetPath>$(TargetFileName).config</TargetPath>
</AppConfigWithTargetPath>
</ItemGroup>
</Target>
<!--Override After Publish to support ClickOnce AfterPublish. Target replaces the untransformed config file copied to the deployment directory with the transformed one.-->
<Target Name="AfterPublish">
<PropertyGroup>
<DeployedConfig>$(_DeploymentApplicationDir)$(TargetName)$(TargetExt).config$(_DeploymentFileMappingExtension)</DeployedConfig>
</PropertyGroup>
<!--Publish copies the untransformed App.config to deployment directory so overwrite it-->
<Copy Condition="Exists('$(DeployedConfig)')" SourceFiles="$(IntermediateOutputPath)$(TargetFileName).config" DestinationFiles="$(DeployedConfig)" />
</Target>
Here is the way I applied in my .csproj file, quite simple:
<Target Name="AfterBuild" Condition="Exists('Web.$(Configuration).config')">
<Exec Command="attrib -R Web.config" />
<TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="Web.config" StackTrace="true" />
</Target>
Also there is a good post on this.
Further, for web.config transformation, Since VS2012 we can add a publish profile - Publish.pubxml (ProjectFolder/Properties/PublishProfiles/Publish.pubxml) to do a FileSystem publish, thus the web.config transformation will happen by default then. Below is a sample
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<SiteUrlToLaunchAfterPublish />
<publishUrl Condition="$(OutDir) != ''">$(OutDir)\_PublishedWebsites\$(ProjectName)</publishUrl> <!-- For MSBuild -->
<publishUrl Condition="$(OutDir) == ''">$(MSBuildThisFileDirectory)..\..\_PublishedWebsite\</publishUrl> <!-- For Visual Studio...cant use $(ProjectName) -->
<DeleteExistingFiles>True</DeleteExistingFiles>
</PropertyGroup>
</Project>

Resources