Deploying Windows Services with Chef
Deploying Windows services with chef can be a little tricky. The service
resource doesn’t actually know how to create services in Windows, and surprisingly, niether does windows_service
.
Fortunately, this is not a deal breaker since creating services is actually pretty easy using a variety of approaches, and the simplest being sc create
.
So our task breakdown is:
- Create the service if it does not exist.
- Stop the service
- Fetch the updated service files
- Start the service
Create the service
An execute
resource can run our sc
command. In order to check if the service exists first, we can use ::Win32::Service.exists?
in the guard block for the execute. You will also need to `require ‘win32/service’.
So, it might look like this:
execute "Create FooService" do
command "sc create \"FooService\" binPath= \"c:/Service/FooService.exe\""
not_if {::Win32::Service.exists?("FooService")}
end
Voile!
Stop the service
This one is straight forward with the service
or windows_service
resource:
windows_service "FooService" do
action :stop
end
Fetch the updated service files
In my case I am using my build server to “distribute” the services in a folder structure as a build step, which I then simply zip up, and can download as a build artefact.
This means I have two steps to “fetch”: Download, Unpack.
The download step is done with remote_file
:
remote_file "c:/temp/Package.zip" do
source "http://buildserver/lastest/package.zip"
mode '0775'
only_if {::File.directory?('C:/temp/') }
action :create
end
You can of course use role
and environment
attributes to inject the URL for the package.
I’ve decided to use 7-Zip to do my unpacking, which I run with another execute resource:
execute 'UnpackServices' do
command '7z x C:/temp/Package.zip -o"C:/Service" -y'
environment(
'Path' => 'c:/"Program Files"/7-Zip'
)
subscribes :run, 'remote_file[c:/temp/Package.zip]', :immediately
end
Note that I’m subscribing to the remote_file[c:/temp/Package.zip
. This is because the remote_file
resource seems to execute asynchronously, so I need to wait for that to finish before trying to unpack it.
Start the service
Starting the service is straight forward. You can either use a new service resource (just provide a different name), or you can notify the existing service resource from your UnpackServices
:
notifies :start, 'windows_service[FooService]', :immediately
Conclusion
And thats the nuts and bolts of it.
Theres a few things going on here, and I’ve templatised this into a way that makes it easy to extend without changing the recipe (as in, add new services), but that’s for another post.