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.
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