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.
With this information we can now configure the csmanage tool to talk with the service management API.
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
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
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
4: csmanage /delete-deployment /hosted-service:domgreen
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
Once the service is successfully deployed we can set the status of the deployment to a running state and allow it to initialize itself.
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).
10: cspack \ServiceDefinition.csdef
12: /role:WorkerRole1; \CloudService.WorkerRole\bin\Release;domgreen.CloudService.WorkerRole.dll
21: csmanage /update-deployment /hosted-service:domgreen /slot:production /status:suspended
25: csmanage /delete-deployment /hosted-service:domgreen /slot:production
29: csmanage /create-deployment /hosted-service:domgreen /slot:production /name:domgreen
30: /label:domgreenLabel /package:$(BlobStorageEndpoint)packages/CloudService.cspkg
35: csmanage /update-deployment /hosted-service:domgreen /slot:production /status:running
42: <Target Name="BeforeDropBuild" Condition=" '$(IsDesktopBuild)'!='true' ">
46: <Message Text="use cspack create a package for deployment"/>
47: <Exec Command="$(PackageCommand)" WorkingDirectory="$(SolutionDir)" />
49: <Message Text="run program to place package in blob storage"/>
50: <Exec Command="$(DeployToBlobStorageCommand) $(SolutionDir)\AirWatch.cspkg"/>
52: <Message Text="suspend the current running cloud application"/>
53: <Exec Command="$(SuspendCommand)" ContinueOnError="true"/>
55: <Message Text="delete the currently deployed cloud application"/>
56: <Exec Command="$(DeleteCommand)" ContinueOnError="true"/>
58: <Message Text="create a azure service using the package placed in blob storage"/>
59: <Exec Command="$(CreateCommand)" WorkingDirectory="$(SolutionRoot)"/>
61: <Message Text="run the created application"/>
62: <Exec Command="$(RunCommand)" />
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.
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 :) .