Using Azure Management Libraries from Azure Web Jobs
by Frans Lytzen | 12/11/2015
In this post I will show how to set up an Azure Web Job to automatically reboot a worker role at certain times. It's just an example, you can schedule pretty much any Azure operation in this way, including shutting down VMs at the end of the day etc.
Azure Management Libraries
One of the neat things is that, as I understand it, the libraries are autogenerated from the REST API so it should keep up pretty well. When you do hit an error, you will usually be able to see the REST call and response that the library tried to make in the exception which you can then cross-reference with the API documentation.
You may be tempted to use Powershell instead of C# as it's better documented and you'd think it would be simpler and cleaner - but you may struggle to get the Azure Powershell to work in Azure Web Jobs due to some weird issue that gives you an "please connect to the Internet before running this command" error.
Pre-requisites
- Web Jobs run in an Azure Web App so you need one of them already set up. Note that we will need to upload a certificate to that Web App, which means you need a Basic or Standard Web App - which does come with a price tag! It may be possible to alter the approach I am outlining to use AD auth and so bypass this requirement. If you do this and write it up, ping me in the comments and I will link from here. It may even be possible to only temporarily upscale to upload the cert and then downscale after - I haven't tried it.
Authentication
- Create a new Azure Management Certificate using
makecert -sky exchange -r -n "CN=CERTIFICATENAME" -pe -a sha1 -len 2048 -ss My "CERTIFICATENAME.cer"
See https://msdn.microsoft.com/en-us/library/azure/gg551722.aspx. You normally need to run this from your Visual Studio developer command prompt. Apparently makecert is now deprecated and you are supposed to use some odd powershell command instead - sorry if you find yourself needing to do that (do please leave a comment if you have the equivalent powershell syntax and I will update the post). - Upload the resulting .cer file as a Management Certificate to the subscription that you wish to control.
- Now export the certificate from your local certificate store including the private key; the .cer file you uploaded only has the public key and basically tells Azure that whoever has the matching private key is allowed to manage that Azure subscription. The Private key has been installed on your machine, but that isn't going to help you a whole lot when you try to run the job in an Azure Web Job.
- Open MMC and add the Certificate plugin for My User Account
- Find the certificate you created above, in the Personal folder
- Open it and choose to Copy to file (on the Details page)
- Select to save the private key and specify a password
- Save the .pfx file
- In the Azure Web App that you are going to use to run the job, upload the PFX file (in the old portal it is on the Configuration page)
- Add an "app setting" for the Azure Web App with the key WEBSITE_LOAD_CERTIFICATES and the value should be the thumbprint of the certificate you uploaded:
- After you uploaded the certificate you should be able to see the thumbprint in the portal
- Note that in some cases you get an odd character in front of the thumbprint which may not be visible when you paste it, but which will cause problems. To avoid this, paste the thumbprint into notepad and then copy it back out.
Setting up your project in Visual Studio
You need install the Microsoft.WindowsAzure.Management.Compute Nuget package so just do
Install-Package Microsoft.WindowsAzure.Management.Compute
The actual code
namespace WorkerRebooter
{
using System;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Azure;
using Microsoft.WindowsAzure.Management.Compute;
using Microsoft.WindowsAzure.Management.Compute.Models;
class Program
{
private const string subscriptionId = "XXXX";
private const string managementCertThumbprint = "XXXX";
private const string cloudServiceName = "XXXX";
private const string instanceName = "XXXX.Worker_IN_0";
static void Main(string[] args)
{
var client = GetClient();
Log($"About to reboot {instanceName}");
client.Deployments.RebootRoleInstanceByDeploymentSlot(cloudServiceName, DeploymentSlot.Production, instanceName);
Log($"Finished rebooting {instanceName}");
}
private static ComputeManagementClient GetClient()
{
Log($"About to obtain client for sub {subscriptionId}");
var cert = GetManagementCertificate(managementCertThumbprint);
var creds = new CertificateCloudCredentials(subscriptionId, cert);
var client = new ComputeManagementClient(creds);
Log($"Obtained client for sub {subscriptionId}");
return client;
}
private static X509Certificate2 GetManagementCertificate(string thumbPrint)
{
Log($"About to obtain certificate {thumbPrint}");
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var foundList = store.Certificates.Find(X509FindType.FindByThumbprint, thumbPrint, false);
var cert = foundList[0];
Log($"Obtained certificate {thumbPrint}");
return cert;
}
private static void Log(string msg)
{
Console.WriteLine($"{DateTime.UtcNow} {msg}");
}
}
}