Building the deployment package
This post is a part of the automated deployment story.
The first custom step of my build process is building the deployment package. Starting from two applications in the solution it is impractical to just copy over a bunch of DLLs. Much better idea is to package them somehow so they can be easily handled as a whole. There is a number of packaging technologies available for .NET applications, including:
None of them is a complete and perfect solution. First two are biased towards packaging and managing reusable libraries so they put emphasis on versioning and enabling usage of packages in IDE and in the build process. Web Deploy is focused on, well, web deployment and it is lacking good support for any other application type.
In my solution I’ve chosen Web Deploy because it few reasons. First, it was already there since it was the foundation of old deployment scripts. Second, it has a very nice feature of so-called publishing a web application. You may know this feature from VisualStudio as it is available from context menu on web projects. Basically, it removes all the files that are not necessary to run the application (like .cs, .csproj).
There are two central concepts in Web Deploy: operation and provider. Operation describes what to do. There are three (only!) possible operations:
- dump – displays information about an object
- delete – deletes an object
- sync – synchronizes two objects
Web Deploy comes with a great variety of providers. Providers implement specific deployment steps like changing the machine.config file or putting an assembly to GAC. The most important one for me is the package provider. It allows to pack a specified web site in IIS into a zip file and clone it somewhere else. What you can’t read on the documentation page (but can find on the Internet, of course) is, you can build similar package via MSBuild. Do do so, just call
MSBuild "ProjectName.csproj" /T:Package
After your main build process completes. The output is a nice zip file containing all the information necessary to deploy the web application.
DRYing the build script
For each application I deploy I need to call some packaging routing in the build script. This can quickly become a copy-pasted spaghetti. Fortunately I found out that MSBuild supports iteration. Here’s how you define the collection of elemenents
<ItemGroup Label="WebPackages"> <WebPackages Include="ProjectA\ProjectA.csproj" /> <WebPackages Include="ProjectB\ProjectB.csproj" /> <WebPackages Include="ProjectC\ProjectC.csproj" /> </ItemGroup>
And here is how you call a specific routine on all of them
<Target Name="web-package-build" Inputs="@WebPackages" Outputs="%(WebPackages.Identity)"> <Exec Command="$(MSBuild4) "$(SolutionRoot)\%(WebPackages.Identity)" /p:Configuration=$(BuildConfig) /p:DeployOnBuild=true /p:DeployTarget=Package /tv:4.0" /> </Target>
Nice, isn’t it?
One to rule them all
Bare Web Deploy would probably be enough for three or four web applications. As you remember, in my case there is about a dozen of them and another dozen of console tools. That’s why I decided I need another layer. Later on it turned out to be a good idea also for another reason — security. During the audit process one of the auditors pointed out that we don’t have any mechanism to guarantee that the binaries deployed to production are not forged.
I thought it will be a trivial task to implement — just use a command-line tool to zip all the packages (and console applications, each in it’s own folder) and another command-line tool to sign the zip file digitally. Unfortunately it turned out that there are no built-in tools on the Windows platform for these simple tasks. I didn’t like the idea of installing applications like 7zip or similar on production servers so I decided to create a custom tool for the task. This is exactly how PackMan was born.
PackMan is a quick and dirty tool for packaging a bunch of files together in a digitally signed zip file. Of course it also allows you to verify the signature and unpack the contents. PackMan is stream-based so it has no problem supporting quite large packages (mine has about 120 MB). It uses Windows PKI to retrieve the keys for signing and verifying.