简体   繁体   中英

Set ACLs during Web Deployment via MSBuild

I have a mostly working web build-and-deploy configuration running in TeamCity, that basically uses MSBuild to automatically deploy the site to a web server. MSDeploy sets everything to Readonly on the target server by default, and I need the AppPool identity to have write access to just one folder.

I found an article by Kevin leetham that gets me 90% of the way there. Kevin describes how it is possible to hook into the MSBuild Web Publish Pipeline by creating a file called ProjectName.wpp.targets, along these lines:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
     <!--Extends the AfterAddIisSettingAndFileContentsToSourceManifest action do also set ACLs -->

    <IncludeCustomACLs>TRUE</IncludeCustomACLs>

    <AfterAddIisSettingAndFileContentsToSourceManifest Condition="'$(AfterAddIisSettingAndFileContentsToSourceManifest)'==''">
      $(AfterAddIisSettingAndFileContentsToSourceManifest);
      SetCustomACLs;
    </AfterAddIisSettingAndFileContentsToSourceManifest>
  </PropertyGroup>
  <Target Name="SetCustomACLs" Condition="'$(IncludeCustomACLs)'=='TRUE'">
    <Message Text="Adding Custom ACls" />
    <ItemGroup>
      <!-- Ensure the AppPool identity has write access to the Files directory -->
      <MsDeploySourceManifest Include="setAcl" Condition="$(IncludeSetAclProviderOnDestination)">
        <Path>$(_MSDeployDirPath_FullPath)\files</Path>
        <setAclAccess>Read,Write,Modify</setAclAccess>
        <setAclResourceType>Directory</setAclResourceType>
        <AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
      </MsDeploySourceManifest>
    </ItemGroup>
  </Target>
</Project>

This is so nearly working that it is driving me crazy. The ACL gets added to the manifest, but the problem is that it generates an absolute path based on the build location, rather than being relative to the IIS web app on the target server. the generated manifest comes out like this (some names have been changed to protect the innocent):

<?xml version="1.0" encoding="utf-8"?>
<sitemanifest>
  <IisApp path="C:\SolutionPath\IisWebAppName\src\MyProjectName\obj\Release_Deploy\Package\PackageTmp" managedRuntimeVersion="v4.0" />
  <setAcl path="C:\SolutionPath\IisWebAppName\src\MyProjectName\obj\Release_Deploy\Package\PackageTmp" setAclResourceType="Directory" />
  <setAcl path="C:\SolutionPath\IisWebAppName\src\MyProjectName\obj\Release_Deploy\Package\PackageTmp" setAclUser="anonymousAuthenticationUser" setAclResourceType="Directory" />
  <setAcl path="C:\SolutionPath\IisWebAppName\src\MyProjectName\obj\Release_Deploy\Package\PackageTmp\files" setAclResourceType="Directory" setAclAccess="Read,Write,Modify" />
</sitemanifest>

This actually looks correct, the last line is my custom ACL from teh wpp.targets file. However, when MSDeploy sends this to the target server, here's what happens:

2>Start Web Deploy Publish the Application/package to https://webhostingprovider.biz:8172/msdeploy.axd?site=IisWebAppName ...
2>Adding sitemanifest (sitemanifest).
2>Adding ACL's for path (IisWebAppName)
2>Adding ACL's for path (IisWebAppName)
2>Adding ACL's for path (C:\SolutionPath\IisWebAppname\src\MyProjectName\obj\Release_Deploy\Package\PackageTmp\files)
2>C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.targets(4377,5): Error ERROR_USER_NOT_AUTHORIZED_FOR_SETACL: Web deployment task failed. (Could not complete an operation with the specified provider ("setAcl") when connecting using the Web Management Service. This can occur if the server administrator has not authorized the user for this operation. setAcl http://go.microsoft.com/fwlink/?LinkId=178034

The whole thing falls over on my custom ACL path, which comes out using an absolute path name instead of being relative to IisWebAppName . I cannot figure out why!!

Help please :)

You need to create a ProviderPath parameter with a DefaultValue that takes it's value of another parameter using the {param name} syntax.

Here's a helper I included on another question that performs all the actions:

<ItemDefinitionGroup>
  <AdditionalAcls>
    <AclAccess>Write</AclAccess>
    <ResourceType>Directory</ResourceType>
  </AdditionalAcls>
</ItemDefinitionGroup>

<PropertyGroup>
  <AfterAddIisSettingAndFileContentsToSourceManifest>
    $(AfterAddIisSettingAndFileContentsToSourceManifest);
    AddAdditionalAclsToSourceManifest;
  </AfterAddIisSettingAndFileContentsToSourceManifest>
  <AfterAddIisAndContentDeclareParametersItems>
    $(AfterAddIisAndContentDeclareParametersItems);
    AddAdditionalAclsDeclareParameterItems
  </AfterAddIisAndContentDeclareParametersItems>
</PropertyGroup>

<Target Name="AddAdditionalAclsToSourceManifest">
  <ItemGroup Condition="'@(AdditionalAcls)' != ''">
    <MsDeploySourceManifest Include="setAcl">
      <Path>$(_MSDeployDirPath_FullPath)\%(AdditionalAcls.Identity)</Path>
      <setAclResourceType Condition="'%(AdditionalAcls.ResourceType)' != ''">%(AdditionalAcls.ResourceType)</setAclResourceType>
      <setAclAccess>%(AdditionalAcls.AclAccess)</setAclAccess>
      <AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
    </MsDeploySourceManifest>
  </ItemGroup>
</Target>

<Target Name="AddAdditionalAclsDeclareParameterItems">
  <ItemGroup Condition="'@(AdditionalAcls)' != ''">
    <MsDeployDeclareParameters Include="Add %(AdditionalAcls.AclAccess) permission to %(AdditionalAcls.Identity) Folder">
      <Kind>ProviderPath</Kind>
      <Scope>setAcl</Scope>
      <Match>^$(_EscapeRegEx_MSDeployDirPath)\\@(AdditionalAcls)$</Match>
      <Description>Add %(AdditionalAcls.AclAccess) permission to %(AdditionalAcls.Identity) Folder</Description>
      <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/@(AdditionalAcls)</DefaultValue>
      <DestinationContentPath>$(_DestinationContentPath)/@(AdditionalAcls)</DestinationContentPath>
      <Tags>Hidden</Tags>
      <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
      <Priority>$(VsSetAclPriority)</Priority>
    </MsDeployDeclareParameters>
  </ItemGroup>
</Target>

You can use it by declaring:

<ItemGroup>
    <AdditionalAcls Include="MyRelativeWritableDirectory" />
</ItemGroup>

Please note that this solution only currently works if you don't need a backslash in the path (ie. if it's a root directory only). If you need a sub-directory, you'll need to steal the trick I use for "SkipDeleteItems" (later in that answer) to add regex-escaped path metadata to each item.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM