- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Tuesday, November 8, 2011

Setting Folder Permissions on Web Publish

Note: I’d like to thank Tom Dykstra for helping me put this together

As part of the deployment process you might need to set permissions on one or more folders in the destination web application. For example, if your application allows users to upload files, it needs write access to a folder in order to store the uploaded files. By default, theweb publishing pipeline (the automated deployment process) automatically sets write permission on the App_Data folder in order to enable database updates in case you store databases in that folder, and you can use the same method to make the deployment process automatically set permissions on any folder you choose.

The <projectname>.wpp.targets File

The web publishing pipeline is controlled by MSBuild .targets files that are installed with Visual Studio. Rather than editing the Visual Studio .targets files, you can create a project-specific .targets file that Visual Studio will use to modify the deployment process for a specific project. To do that, you create an XML file in the project folder and name it < projectname>.wpp.targets. The following example shows XML markup that causes the web publishing pipeline to set write permissions for a folder named Elmah located in the root folder of the application. The sample is designed to work for both deployment methods: publish or web deployment package.

<?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)\Elmah</Path>
        <setAclAccess>Read,Write</setAclAccess>
        <setAclResourceType>Directory</setAclResourceType>
        <AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
      </MsDeploySourceManifest>
    </ItemGroup>
  </Target>

  <Target Name="DeclareCustomParameters" AfterTargets="AddIisAndContentDeclareParametersItems">
    <ItemGroup>
      <MsDeployDeclareParameters Include="ElmahSetAclParam">
        <Kind>ProviderPath</Kind>
        <Scope>setAcl</Scope>
        <Match>^$(_EscapeRegEx_MSDeployDirPath)\\Elmah$</Match>
        <Description>Add write permission to the Elmah folder.</Description>
        <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/Elmah</DefaultValue>
        <Value>$(_DestinationContentPath)/Elmah</Value>
        <Tags>Hidden</Tags>
        <Priority>$(VsSetAclPriority)</Priority>
        <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
      </MsDeployDeclareParameters>
    </ItemGroup>
  </Target>
  
</Project>

The first Target element ("SetupCustomAcls") causes a setAcl action to be added to the deployment package's source manifest when the package is created. The source manifest is a file that specifies what to include in the deployment package. The path to the folder is specified using an MSBuild variable (_MSDeployDirPath_FullPath) that specifies the path to the project root folder:

<?xml version="1.0" encoding="utf-8"?>
<sitemanifest>  <IisApp path="C:\ContosoUniversity\ContosoUniversity\obj\Release\Package\PackageTmp" managedRuntimeVersion="v4.0" />
  <setAcl path="C:\ContosoUniversity\ContosoUniversity\obj\Release\Package\PackageTmp" setAclResourceType="Directory" />
  <setAcl path="C:\ContosoUniversity\ContosoUniversity\obj\Release\Package\PackageTmp" setAclUser="anonymousAuthenticationUser" setAclResourceType="Directory" />
  <setAcl path="C:\ContosoUniversity\ContosoUniversity\obj\Release\Package\PackageTmp\App_Data" setAclResourceType="Directory" setAclAccess="Write" />
  <setAcl path="C:\ContosoUniversity\ContosoUniversity\obj\Release\Package\PackageTmp\Elmah" setAclResourceType="Directory" setAclAccess="Read,Write" />
</sitemanifest>

This isn't all you need to do, however, because the permissions actually need to be set on the folder in the destination application, not the folder in the deployment package. That's why the second Target element is needed. The second Target element ("DeclareCustomParameters") creates a user-defined Web Deploy parameter named ElmahSetAclParam. The value of this parameter will change the value of the setAcl path at deploy time. TheDefaultValue element of the ElmahSetAclParam parameter sets the value that will be used when you are deploying by creating and installing a deployment package; the Value element sets the value that will be used when you are publishing without using a deployment package.

If you are using a deployment package, the actual path to the destination folder isn't known until the package is installed. Therefore the default value of this parameter is set using an MSDeploy parameter that resolves to the name of a system-defined Web Deploy parameter:

<DefaultValue>{$(_MsDeployParameterNameForContentPath)}/Elmah</DefaultValue>

When the package is created, _MsDeployParameterNameForContentPath is translated into "{IIS Web Application Name}", so the actual default value becomes "{IIS Web Application Name}/Elmah". When the package is installed, the value of that Web Deploy parameter is the path of the destination root folder, and so as a result the ElmahSetAclParam parameter resolves to the physical path of the destination Elmah folder (for example: C:\inetpub\wwwroot\ContosoUniversity\Elmah). That path then replaces the package's physical path for the Elmah setAcl element in the source manifest.

When you publish the project rather than using a deployment package, the Value element instead of the DefaultValue element provides the value for the ElmahSetAclParam parameter. In that case, you are deploying from Visual Studio, and Visual Studio has the actual destination path value in an MSBuild property, so the Value element can specify that MSBuild property directly and does not need to use a Web Deploy variable:

<Value>$(_DestinationContentPath)/Elmah</Value>

The "Hidden" value in the Tags element prevents the ElmahSetAclParam parameter from being displayed in the IIS Manager UI if you use IIS Manager to install the package. The UI doesn't have to show the value, because a user never needs to change it manually.

<Tags>Hidden</Tags>

Adding More Folders

You can add more folders by adding ItemGroup elements. The following example builds on the earlier one by also granting Read permission for the bin folder to the NETWORK SERVICE account. (This is required in some cases for SQL Server Compact.) Notice that in order to specify the account to which access is granted, "setAclUser" was added to the < AdditionalProviderSettings> element, and a <SetAclUser> element specifying NETWORK SERVICE was added.

<?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)\Elmah</Path>
                <setAclAccess>Read,Write</setAclAccess>
                <setAclResourceType>Directory</setAclResourceType>
                <AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
            </MsDeploySourceManifest>
            <MsDeploySourceManifest Include="setAcl">
                <Path>$(_MSDeployDirPath_FullPath)\bin</Path>
                <setAclUser>NETWORK SERVICE</setAclUser>
                <setAclAccess>Read</setAclAccess>
                <setAclResourceType>Directory</setAclResourceType>
                <AdditionalProviderSettings>setAclUser;setAclResourceType;setAclAccess</AdditionalProviderSettings>
            </MsDeploySourceManifest>
        </ItemGroup>
    </Target>

    <Target Name="DeclareCustomParameters" AfterTargets="AddIisAndContentDeclareParametersItems">
        <ItemGroup>
            <MsDeployDeclareParameters Include="ElmahSetAclParam">
                <Kind>ProviderPath</Kind>
                <Scope>setAcl</Scope>
                <Match>^$(_EscapeRegEx_MSDeployDirPath)\\Elmah$</Match>
                <Description>Add write permission to the Elmah folder.</Description>
                <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/Elmah</DefaultValue>
                <Value>$(_DestinationContentPath)/Elmah</Value>
                <Tags>Hidden</Tags>
                <Priority>$(VsSetAclPriority)</Priority>
                <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
            </MsDeployDeclareParameters>
            <MsDeployDeclareParameters Include="BinSetAclParam">
                <Kind>ProviderPath</Kind>
                <Scope>setAcl</Scope>
                <Match>^$(_EscapeRegEx_MSDeployDirPath)\\Bin$</Match>
                <Description>Add read permission to the bin folder.</Description>
                <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/bin</DefaultValue>
                <Value>$(_DestinationContentPath)/bin</Value>
                <Tags>Hidden</Tags>
                <Priority>$(VsSetAclPriority)</Priority>
                <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
            </MsDeployDeclareParameters>
        </ItemGroup>
    </Target>
    
</Project>

.targets File Caching in Visual Studio

Setting up .targets files is like writing code -- rarely do you get everything right the first time. If you do make a mistake that you have to fix, you should be aware that Visual Studio caches target files. This means that every time you change a .targets file, you must exit Visual Studio and restart it before you rebuild the package in order to see the effect of your change. This is not an issue if you create packages using the command line.

Making Changes that Affect Parameters in Packages

Visual Studio will not rebuild a package if it cannot determine that a change has been made, and changes that affect Web Deploy parameters are sometimes not recognized as changes. Therefore, if you make any changes that affect parameters in the source manifest, it's best to select Clean Solution from the Build menu in order to delete the contents of the Package folder before you rebuild the package.

Sayed Ibrahim Hashimi – @SayedIHashimi

Tuesday, November 8, 2011 4:49:54 AM (GMT Standard Time, UTC+00:00)  #     |