JHauge's blog

Philosophy major turned web developer

02 Aug 2014

Deploying an Umbraco Website with Webdeploy

Disclaimer: This is mostly a feeble attempt to hold on to my sanity, after wrestling with Web Deploy a.k.a msdeploy over the course of a week.

Goal of the week was to prepare a web deployment package to deploy an Umbraco website to a server I had no access, which in itself isnt' a dificult task. Challenge was that I would like to automate setting the correct ACL’s on the destination server as well as create necessary empty folders, preferrably without resorting to empty.txt files. (For some reason I intensely dislike empty text files, used to solve deployment problems)

That doesn’t sound to hard does it? I didn’t think so either, so I started out by just setting up a normal publish profile, using the Visual Studio GUI.

Publish step 1

First step creating a publish package

Publish step 2 - the settings

Second step - destination settings

This proces got me to the first version of a deployment packages, that could be installed on a remote server through the command line. The package consists of the following files:

The moving parts

  • The project.wpp.targets file
    Use this for customizations that are to be used on all WPP deployments

  • The publish xml file:
    Use this to setup customizations for the particular publish scenario

Additions to .wpp.targets file

Excluding files

Consider setting “Build action” property to “None” instead of “Content”

Alternatively remove files from output using a target:

<Target Name="DeploymentExcludes" BeforeTargets="ExcludeFilesFromPackage">
    <Message Text="Excluding files" />
    <ItemGroup Condition="'$(Configuration)'=='Release'">
        <ExcludeFromPackageFiles Include="css\**\*.less" />
    </ItemGroup>
</Target>

For latest project it was setup in the .wpp.targets file

Additions to .pubxml

Running the targets below

<Project>
    <PropertyGroup>
        <UseMsDeployExe>true</UseMsDeployExe>
        <!-- Custom targets -->
        <AfterAddIisAndContentDeclareParametersItems>
            SetParams;
        </AfterAddIisAndContentDeclareParametersItems>
        <AfterAddDeclareParametersItems />
        <AfterAddIisSettingAndFileContentsToSourceManifest>
            CreateEmptyFolders;
            EmptyFolderSkipRules;
            SetAcl;
        </AfterAddIisSettingAndFileContentsToSourceManifest>
    </PropertyGroup>
</Project>

Research why <UseMsDeployExe> was necessary!

Creating empty folders

<Target Name="CreateEmptyFolders">
    <Message Text="Add empty folders" />
    <MakeDir Directories="$(_MSDeployDirPath_FullPath)\App_Code" />
    <MakeDir Directories="$(_MSDeployDirPath_FullPath)\App_Data" />
    <MakeDir Directories="$(_MSDeployDirPath_FullPath)\masterpages" />
    <MakeDir Directories="$(_MSDeployDirPath_FullPath)\usercontrols" />
    <MakeDir Directories="$(_MSDeployDirPath_FullPath)\media" />
</Target>

In .pubxml file

Skipping deletion of files on target

<Target Name="EmptyFolderSkipRules">
    <Message Text="Adding skip rules for key empty folders" />
    <ItemGroup>
        <MsDeploySkipRules Include="SkipFoldersInAppData">
            <SkipAction>Delete</SkipAction>
            <objectName>dirPath</objectName>
            <absolutePath>App_Data\\.*</absolutePath>
        </MsDeploySkipRules>
        <MsDeploySkipRules Include="SkipFilesInAppData">
            <SkipAction>Delete</SkipAction>
            <objectName>filePath</objectName>
            <absolutePath>App_Data\\.*</absolutePath>
        </MsDeploySkipRules>
    </ItemGroup>
</Target>

In .pubxml file

Setting ACLs

<ItemGroup>
    <AppCodeDirAcl Include="App_Code">
        <AclAccess>Modify</AclAccess>
    </AppCodeDirAcl>
    <AppDataDirAcl Include="App_Data">
        <AclAccess>Modify</AclAccess>
    </AppDataDirAcl>
    <ConfigDirAcl Include="config">
        <AclAccess>Modify</AclAccess>
    </ConfigDirAcl>
    <CssDirAcl Include="css">
        <AclAccess>Modify</AclAccess>
    </CssDirAcl>
    <ScriptsDirAcl Include="scripts">
        <AclAccess>Modify</AclAccess>
    </ScriptsDirAcl>
    <MasterpagesDirAcl Include="masterpages">
        <AclAccess>Modify</AclAccess>
    </MasterpagesDirAcl>
    <MediaDirAcl Include="media">
        <AclAccess>Modify</AclAccess>
    </MediaDirAcl>
</ItemGroup>
<Target Name="SetAcl">
    <Message Text="Running acls" />
    <ItemGroup>
        <MsDeploySourceManifest Include="setAcl">
            <Path>$(_MSDeployDirPath_FullPath)\%(AppCodeDirAcl.Identity)</Path>
            <setAclAccess Condition="%(AppCodeDirAcl.AclAccess) != ''">%(AppCodeDirAcl.AclAccess)</setAclAccess>
            <AdditionalProviderSettings>setAclAccess</AdditionalProviderSettings>
        </MsDeploySourceManifest>
    </ItemGroup>
    <ItemGroup>
        <MsDeploySourceManifest Include="setAcl">
            <Path>$(_MSDeployDirPath_FullPath)\%(AppDataDirAcl.Identity)</Path>
            <setAclAccess Condition="%(AppDataDirAcl.AclAccess) != ''">%(AppDataDirAcl.AclAccess)</setAclAccess>
            <AdditionalProviderSettings>setAclAccess</AdditionalProviderSettings>
        </MsDeploySourceManifest>
    </ItemGroup>
    <ItemGroup>
        <MsDeploySourceManifest Include="setAcl">
            <Path>$(_MSDeployDirPath_FullPath)\%(CssDirAcl.Identity)</Path>
            <setAclAccess Condition="%(CssDirAcl.AclAccess) != ''">%(CssDirAcl.AclAccess)</setAclAccess>
            <AdditionalProviderSettings>setAclAccess</AdditionalProviderSettings>
        </MsDeploySourceManifest>
    </ItemGroup>
    <ItemGroup>
        <MsDeploySourceManifest Include="setAcl">
            <Path>$(_MSDeployDirPath_FullPath)\%(ConfigDirAcl.Identity)</Path>
            <setAclAccess Condition="%(ConfigDirAcl.AclAccess) != ''">%(ConfigDirAcl.AclAccess)</setAclAccess>
            <AdditionalProviderSettings>setAclAccess</AdditionalProviderSettings>
        </MsDeploySourceManifest>
    </ItemGroup>
    <ItemGroup>
        <MsDeploySourceManifest Include="setAcl">
            <Path>$(_MSDeployDirPath_FullPath)\%(ScriptsDirAcl.Identity)</Path>
            <setAclAccess Condition="%(ScriptsDirAcl.AclAccess) != ''">%(ScriptsDirAcl.AclAccess)</setAclAccess>
            <AdditionalProviderSettings>setAclAccess</AdditionalProviderSettings>
        </MsDeploySourceManifest>
    </ItemGroup>
    <ItemGroup>
        <MsDeploySourceManifest Include="setAcl">
            <Path>$(_MSDeployDirPath_FullPath)\%(MasterpagesDirAcl.Identity)</Path>
            <setAclAccess Condition="%(MasterpagesDirAcl.AclAccess) != ''">%(MasterpagesDirAcl.AclAccess)</setAclAccess>
            <AdditionalProviderSettings>setAclAccess</AdditionalProviderSettings>
        </MsDeploySourceManifest>
    </ItemGroup>
    <ItemGroup>
        <MsDeploySourceManifest Include="setAcl">
            <Path>$(_MSDeployDirPath_FullPath)\%(MediaDirAcl.Identity)</Path>
            <setAclAccess Condition="%(MediaDirAcl.AclAccess) != ''">%(MediaDirAcl.AclAccess)</setAclAccess>
            <AdditionalProviderSettings>setAclAccess</AdditionalProviderSettings>
        </MsDeploySourceManifest>
    </ItemGroup>
</Target>
<Target Name="SetParams">
    <Message Text="Setting params" />
    <ItemGroup>
        <MsDeployDeclareParameters Include="SetAcl %(AppCodeDirAcl.Identity)">
            <Kind>ProviderPath</Kind>
            <Scope>setAcl</Scope>
            <Match>^$(_EscapeRegEx_MSDeployDirPath)\\@(AppCodeDirAcl)$</Match>
            <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/@(AppCodeDirAcl)</DefaultValue>
            <Value>$(_DestinationContentPath)/@(AppCodeDirAcl)</Value>
            <Tags>Hidden</Tags>
            <Priority>$(VsSetAclPriority)</Priority>
            <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
        </MsDeployDeclareParameters>
    </ItemGroup>
    <ItemGroup>
        <MsDeployDeclareParameters Include="SetAcl %(AppDataDirAclAcl.Identity)">
            <Kind>ProviderPath</Kind>
            <Scope>setAcl</Scope>
            <Match>^$(_EscapeRegEx_MSDeployDirPath)\\@(AppDataDirAclAcl)$</Match>
            <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/@(AppDataDirAclAcl)</DefaultValue>
            <Value>$(_DestinationContentPath)/@(AppDataDirAclAcl)</Value>
            <Tags>Hidden</Tags>
            <Priority>$(VsSetAclPriority)</Priority>
            <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
        </MsDeployDeclareParameters>
    </ItemGroup>
    <ItemGroup>
        <MsDeployDeclareParameters Include="SetAcl %(CssDirAcl.Identity)">
            <Kind>ProviderPath</Kind>
            <Scope>setAcl</Scope>
            <Match>^$(_EscapeRegEx_MSDeployDirPath)\\@(CssDirAcl)$</Match>
            <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/@(CssDirAcl)</DefaultValue>
            <Value>$(_DestinationContentPath)/@(CssDirAcl)</Value>
            <Tags>Hidden</Tags>
            <Priority>$(VsSetAclPriority)</Priority>
            <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
        </MsDeployDeclareParameters>
    </ItemGroup>
    <ItemGroup>
        <MsDeployDeclareParameters Include="SetAcl %(ConfigDirAcl.Identity)">
            <Kind>ProviderPath</Kind>
            <Scope>setAcl</Scope>
            <Match>^$(_EscapeRegEx_MSDeployDirPath)\\@(ConfigDirAcl)$</Match>
            <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/@(ConfigDirAcl)</DefaultValue>
            <Value>$(_DestinationContentPath)/@(ConfigDirAcl)</Value>
            <Tags>Hidden</Tags>
            <Priority>$(VsSetAclPriority)</Priority>
            <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
        </MsDeployDeclareParameters>
    </ItemGroup>
    <ItemGroup>
        <MsDeployDeclareParameters Include="SetAcl %(ScriptsDirAcl.Identity)">
            <Kind>ProviderPath</Kind>
            <Scope>setAcl</Scope>
            <Match>^$(_EscapeRegEx_MSDeployDirPath)\\@(ScriptsDirAcl)$</Match>
            <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/@(ScriptsDirAcl)</DefaultValue>
            <Value>$(_DestinationContentPath)/@(ScriptsDirAcl)</Value>
            <Tags>Hidden</Tags>
            <Priority>$(VsSetAclPriority)</Priority>
            <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
        </MsDeployDeclareParameters>
    </ItemGroup>
    <ItemGroup>
        <MsDeployDeclareParameters Include="SetAcl %(MasterpagesDirAcl.Identity)">
            <Kind>ProviderPath</Kind>
            <Scope>setAcl</Scope>
            <Match>^$(_EscapeRegEx_MSDeployDirPath)\\@(MasterpagesDirAcl)$</Match>
            <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/@(MasterpagesDirAcl)</DefaultValue>
            <Value>$(_DestinationContentPath)/@(MasterpagesDirAcl)</Value>
            <Tags>Hidden</Tags>
            <Priority>$(VsSetAclPriority)</Priority>
            <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
        </MsDeployDeclareParameters>
    </ItemGroup>
    <ItemGroup>
        <MsDeployDeclareParameters Include="SetAcl %(MediaDirAcl.Identity)">
            <Kind>ProviderPath</Kind>
            <Scope>setAcl</Scope>
            <Match>^$(_EscapeRegEx_MSDeployDirPath)\\@(MediaDirAcl)$</Match>
            <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/@(MediaDirAcl)</DefaultValue>
            <Value>$(_DestinationContentPath)/@(MediaDirAcl)</Value>
            <Tags>Hidden</Tags>
            <Priority>$(VsSetAclPriority)</Priority>
            <ExcludeFromSetParameter>True</ExcludeFromSetParameter>
        </MsDeployDeclareParameters>
    </ItemGroup>
</Target>

In .pubxml file - all these Item/Property-groups might be merged into one declaration if was smarter about parameter handling