Wed, 15 Aug 2012

Calling the Windows Azure Service Management API from Python

Written as a response to a Stack Overflow question about how to do it, below is a little Python command-line tool that takes a .publishsettings file as an argument and calls the List Storage Accounts method of the Windows Azure Service Management API.

(Note: This code works on OS X but doesn't work on Windows. In trying to track down the reason, I discovered a Stack Overflow question/answer that essentially says Python on Windows has an old version of OpenSSL that causes this to fail. Interestingly enough, the person asking the question was also calling the Service Management API. Small world!)

import argparse
import base64
import os
from OpenSSL.crypto import *
import requests
import tempfile
import xml.etree.ElementTree

parser = argparse.ArgumentParser()
parser.add_argument('file', metavar='file', type=str,
        help='Your .publishsettings file.')
args = parser.parse_args()

tree = xml.etree.ElementTree.parse(args.file)
pp = tree.find('PublishProfile')
cert = load_pkcs12(base64.decodestring(pp.get('ManagementCertificate')))
subscription_id = pp.find('Subscription').get('Id')

with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
        f.file.write(dump_certificate(FILETYPE_PEM, cert.get_certificate()))
        f.file.write(dump_privatekey(FILETYPE_PEM, cert.get_privatekey()))
        name = f.name

response = requests.get(
        'https://management.core.windows.net/%s/services/storageservices'
                % subscription_id,
        headers={'x-ms-version': '2011-02-25'}, cert=f.name).text

os.remove(name)

tree = xml.etree.ElementTree.fromstring(response)
for e in tree.iterfind('.//{http://schemas.microsoft.com/windowsazure}ServiceName'): print e.text

To run it, you'll need a .publishsettings file. For a bit more information about what that is and how to get one, see my earlier blog post "Calling the Windows Azure Service Management API with the New .publishsettings File."

You'll also need to install two prerequisite Python modules. pip install pyopenssl requests should do the trick.

There are two things I don't really like about this code:

  1. It assumes a .publishsettings file that contains only one subscription. I haven't tested it, but I think in the presence of multiple subscriptions, this will just use the first subscription ID it finds. This is just me being lazy.
  2. It writes the certificate out to a temporary file. It seems that Python's ssl module insists on certificates being passed around via file names. If anyone knows of a nicer way to do this, please let me know!