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

13,100

Solution 1

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.

  1. 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)
  2. Inside the file paste the MSBuild content which is below this list
  3. 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?

Solution 2

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.

Share:
13,100
ladenedge
Author by

ladenedge

Updated on June 03, 2022

Comments

  • ladenedge
    ladenedge almost 2 years

    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?

  • ladenedge
    ladenedge almost 13 years
    Heh, I don't at all feel bad for not figuring this one out by myself. It does indeed work, though, and I'm grateful for your taking the time to create such a comprehensive answer!
  • TimH
    TimH over 12 years
    Is there a way to make this work for multiple folders? For example, how would you extend this to make not only doc/public writable, but also /home/temp ?
  • Sayed Ibrahim Hashimi
    Sayed Ibrahim Hashimi over 12 years
    You should be able to just copy the item value for MSDeploySourceManifest and MSDeployDeclareParameters and update values. Did you try that and it didn't work
  • Ethan J. Brown
    Ethan J. Brown over 12 years
    FYI - This doesn't seem to work for applications that are not pushed into a virtual directory and are pushed to Default Site/ for instance. I'm trying to figure out how to tweak it so it will work for that scenario as well. Error is on line 3847 of Web.Publishing.targets where VSMSDeploy is called - Site '' does not exist.
  • Sayed Ibrahim Hashimi
    Sayed Ibrahim Hashimi over 12 years
    This may be a bug, can you email me details so that I can follow up on it? My email is sayedha [at]{Microsoft}dotCOM.
  • binarydreams
    binarydreams over 11 years
    The use of using DefaultValue to inherit the value from an existing parameter makes this answer much more preferable.