During the last twelve months I have spent a lot of time on Continuous Delivery and a deep dive into Team Foundation Server 2012. A commit stage is set, we use TFS build to build, and NuGet as an Artifact Repository. Now my goal is to bring the deployment to the team, let them also be part of the deployment process, maintain and create deployment scripts etc. I have looked around among some deployment tools like Octopus and InRelease. InRelease looks promising. Instead of using one of the great tools, I decided to create my own. Why you may ask? The reason is that I want to be able to do any modifications and bring the code to the team, and to be honest, I needed something to do on my spare time ;)
In this blog post I will describe my tool.
Pull or Push deployment
My first tool supported only Push deployment. It uses my own XML definition do specify environments and deployment steps. My deployment controller at that time read the XML and downloaded the packages to deploy from a NuGet repository and pushed them to deployment agents (Installed on every machine). The deployment agent role was to unzip the package and do configuration files transformations and to run PowerShell scripts. By using push deployment, I have more control over how the deployment went. The main problem was that I needed to make sure the Deployment controller had access to the server where the agents was installed, and also that a specific network port was open in the firewall. Another problem is when a new web server will be added (for example to a load balancing), changes need to be made to the XML file specifying the deployment environments, also a new push deployment need to be started to configure the server.
I decided to move over to a more pull deployment solution. I removed my own XML definition file and decided to use Microsoft Workflow instead. The Deployment controller uses OWN and Katana instead of WCF (my first push tool uses WCF). The deployment controller has the responsibility to execute a deployment workflow. I added a workflow activity that can be used to perform action on specific servers. When a deployment is started the deployment control added the task to a queue. The deployment agent checks the queue for a task. If a task was added to the queue for the specific server, the agent started to execute the task, and report back the progress to the deployment controller. The deployment agent downloads the packages it needs, it also transforms configuration files and execute PowerShell scripts if they was part of the package to be installed. Nothing form the queue will be removed, the deployment agent hold the responsibility to know which task it has already "executed". By doing so I can simply install an agent to a new server, specify that it has the role as a "webserver", and when it's started, it can check the queue for the latest task to be executed. With this solution I can simply add a server and it will automatically be configured. No changes to the deployment script, or trigger a push deploy is needed.
Note: I don't have the goal to build a high scalable solution for hundreds of servers.
Here is an architecture overview:
The deployment controller doesn't know anything about the deployment agents, only the agents know about the deployment controller. I use NuGet.Core to get packages from the Artifact Repository. I also use the Microsoft.Web.Xdt library for configuration files transformations. The deployment agent is a thin service, it's the workflow activity the agent will execute that handle all the things that should happen within the agent.
I decided to use Microsoft Workflow to specify the deployments steps that should be performed. I decided to use workflow because of its design editor and to easily visualize the deployment steps. I have created a Sequence, Parallel, InstallNuGetPackage and Agent activity. The reason I have created a custom Sequence and Parallel activity is to make it pass global variables to child activates. Variables that can be used when a configuration file is transformed, or by a PowerShell script. Variables in Workflow is scope based, I needed to make the variables global within the scope and to its children. I was kind of disappointed that Workflow didn't pass parent variables to a child activity. The agent activity is the one that will serialize all of its activity and add it to the queue for an agent to capture. The InstallNuGetPackage is used to download a package from the Artifact Repository, and handle the execution of PowerShell scripts and configuration transformation. I will probably add more activity, such as Installing Service, creating MSMQ queues, creating web applications etc. Activity that will reduce the PowerShell script.
Here is a simple deployment workflow:
Note: The above workflow will only demonstrate that an agent activity can perform activity in Sequence and the above will also perform InstallNuGetPackages in parallel.
The goal with the workflow is to make it easier for other team members to setup deployment script by drag & drop of activities, and also have a visualized overview of the script.
If you would like to follow my progress or know when I post a new blog post, please follow me on twitter: @fredrikn