As part of a new project to create a Jenkins CI server on Azure I am writing a set of powershell scripts to control virtual machines on Azure. For this project the plan is to use virtual machine (VM) images as a template for an ‘immutable server’ that will contain the Jenkins instance.
Now the actual server isn't really ‘immutable’ given that the jenkins instance will update, add and delete files on the hard drive which will obviously change the state of the server. As such the immutable idea isn't applied to the whole server but more to the configuration part of the server. The idea being that the configuration of the server will not be changed once the server is put in production. Any configuration changes (e.g. a new version of Jenkins) will be done by creating a new image, spinning up a new server based on that image and then destroying the old server and replacing it with the new one.
So in order to achieve this goal the first step will be to build an image with all the required software on it and then verify that this image has indeed been created correctly.
To create the image we first obtain a certificate that can be used for the WinRM SSL connection between the Azure VM and the local machine that is executing the creation scripts. You can either get an official one or you can use a self-signed certificate (which is obviously less secure). Two things of interest are:
- The certificate needs to have an exportable private key because otherwise it cannot be used for the WinRM connection.
- The certificate needs to be named after the connection that you expect to make. For a connection to
an Azure VM this will most likely be something like
Once the certificate is installed in the user certificate store we can create a new virtual machine
from a given base image, e.g. a Windows 2012 R2 server image. The following powershell function creates
a new windows VM with a WinRM endpoint with the certificate that was created earlier. Note that the
New-AzureVM function can create resource
and storage groups for the new VM if you don't specify a storage account and a matching resource group.
Once the VM is running a new Powershell remote session can be opened to the machine in order to start
the configuration of the machine. Note that this approach only seems to be working for
Get-AzureWinRMUri function only returns the
https URI. Hence the need for a certificate
that can be used to secure the connection.
The next step is to copy all the installer files and configuration scripts to the VM. This can be done over the remoting channnel.
Once all the required files have been copied to the VM the configuration of the machine can be started. This can be done in many different ways, e.g through the use of a configuration management tool or just via the use of plain old scripts. When the configuration is complete and all the necessary clean-up has been done the time has come to turn the VM into an image. Before doing that a Windows machine will have to be sysprepp'ed so that there are no unique identifiers in the image (and thus in the copies).
In order to sysprep an Azure VM it is necessary to execute the sysprep command through a script on the VM because sysprep fails if the command is given directly through the remoting channel. The following function creates a new Powershell script which invokes sysprep, copies that to the VM and then executes that script. Once sysprep has completed running the machine will be turned off and an image can be created.
The next step is to test the new image in order to verify that all configuration changes have been applied correctly. The explanation of how the testing of an virtual machine image works is a topic for the next blog post.