Can Web Deploy's setAcl provider be used on a sub-directory? - visual-studio-2010

I'm trying to make a subdirectory in an MS Deploy package writable to the application pool user. Thanks to a helpful post about the setAcl provider by Kevin Leetham I was able to get most of what I need into my project file:
<MsDeploySourceManifest Include="setAcl"
Condition="$(IncludeSetAclProviderOnDestination)">
<Path>$(_MSDeployDirPath_FullPath)\doc\public</Path>
<setAclAccess>Read,Write,Modify</setAclAccess>
<setAclResourceType>Directory</setAclResourceType>
<AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
</MsDeploySourceManifest>
Note that I've added "\doc\public" to the root deployment directory. In the resulting manifest that VS2010 builds, I see the following setAcl element:
<sitemanifest>
<contentPath path="C:\Source\...\obj\Debug\Package\PackageTmp" />
<setAcl path="C:\Source\...\obj\Debug\Package\PackageTmp"
setAclResourceType="Directory" />
<setAcl path="C:\Source\...\obj\Debug\Package\PackageTmp"
setAclUser="anonymousAuthenticationUser"
setAclResourceType="Directory" />
<setAcl path="C:\Source\...\obj\Debug\Package\PackageTmp\doc\public"
setAclResourceType="Directory"
setAclAccess="Read,Write,Modify" />
</sitemanifest>
That last line looks good: it's appended the subdirectory I want to be writable, and the access modifiers all seem to have transferred over well enough.
However, when I deploy this package I receive an error:
Error: A value for the 'setAclUser' setting must be specified when the
'setAcl' provider is used with a physical path.
This is a confusing error because I'm not trying to set an ACL on a physical path, exactly, but a subdirectory of a web application. Looking at the output of MS Deploy, it's easy to see the problem:
Info: Adding setAcl (REST Services\1.0.334).
Info: Adding setAcl (REST Services\1.0.334).
Info: Adding setAcl (C:\...\obj\Release\Package\PackageTmp\doc\public).
MS Deploy is apparently substituting the web application name for my absolute path "C:...\obj\Release\Package\PackageTmp", but when I append "\doc\public" to that absolute path it no longer recognizes it as a web application directory. This exact problem is described by another victim over on the ASP.NET forums without any resolution.
Does anyone know how to set an ACL on a particular subdirectory of a web application via Web Deploy without manually identifying the physical path and application pool user on the target host?

OK let me first say that this is way harder than it should be!
I think the reason why it is failing is because when you are publishing it cannot recognize the folder as being a folder in the IIS Application. The reason this is happening is because the full path is being transferred to the destination when the SetAcl provider is invoked. Instead of that we need an path which is relative to the IIS Application. For instance in your case it should be something like : "REST SERVICES/1.0.334/doc/public". The only way to do this is to create an MSDeploy parameter which gets populated with the correct value at publish time. You will have to do this in addition to creating your own SetAcl entry in the source manifest. Follow the steps below.
In the same directory as your project create a file with the name {ProjectName}.wpp.targets (where {ProjectName} is the name of your Web application project)
Inside the file paste the MSBuild content which is below this list
Reload the project in Visual Studio (VS caches the project files in memory so this cache needs to be cleared).
{ProjectName}.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">
<!-- This must be declared inside of a target because the property
$(_MSDeployDirPath_FullPath) will not be defined at that time. -->
<ItemGroup>
<MsDeploySourceManifest Include="setAcl">
<Path>$(_MSDeployDirPath_FullPath)\doc\public</Path>
<setAclAccess>Read,Write,Modify</setAclAccess>
<setAclResourceType>Directory</setAclResourceType>
<AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<Target Name="DeclareCustomParameters" AfterTargets="AddIisAndContentDeclareParametersItems">
<!-- This must be declared inside of a target because the property
$(_EscapeRegEx_MSDeployDirPath) will not be defined at that time. -->
<ItemGroup>
<MsDeployDeclareParameters Include="DocPublicSetAclParam">
<Kind>ProviderPath</Kind>
<Scope>setAcl</Scope>
<Match>^$(_EscapeRegEx_MSDeployDirPath)\\doc\\public$</Match>
<Value>$(_DestinationContentPath)/doc/public</Value>
<ExcludeFromSetParameter>True</ExcludeFromSetParameter>
</MsDeployDeclareParameters>
</ItemGroup>
</Target>
</Project>
To explain this a bit, the target SetupCustomAcls will cause a new SetAcl entry to be placed inside of the source manifest used during publishing. This target is executed after the AddIisSettingAndFileContentsToSourceManifest target executes, via the AfterTargets attribute. We do this to ensure that the item value is created at the right time and because we need to ensure that the property _MSDeployDirPath_FullPath is populated.
The DeclareCustomParameters is where the custom MSDeploy parameter will be created. That target will execute after the AddIisAndContentDeclareParametersItems target. We do this to ensure that the property _EscapeRegEx_MSDeployDirPath is populated. Notice inside that target when I declare the value of the parameter (inside the Value element) that I use the property _DestinationContentPath which is the MSBuild property containing the path to where your app is being deployed, i.e. REST Services/1.0.334.
Can you try that out and let me know if it worked for you or not?

FYI - this does work for a root website if you follow the convention specified in the post here:
http://forums.iis.net/p/1176955/1977169.aspx#1977169
<Match>^$(_EscapeRegEx_MSDeployDirPath)\\#(CustomDirAcl)$</Match>
<DefaultValue>{$(_MsDeployParameterNameForContentPath)}/#(CustomDirAcl)</DefaultValue>
<Value>$(_DestinationContentPath)/#(CustomDirAcl)</Value>
This post also has the benefit of being able to specify a block of subdirectories in a single ItemGroup.

Related

Build target in the Publish Profile is not executing even if the Publish is successful

I have a publish profile Staging.pubxml created for a console project (.NET Core 2.1), as follows:
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Configuration>Staging</Configuration>
<Platform>Any CPU</Platform>
<TargetFramework>netcoreapp2.1</TargetFramework>
<PublishDir>bin\Release\netcoreapp2.1\publish\</PublishDir>
<SelfContained>false</SelfContained>
<_IsPortable>true</_IsPortable>
</PropertyGroup>
<Target Name="CustomActionsAfterPublish" AfterTargets="AfterPublish">
<Message Text="First occurrence" Importance="high" />
</Target>
</Project>
When I publish it (via VS2019); publish completes successfully. Yet I'am not seeing the text: First occurrence in the output window.
In VS, MSBuild output verbosity is set to Diagnostic.
Am I doing this wrong?
Am I doing this wrong?
1.Hi friend, the .pubxml file is not a good place to define custom Target.
According to your target name, you want to run the target after build target. For this situation: always we define the custom target in project file. Please try right-click your project and choose edit xxx.csproj, and add the target into it. Then reload the project and publish, then you can find the First occurrence message in the build output.
And after my test, in.csproj file, the target can't be named AfterBuild, you can change it to:
<Target Name="CustomTargetName" AfterTargets="build">
<Message Text="First occurrence" Importance="high" />
</Target>
2.Also, the way you use can work in VS2019 though we don't suggest adding it in .pubxml. But it indeed works. For the reason why you can't get the message in output window, please check if the Staging.pubxml is really called during the publish process.
(Change the value of the PublishDir to another folder, and publish it again, check if the project is published to new directory, then you'll find if the xxx.pubxml is used)
Update:
According to your .pubxml file, your project is a .net core console app instead of a asp.net core web app. This makes the difference!
I test it in both VS2017 and VS2019, and find the target works well in asp.net web app but not .net core console one. I believe for those non-web app, this kind of after publish target may not be supported.
And please check these two official documents I found:
Asp.net core: Run a target before or after publishing
.net core: Deploy .net core apps with VS
I can't find any custom-target-related info in the .net core document. So I'm afraid it might be not supported by design.
Update2:
Actually your original need is to add CopyFiles target to publish process. So I assume what you really want is to copy sth to Publish Folder after Publish process or copy sth from Publish Folder to target folder.
Though the after publish target is not supported in .net core console apps. We have corresponding workarounds:
To copy sth to Publish Folder after Publish:
For this situation, you can add a after build target to copy the files to Publish folder before publish process. I think it works for this situation:
<Target Name="CustomTarget" AfterTargets="build">
<ItemGroup>
<MySourceFiles Include="xxx\pathToTheFolderWhereYourFilesAre\*.*"/>
</ItemGroup>
<Copy
SourceFiles="#(MySourceFiles)"
DestinationFolder="$(ProjectDir)bin\Release\netcoreapp2.1\publish\"
/>
</Target>
To copy sth from Publish Folder to target folder:
For .net core console app, actually the so-called publish process is to copy files from obj folder to publish folder. See this screenShot:
Go Tools=>Options=>Project and Solutions=>Build and Run to change the build output verbosity to Detailed and you can see details in build and publish process in VS.
That's why I suggest you can Go Project=>Properties=>build events=>post-build events to use a xcopy command to copy the files to your destination folder.(Or you can use a script like above, remember to change the source path and destination path). When we want to copy the files in publish folder, actually we're trying to copy the files from obj folder. So do something after build process is enough! Not need to do it after publish process.

Config transformation with Webdeploy, Preview not working

I want to use config transformation for custom files, if I replace ??? with MSDeployPublish, the Preview functionality is not working and it replaces the local files in solution. However the publish works without using Preview keeping local files untouched.
Config files:
AppSettings.config
└AppSettings.Test.config
└AppSettings.Stage.config
└AppSettings.Release.config
ConnectionString.config
└ConnectionString.Test.config
└ConnectionString.Stage.config
└ConnectionString.Release.config
What is the correct name of the target to use transformation for preview (without changing the files in soulution)? The way that web.config works when making preview with webDeploy
<Target Name="???">
<TransformXml Source="App_Config\AppSettings.config" Destination="App_Config\AppSettings.config" Transform="App_Config\AppSettings.$(Configuration).config" />
<TransformXml Source="App_Config\ConnectionStrings.config" Destination="App_Config\ConnectionStrings.config" Transform="App_Config\ConnectionStrings.$(Configuration).config" />
</Target>
I don't want to use any extensions like SlowCheetah, just build in functionalities.
<Target Name="Build"> also works
UPDATE 1
I've succeeded to publish without change local config files:
Destination="$(_PackageTempDir)\App_Config\ConnectionStrings.config"
But the Preview still not transforming the configs (also when I define a message for output it is not appear). What black magic happens when I click "Preview", in the publish screen ?
I've tried with targets: TransformWebConfigCore,CopyAllFilesToSingleFolderForPackage ,GatherAllFilesToPublish
I've made it.
The Target name should be a custom name that doesn't exist. Then AfterTargets attribute should be specified with value of Package. This target happens almost at the end of the chain and ensures that the $(_PackageTempDir) directory is created.
The transformations are done in the package directory. This way ensures when making a preview with webdeploy it will compare files correctly and without change local files in source control.
Here is the definition:
<Target Name="CustomConfigTransform" AfterTargets="Package">
<TransformXml Source="App_Config\AppSettings.config" Destination="$(_PackageTempDir)\App_Config\AppSettings.config" Transform="App_Config\AppSettings.$(Configuration).config" />
<TransformXml Source="App_Config\ConnectionStrings.config" Destination="$(_PackageTempDir)\App_Config\ConnectionStrings.config" Transform="App_Config\ConnectionStrings.$(Configuration).config" />
</Target>
Of course there could be a better way: in the target CollectWebConfigsToTransform from Microsoft.Web.Publishing.targets, it should know somehow that there are more config files for transform. But currently no idea.

setAcl in wpp.targets not performed upon Publish - Web Deploy (but says it is)

I have a web forms application (Visual Studio 2010) with an existing wpp.targets file working successfully to do things like LESS preprocessing, resource minification/bundling, web.config encryption, etc.
I have always been able to deploy just fine by simply right-clicking on the web app, and choosing the Publish - File System option.
I recently decided to try and automate the setting of the ACL permissions on a specific folder within the app. This led me down the road of changing from the File System publish option to the Web Deploy option (which also works fine after installing and configuring Web Deploy 3 on the server).
The reason I switched to Web Deploy is because it's my understanding that by using the Web Deploy option, I should be able to add additional steps to my wpp.targets file to set the necessary folder permissions.
I've seen numerous articles, blogs, forum posts, etc. on the subject and it seems fairly straight forward.
I'm trying to give Read/Write/Modify access to Domain Users for a folder named "IDAutomation" - so I basically just added the following at the end of my existing wpp.targets file:
<Target Name="SetupCustomAcls" AfterTargets="AddIisSettingAndFileContentsToSourceManifest">
<ItemGroup>
<MsDeploySourceManifest Include="setAcl">
<Path>$(_MSDeployDirPath_FullPath)\IDAutomation</Path>
<setAclAccess>Read,Write,Modify</setAclAccess>
<setAclUser>Domain Users</setAclUser>
<setAclResourceType>Directory</setAclResourceType>
<AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<Target Name="DeclareCustomParameters" AfterTargets="AddIisAndContentDeclareParametersItems">
<ItemGroup>
<MsDeployDeclareParameters Include="IDAutomationSetAclParam">
<Kind>ProviderPath</Kind>
<Scope>setAcl</Scope>
<Match>^$(_EscapeRegEx_MSDeployDirPath)\\IDAutomation$</Match>
<Value>$(_DestinationContentPath)/IDAutomation</Value>
<ExcludeFromSetParameter>True</ExcludeFromSetParameter>
</MsDeployDeclareParameters>
</ItemGroup>
</Target>
But I'm obviously missing something because I click Publish -> Web Deploy - and let it do its thing, the permissions are not applied to the folder. The app is deployed successfully and everything looks good - it just doesn't set the permissions on the folder for me.
Here's some excerpts from the end of the deploy output:
Target "Package" skipped, due to false condition; ($(_CreatePackage)) was evaluated as (false).
Target "MSDeployPublish" in file ..... from project .....
Start Web Deploy Publish the Application/package to....
...
Starting Web deployment task from source:manifest(.....) to Destination:auto().
Updating setAcl (Site/app).
Updating setAcl (Site/app).
Updating setAcl (Site/app/IDAutomation). <-- Appears to be doing something??
Updating filePath......
....
Updating setAcl (Site/app).
Updating setAcl (Site/app).
Updating setAcl (Site/app/IDAutomation). <-- Appears to be doing something??
Successfully executed Web deployment task.
Publish is successfully deployed.
Task "MSdeploy" skipped, due to false condition; ($(UseMsdeployExe)) was evaluated as (False).
Done building target "MSDeployPublish" in project ...
Done building project ...
So it appears to be setting the acl on the folder (twice for some reason) as you can see, but when I go look at the folder on the remote server, the permissions have not been applied.
What am I missing here?
I'm not trying to build a package for later/manual deployment or anything involving a build server. I'm simply manually trying to Publish -> Web Deploy.
Also Web Deploy 3.0 is installed on my machine (win7) as well as the web server (Win2008R2/IIS7.5).
-- UPDATE --
I've discovered that regardless of what I set in the setAclUser element, the sitemanifest.xml file is always missing the setAclUser attribute for the folder (abbreviated paths):
<sitemanifest>
<IisApp path="C:\...\obj\...\Package\PackageTmp" managedRuntimeVersion="v4.0" />
<setAcl path="C:\...\obj\...\Package\PackageTmp" setAclResourceType="Directory" />
<setAcl path="C:\...\obj\...\Package\PackageTmp" setAclUser="anonymousAuthenticationUser" setAclResourceType="Directory" />
<setAcl path="C:\...\obj\...\Package\PackageTmp\IDAutomation" setAclResourceType="Directory" setAclAccess="Read,Write" />
</sitemanifest>
So you can see there's no setAclUser on the setAcl element for the IDAutomation folder. Hopefully that will be a clue to someone?
Thanks again-
sigh - Finally realized I was missing the setAclUser property from the AdditionalProviderSettings:
<AdditionalProviderSettings>setAclUser;setAclResourceType;setAclAccess</AdditionalProviderSettings>

Can MSBuild exclude "Hidden" Web Deploy parameters from the generated SetParameters.xml?

In my Parameters.xml file, I have a couple of parameters that use the Web Deploy "variable" syntax to refer to other parameters, like this one that refers to the IIS Web Application Name parameter:
<parameter name="MyParam"
defaultValue="{IIS Web Application Name}/Web.config"
tags="Hidden"/>
My problem is that VS automatically imports this parameter into my SetParameters.xml file when I build the deployment package in spite of it being tagged as hidden. When it is passed to msdeploy via setParamFile, Web Deploy literally interprets the value of the parameter as
{IIS Web Application Name}/Web.config
rather than substituting the IIS application name.
If I remove the parameter from the auto-generated SetParameters.xml file, the variable works as expected. Is there any way to prevent VS from including that parameter in the first place, either by name or by tag?
This was actually far easier than I thought, given the answer to my earlier question.
I just needed to add a Hidden tag in the target that follows AddIisAndContentDeclareParametersItems. This apparently sets the tag in the source manifest prior to the package being built. It ends up looking something like this:
<Target Name="DeclareCustomParameters"
AfterTargets="AddIisAndContentDeclareParametersItems">
<ItemGroup>
<MsDeployDeclareParameters Include="Foo">
<!-- <snip> -->
<!-- the following elements are the important ones: -->
<Tags>Hidden</Tags>
<ExcludeFromSetParameter>True</ExcludeFromSetParameter>
</MsDeployDeclareParameters>
</ItemGroup>
</Target>
That was it!
This answer is for anyone else looking for a more complete example of substitution via targets. This example shows substituting a variable "database server name" into a connection string.
The ExcludeFromSetParameter element appears to be the key to making substitution work as it keeps the param out of the SetParameters.xml file (as the OP mentioned he did manually). Unfortunately, I don't think that ExcludeFromSetParameter can be set from a parameters.xml file, so this is the only option...
<Target Name="DeclareCustomParameters" BeforeTargets="Package">
<ItemGroup>
<MsDeployDeclareParameters Include="DatabaseServer">
<Description>Location of the database server hosting the user database</Description>
<Value>localhost</Value>
<DefaultValue>localhost</DefaultValue>
<Tags>DBServer, SQL</Tags>
</MsDeployDeclareParameters>
<MsDeployDeclareParameters Include="DB Connection String">
<Kind>XmlFile</Kind>
<Scope>Web.config</Scope>
<Match>/configuration/connectionStrings/add[#name='Database']/#connectionString</Match>
<Description>The connection string to the Database</Description>
<DefaultValue>Data Source={DatabaseServer};Initial Catalog=MyDatabase;Integrated Security=true;MultipleActiveResultSets=true;</DefaultValue>
<Tags>Hidden</Tags>
<ExcludeFromSetParameter>True</ExcludeFromSetParameter>
</MsDeployDeclareParameters>
</ItemGroup>
</Target>

Visual Studio 2010: How to publish an ASP.NET web app to a target folder with MSBUILD?

In Visual Studio 2010, you know how you can change your configuration (debug, release, etc), right-click a project in the solution explorer, click publish, and have all the important web app project files for the selected configuration copied to a target folder along with an xdt-transformed web.config? Well, I am looking for the MSBUILD equivalent of exactly that.
My challenge to you: Provide the ONE LINE that I need to execute at my command prompt in order to accomplish this. No third party programs. No tutorial videos. Just a single, straight-up command that I can literally copy from your response, paste into a command window, modify as necessary to support my directory structure, and then hit enter.
If not, then perhaps someone could provide a link to a complete MSBUILD reference showing every command, switch, and value I can use at the command line.
Put the below to ProjectPublish.MSBuild.xml file (change PropertyGroup as needed):
<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Publish">
<PropertyGroup>
<ProjectFile>Path\To\Web.csproj</ProjectFile>
<PublishDir>Path\For\Publish\Output</PublishDir>
<TempDir>Path\To\Temp\Folder</TempDir>
<BuildConfig>Release|Debug</BuildConfig>
</PropertyGroup>
<Target Name="Publish">
<MSBuild Projects="$(ProjectFile)"
Properties="Configuration=$(BuildConfig);WebProjectOutputDir=$(PublishDir);OutDir=$(TempDir)\;BuildingProject=true"
Targets="ResolveReferences;_CopyWebApplication" />
</Target>
</Project>
Calling this from command line (or .bat file) should do the trick:
%windir%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe ProjectPublish.MSBuild.xml
I found the solution I was looking for after all these months here
In case the above link goes bad, here's the skinny of what it says:
Unload then edit your project file. Look for the line where it's importing Microsoft.WebApplication.targets. Will look like:
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
Beneath that line, paste in this XML:
<Target Name="PublishToFileSystem" DependsOnTargets="PipelinePreDeployCopyAllFilesToOneFolder">
<Error Condition="'$(PublishDestination)'==''" Text="The PublishDestination property must be set to the intended publishing destination." />
<MakeDir Condition="!Exists($(PublishDestination))" Directories="$(PublishDestination)" />
<ItemGroup>
<PublishFiles Include="$(_PackageTempDir)\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(PublishFiles)" DestinationFiles="#(PublishFiles->'$(PublishDestination)\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="True" />
</Target>
Now, run this in a command prompt within the same folder as your project file:
msbuild TestWebApp.csproj "/p:Platform=AnyCPU;Configuration=Debug;PublishDestination=C:\pub" /t:PublishToFileSystem
Remember to specify the path to MSBUILD in the command or add the path to your global path environmental variable (which is what I did). On my machine, it was here:
C:\Windows\Microsoft.NET\Framework\v4.0.30319
To test this, I put a config transform in my Web.Release.config to add an AppSetting key (if you do this, make sure the AppSettings node is present in your base config file or you will get an error). When I used the above command to build the debug configuration, the key was not present in the published config file as expected. However, when I used the release config, the key was successfully added to the file.
I really wish Microsoft hadn't obfuscated the heck out of this. At any rate, this is the simplest solution I have found anywhere on the internet. I hope it helps the rest of you.

Resources