Deploying to the Cloud as Part of Your Daily Build

 

I have recently been working on a big Azure application that we have been wanting to deploy to the cloud and test, to make sure that no unexpected bugs occur whilst the application is in the wild and give the opportunity to see how it performs.

Now the big problem has been deploying our application to the cloud every time we wanted to update the application online, we had a dev sit there and manually publish the service, upload the package and initialize the instances. This obviously wasn't great as one of the developers on the team would have to take time out from his tasks / bugs to upload the application.

However, the Azure team came through with some great releases just over a week ago that would allow the management of our services via an API. As soon as I read the blog post announcing the Windows Azure Service Management API I knew that my prayers had been answered and we would be able to automate deployment of our application out to the cloud. This surprisingly was much easier to understand and implement than I imagined as a sample called csmanage.exe was included with the release giving a hook into all of the API’s (documentation).

The most time consuming part turned out to be wrapping the calls to csmanage.exe into build scripts and watching a number of builds and deployments fail before getting it right (no one likes to wait 15mins only to see a red light).

So here’s what I did to get our application deploying on the daily build:

Seting up the Service

Setting up the service requires you to upload a certificate to your Azure login, this can either be done with the following handy command line argument:

makecert -r -pe -a sha1 -n "CN=Windows Azure Authentication Certificate" -ss My -len 2048 -sp "Microsoft Enhanced RSA and AES Cryptographic Provider" -sy 24 testcert.cer

Or use Eric Nelson’s walk through “Using IIS to generate a X509 certificate”.

Now we just need to log into the portal and upload the certificate, taking note of your Subscription ID and certificate thumbprint.

az1

Configure csmanage.exe

With this information we can now configure the csmanage tool to talk with the service management API.

az2

Packaging the Cloud Service

When deploying prior to the release of the management API I would normally right click the Cloud service within Visual Studio and let it handle the packing of the application itself, however during the build we will need to do this automatically. So we can dig out cspack.exe a command line application to package up your solution that was shipped with the Azure SDK.

    1: cspack \ServiceDefinition.csdef 
    2:         /role:WebRole1;\CloudService.WebRole 
    3:         /role:WorkerRole1;\CloudService.WorkerRole\bin\Release;
    4:               domgreen.CloudService.WorkerRole.dll
    5:         /out:CloudService.cspkg

When packaging the application we need to give the cspack executable a number of arguments these are, the service definition so that it knows what configuration to expect, any web roles and their containing folder, any worker roles with the folder containing the dll and also the name of the dll and an optional out parameter. I normally use the out parameter to ensure that I have the correct name and location of the package for the next step.

Uploading cspkg to Azure Storage

When deploying with the csmanage.exe we need to ensure that the packaged solution is available in blob storage on the same account you are deploying to, to do this I have created a simple command line application that takes the cspkg file and puts it into a folder called “packages” in our storage area.

    1: PackageToBlobStorage.exe CloudService.cspkg

Using csmanage.exe

Now that we have our package in Azure storage and csmanage.exe configured we can use the management API to call out to our service and deploy to Azure.

I first suspend and delete any service that is currently running:

    1: csmanage /update-deployment /hosted-service:domgreen 
    2:          /slot:production /status:suspended
    3:  
    4: csmanage /delete-deployment /hosted-service:domgreen 
    5:          /slot:production

This leave me safe in the knowledge that the creation of a package will not fail due to a running service.

Now the important part using csmanage.exe to create a deployment in our hosted service, within this command I am selecting the hosting area I wish to create the deployment, the production slot as well as the location of the package (blob storage) and any configuration that will be used in the deployed service.

    1: csmanage /create-deployment /hosted-service:domgreen 
    2:          /slot:production /name:domgreen 
    3:         /label:domgreenLabel 
    4:         /package:$(BlobStorageEndpoint)packages/CloudService.cspkg 
    5:         /config:$(SolutionDir)\CloudService\ServiceConfiguration.cscfg

Once the service is successfully deployed we can set the status of the deployment to a running state and allow it to initialize itself.

    1: csmanage /update-deployment /hosted-service:domgreen 
    2:          /slot:production /status:running

Once the deployment is fully initialized users can start using the application.

Creating Build Targets

With the correct commands figured out and correctly deploying my application to the cloud I decided to write some build targets to do this during our daily builds. To do this I used the msbuild exec command (I know this is probably really ugly but it gets the job done, well at least until the Azure boys give some built in tasks).

    1: <PropertyGroup>
    2:    
    3:    ...
    4:  
    5:     <BlobStorageEndpoint>
    6:         https://domgreen.blob.core.windows.net/
    7:     </BlobStorageEndpoint>
    8:     
    9:     <PackageCommand>
   10:         cspack \ServiceDefinition.csdef 
   11:         /role:WebRole1;\CloudService.WebRole 
   12:         /role:WorkerRole1; \CloudService.WorkerRole\bin\Release;domgreen.CloudService.WorkerRole.dll 
   13:         /out:CloudService.cspkg
   14:     </PackageCommand>
   15:     
   16:     <DeployToBlobStorageCommand>
   17:         C:\PackageToBlobStorage\PackageToBlobStorage.exe
   18:     </DeployToBlobStorageCommand>
   19:     
   20:     <SuspendCommand>
   21:         csmanage /update-deployment /hosted-service:domgreen /slot:production /status:suspended
   22:     </SuspendCommand>
   23:     
   24:     <DeleteCommand>
   25:         csmanage /delete-deployment /hosted-service:domgreen /slot:production
   26:     </DeleteCommand>
   27:     
   28:     <CreateCommand>
   29:         csmanage /create-deployment /hosted-service:domgreen /slot:production /name:domgreen 
   30:         /label:domgreenLabel /package:$(BlobStorageEndpoint)packages/CloudService.cspkg 
   31:         /config:$(SolutionDir)\CloudService\ServiceConfiguration.cscfg
   32:     </CreateCommand>
   33:     
   34:     <RunCommand>
   35:         csmanage /update-deployment /hosted-service:domgreen /slot:production /status:running
   36:     </RunCommand>
   37:     
   38:     ...
   39:     
   40:   </PropertyGroup>
   41:  
   42:   <Target Name="BeforeDropBuild" Condition=" '$(IsDesktopBuild)'!='true' ">
   43:  
   44:     ...
   45:  
   46:     <Message Text="use cspack create a package for deployment"/>
   47:     <Exec Command="$(PackageCommand)" WorkingDirectory="$(SolutionDir)" />
   48:  
   49:     <Message Text="run program to place package in blob storage"/>
   50:     <Exec Command="$(DeployToBlobStorageCommand) $(SolutionDir)\AirWatch.cspkg"/>
   51:  
   52:     <Message Text="suspend the current running cloud application"/>
   53:     <Exec Command="$(SuspendCommand)" ContinueOnError="true"/>
   54:  
   55:     <Message Text="delete the currently deployed cloud application"/>
   56:     <Exec Command="$(DeleteCommand)" ContinueOnError="true"/>
   57:  
   58:     <Message Text="create a azure service using the package placed in blob storage"/>
   59:     <Exec Command="$(CreateCommand)" WorkingDirectory="$(SolutionRoot)"/>
   60:  
   61:     <Message Text="run the created application"/>
   62:     <Exec Command="$(RunCommand)" />
   63:     
   64:     ...
   65:     
   66: </Target>

The only problem I found with this that the csmanage.exe will return exit codes and so the exec command will need to ignore these, or will treat them as errors during the build.

Multiple Deployments

On the current project we are actually deploying the application to a number of different sites for test, performance etc. so have also had to create a number of scripts to ensure that the endpoints are correct for each of the deployments. Added to this we needed to check that the csmangae.exe and the cloud apps configuration (cscfg) are set up correctly using the sdc tasks and their xml setters to get this working.

 

Now at the end of each day the latest iteration of our application is uploaded and deployed to Azure, leaving us devs to be happy coders and not worry about deployment :) .