Search code examples
asp.net-corewebdeploy

How do you set ACLs in an ASP.NET Core Web Deploy package?


Prior to ASP.NET Core, it was pretty easy to adjust ACLs in a Web Deploy package with certain MSBuild targets: You would add some custom steps after the AddIisSettingAndFileContentsToSourceManifest and AddIisAndContentDeclareParametersItems targets.

Now, in Core, according to my own experiments and issues like this one, these tricks no longer work because

The tasks and targets for ASP.NET Core projects has been re-written ... The target names used in ASP.NET projects are not the same for ASP.NET Core.

I've tried playing with the targets that show up in Microsoft.NET.Sdk.Publish.MSDeployPackage.targets, but I can't seem to make it work. I get this sort of error during package build:

The target "MSDeployPackagePublish" listed in a BeforeTargets attribute ... does not exist in the project, and will be ignored.

What are the new targets? How can I change ACLs in ASP.NET Core Web Deploy projects?


Solution

  • Bafflingly, the Core team appears to have removed the ability to customize the MS Deploy pipeline via MSBuild. These concerns are echoed in several GitHub issues going back to at least 2017, so it appears they have no intention of restoring this feature.

    The crux of the problem is the MS Deploy targets have been "simplified for better readability." 😡

    The _CreateManifestFiles target, which used to be made up of several targets between which we could inject our own, now does this:

    1. Removes all previously specified MsDeploySourceManifest rules. 😡
    2. Adds a couple hardcoded MsDeploySourceManifest rules.
    3. Writes the manifest XML to disk.

    Likewise, the _CreateParameterFiles target now does this:

    1. Removes all previously specified MsDeployDeclareParameters rules. 😡
    2. Adds a couple hardcoded MsDeployDeclareParameters rules.
    3. Writes the parameters XML to disk.

    Fortunately, there is an extremely terrible workaround to the new locked-down design.

    1. Recreate the Manifest XML

    All of the targets are publicly usable, so my solution was just to re-write the _CreateManifestFiles target with my own -- and overwrite the manifest XML. Here's what I ended up with in my .pubxml:

    <Target Name="ReCreateManifestFile" AfterTargets="_CreateManifestFiles">
      <ItemGroup>
        <MsDeploySourceManifest Remove="@(MsDeploySourceManifest)" />
        <MsDeploySourceManifest Include="IisApp">
          <Path>$(PublishIntermediateOutputPath)</Path>
        </MsDeploySourceManifest>
        <MsDeploySourceManifest Include="setAcl">
          <Path>$(PublishIntermediateOutputPath)logs</Path>
          <setAclAccess>Read,Write</setAclAccess>
          <setAclResourceType>Directory</setAclResourceType>
          <AdditionalProviderSettings>setAclAccess;setAclResourceType</AdditionalProviderSettings>
        </MsDeploySourceManifest>
      </ItemGroup>
      <CreateManifestFile Manifests="@(MsDeploySourceManifest)" ManifestFile="$(_MsDeploySourceManifestPath)" />
    </Target>
    

    Note that this target executes immediately after _CreateManifestFiles.

    That's the easy part!

    2. Recreate the Parameters XML

    I create the parameters file with the same strategy. If you copy _CreateParameterFiles but add your own MsDeployDeclareParameters, it does almost does everything you need. Unfortunately, the new SDK also removed the ability for these rules to set ExcludeFromSetParameter -- if you read the code, that value is never even deserialized. 😡

    Without the ability to exclude a parameter from SetParameters.xml, you end up with a SetParameters.xml that looks like this:

    <parameters>
      <setParameter name="IIS Web Application Name" value="Default Web Site/GeoGrok" />
      <setParameter name="LogsSetAclParam" value="" />  <!-- not what you want :( -->
    </parameters>
    

    Any parameter in SetParameters.xml can't be ignored at deploy time, even if it has a DefaultValue. That means MSDeploy won't automatically do the substitution of the IIS App Name if you have a default value of, say, {IIS Web Application Name}/logs.

    MOREOVER, the SDK's CreateParameterFile call doesn't even include the DeclareParameterFile attribute, so DefaultValue is never written to parameters.xml in the first place! 😡

    The (again, terrible) solution I went with is:

    1. Re-write _CreateParameterFiles with your own MsDeployDeclareParameters elements.
    2. Adjust the CreateParameterFile to create the proper parameters.xml file.
    3. Copy over a static SetParameters.xml file that only includes the parameters you want your deployment team to see. In my case, that only has the IIS Web Application Name param.

    My static SetParameters.xml file (which I put in my project root) looks like this:

    <?xml version="1.0" encoding="utf-8"?>
    <parameters>
      <setParameter name="IIS Web Application Name" value="Default Web Site" />
    </parameters>
    

    And here's the finished parameters target in my .pubxml file:

    <Target Name="ReCreateParametersFile" AfterTargets="_CreateParameterFiles">
      <ItemGroup>
        <MsDeployDeclareParameters Remove="@(MsDeployDeclareParameters)" />
        <MsDeployDeclareParameters Include="IIS Web Application Name">
          <Kind>ProviderPath</Kind>
          <Scope>IisApp</Scope>
          <Match>$(PublishIntermediateOutputPath)</Match>
          <DefaultValue>$(DeployIisAppPath)</DefaultValue>
          <Value>$(DeployIisAppPath)</Value>
          <Tags>IisApp</Tags>
          <Priority></Priority>
          <ExcludeFromSetParameter>false</ExcludeFromSetParameter>
        </MsDeployDeclareParameters>
        <MsDeployDeclareParameters Include="LogsSetAclParam">
          <Kind>ProviderPath</Kind>
          <Scope>setAcl</Scope>
          <Match>$(PublishIntermediateOutputPath)logs</Match>
          <DefaultValue>{IIS Web Application Name}/logs</DefaultValue>
          <Tags>Hidden</Tags>
          <Priority>$(VsSetAclPriority)</Priority>
          <ExcludeFromSetParameter>true</ExcludeFromSetParameter>  <!-- I'm useless! -->
        </MsDeployDeclareParameters>
      </ItemGroup>
      <CreateParameterFile Parameters="@(MsDeployDeclareParameters)"
                           DeclareParameterFile="$(_MSDeployParametersFilePath)"
                           DeclareSetParameterFile="$(_MSDeploySetParametersFilePath)"
                           IncludeDefaultValue="True"
                           OptimisticParameterDefaultValue="$(EnableOptimisticParameterDefaultValue)"
                           SetParameterFile="$(_MSDeploySetParametersFilePath)"
                           GenerateFileEvenIfEmpty="True" />
      <Copy SourceFiles="$(MSBuildProjectDirectory)\GeoGrok.Web.SetParameters.xml" DestinationFiles="$(_MSDeploySetParametersFilePath)" />
    </Target>
    

    Note that Copy step there at the end.

    Good luck!