Using the Windows Azure REST APIs with PowerShell

Using the Windows Azure REST APIs with PowerShell

  • Comments 3
  • Likes

Summary: Microsoft support escalation engineer, James Kehr, talks about using the Windows Azure REST APIs with Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have James Kehr, a Windows availability support escalation engineer, as our guest blogger…

Application programming interfaces, more commonly called an APIs because it’s a ridiculously long name to type, have traditionally been the playground of programmers. Administrators, which I will call admins because it is a ridiculously long title to type, have generally not played with APIs because, well, we’re not programmers. Then along comes this whole cloud concept, and unsurprisingly, the programmers made an API for it. In the Windows Azure world, it is more specifically a REST API. But there is a problem with using an API in the cloud…building systems and networks is traditionally an admin’s job, and admins are normally not fans of APIs.

Ah ha! Windows PowerShell to the rescue! Right! Right?

Well…yes…but with a caveat.

Technically everything you can do in Windows Azure can be done with Windows PowerShell cmdlets. Just not with dedicated cmdlets. The dedicated Windows Azure cmdlets will do all the common stuff, and most of the uncommon stuff, but not everything. For example, let’s say you need to retrieve your virtual network gateway key to configure a VPN device. Piece of cake:

(Get-AzureVNetGatewayKey -VNetName MyVnet -LocalNetworkSiteName LocalSite).Value

But what if you have a requirement that all VPN preshared keys must be at least 50 characters long? The default in Windows Azure is 32 characters, so this would need to be changed. No worries, simply open Windows PowerShell and…

Hmm, there is no Windows Azure cmdlet for that. Then go into the Windows Azure Management Portal and…uh, there is no portal option. At least not of this writing (Jan 2014). But there is a Windows Azure REST API that will change the key length: Reset Virtual Network Gateway Shared Key.

And there is a Windows PowerShell cmdlet called Invoke-RestMethod.

Maybe the combination of the two will work? As it turns out, it does. It’s just a little tricky to learn how to do it. At least it was for me.

Working together

I would love to say that there is some super-easy, magical way of getting Windows PowerShell and the Windows Azure REST API to work together in perfect harmonious awesomeness. But the truth is, it can make your head explode. There is a reason why admins don’t like APIs. I will do my best to simplify the process, though.

Step 1. Look for dedicated Windows Azure cmdlets

My recommended first step is to make sure that there is, in fact, no Windows Azure cmdlet that can perform the task. A Windows Azure cmdlet requires much less work than invoking the REST API. The Windows Azure module for Windows PowerShell is a work in progress, less now than last year, but it will still change. For this reason, it is important to make sure that you have the latest module and you verify that what you need is not there.

The latest module and change log can be found here: Windows Azure SDK Tools Releases.

You can use this command to verify which version of Windows PowerShell you are using:

Get-Module -list Azure

This one will give you a list, one page at a time, of all the Windows Azure cmdlets:

Get-Command -Module Azure | more

And let’s not forget the handy Windows Azure Cmdlet Reference.

Step 2. Find the right REST API

When you are certain that there is no Windows Azure cmdlet that will work, it’s time to find a Windows Azure REST API. The complete REST API documentation is here: Service Management REST API Reference.

The sections starting with “Operations” have the list of REST operations for the portion of Windows Azure that is listed in the heading. So “Operations on Virtual Network Gateways” cover your VPN gateways. “Operations on Virtual Networks” covers your virtual network configuration, and so on.

Step 3. Research how the API works

There are four important parts to every API: method, request header, URI, and request body. The Service Management REST API Reference will give you three of these pieces and information about what is needed for the fourth.

Method

The first table on each reference page will tell you the method. This is basically an HTTP verb that tells Windows Azure what sort of action you are performing. For this post, I am going to use the Upload Client Root Certificate operation. The method for this API call is “POST”:

$method = "POST"

Image of menu item

Request headers

Another section on this page is Request Headers. This is where you find the header details. You look for the header date in the description and add it to the following template. That is the super simplified version, but it works.

The template is essentially a hash table that links the x-ms-version property to a date. The date is the minimum API Service Management version that is needed to perform the operation. For the upload root certificate operation, this is 2012-03-01.

$headerDate = '2012-03-01'

$headers = @{"x-ms-version"="$headerDate"}

Image of message

URI

The URI is the web address that needs to be used to call the API operation, and it is located in that first table with the method. This is not a straight-up URL that you can plug into the cmdlet. It has several components that must be inserted for the call to go to the correct subscription and object.

My certificate example is one of the simple URIs. Other operations, such as Update Autoscale Settings, have additional URI parameters that may need to be added to the base URI. The URI Parameters section will tell you which of these are required or optional, and list details about how to use them. Or it will say None, and then all you need is the base URI.

$subID = (Get-AzureSubscription -Current).SubscriptionId

$VNetName = "MyVnet"

$URI = "https://management.core.windows.net/$subID/services/networking/$VNetName/gateway/clientrootcertificates"

Image of message

Request body

The request body is where you pass information that is pertinent to the operation, and it can be the most difficult part to populate. Not all API calls require a request body. My example has a simple request body, while other operations, such as Set Network Configuration, have numerous options and are rather complex. (Hint: Use the Windows Azure cmdlets to set the network configuration.) This is one more reason why it is important to pay attention to the Service Management REST API Reference.

The problem I found with the request body sections is that it can be rather vague. In the upload root certificate API, the request body is <Binary>cert-data</Binary>. The section under the body format adds, “The binary data of the X.509 certificate,” which sheds a little more light. But there are still lingering questions. Do I need to pass the actual binary data, or will the certificate text be enough? How should the data be formatted?

I can’t answer that question for every API call, but hopefully this will give you a better idea…

Think of the Windows Azure back-end as a giant cloud full of Windows-based servers and systems—because that’s what it is. Whenever you interact with Windows Azure, you are interacting with Windows. Then think, “If I were to perform this operation on a computer running Windows Server, what would I need?” If you then format your data along those lines, you will quickly find what data is needed. More on this in a bit.

$cert = (Get-ChildItem Cert:\CurrentUser\My | ?{$_.Subject -eq "CN=TEST"}).RawData

$body = "<Binary>-----BEGIN CERTIFICATE-----`n$([convert]::ToBase64String($cert))`n-----END CERTIFICATE-----</Binary>"

Step 4. Format and invoke the Windows Azure REST method

You may have noticed that some of the code in Step 3 had a few extra bits. Let’s quickly review these:

URI

$subID = (Get-AzureSubscription -Current).SubscriptionId

$VNetName = "MyVnet"

$URI = "https://management.core.windows.net/$subID/services/networking/$VNetName/gateway/clientrootcertificates"

The API URI as found in the API Reference is incomplete. In the URI you see text surrounded by less than and greater than symbols (< >). These are parts of the URI that you need to replace. In this case, you need to provide the Windows Azure subscription ID and the virtual network name.

The quickest way to get your Windows Azure subscription ID is to pull it from Windows PowerShell. The Current parameter for Get-AzureSubscription will get you the active subscription ID. If you have more than one subscription ID, and you are properly paranoid, you can use the SubscriptionName parameter to specify a subscription. Or you can simply enter in the subscription ID as a string and forego the cmdlet. It really doesn’t matter, as long as the correct subscription ID gets stored in the $subID variable and then into the URI.

The virtual network name is a little more difficult. You could use a cmdlet to retrieve this, but that requires parsing the XML output from Get-AzureVNetConfig. That’s not too hard, and if you have a lot of virtual networks in your subscription, you may want to do this, but to keep things simple I entered this manually.

Other URIs have different requirements, so pay attention to the parts in <> for whatever API operation you call.

Request body

$cert = (Get-ChildItem Cert:\CurrentUser\My | ?{$_.Subject -eq "CN=TEST"}).RawData

$body = "<Binary>-----BEGIN CERTIFICATE-----`n$([convert]::ToBase64String($cert))`n-----END CERTIFICATE-----</Binary>"

The API Reference does not have the handy <> parts like the URI does, so you need to read through the XML to find what data needs to go where. For the more complex request bodies, the Request Elements section is a life saver. And remember, Windows Azure is Windows, so feed the API Windows-friendly data, and you’ll be right more often than not.

In my example, the certificate, TEST, is already stored in the current user’s personal certificate store. This translates to Cert:\CurrentUser\My in Windows PowerShell speak. The Get-ChildItem cmdlet pulls a list of all the certificates in the store. Pipe that to a simple subject search, and the correct certificate is collected. Wrap all that in parenthesis and tag on the RawData property to get the binary data.

I won’t use the actual binary data in this case because an X.509 certificate file is only text. Seriously, create or export an X.509 certificate and then open the .cer file in Notepad or a text editor. You’ll see that the certificate is text data with the prefix -----BEGIN CERTIFICATE----- and the suffix -----END CERTIFICATE-----.

For this reason, I convert the raw data to a string between the correct prefix and suffix. The `n parts are string carriage returns, which are used to ensure that the formatting is correct. This sends Windows Azure the same X.509 data that you would use when you import a certificate to Windows, IIS, or any other certificate-based role. As strange as this may seem…it works.

Alternately, if you had a .cer file, you could use Get-Content to retrieve the file text. Put that text between the XML element (<Binary></Binary>), and you would be ready to go.

Management certificate

The final piece to the API puzzle is the Windows Azure management certificate. The management certificate is what authenticates the Windows Azure REST API call. Without this certificate, the operation will fail.

You don’t need to specify this with Windows Azure cmdlets because the Windows Azure module does this for you. When you set up the Windows Azure module, the publishsettings file contains a copy of the management certificate, which is then imported with the Import-AzurePublishSettingsFile cmdlet and automatically used by all the dedicated cmdlets to authenticate. This is another reason why I prefer the cmdlets over the REST API.

There are two parameter options that you can use: CertificateThumbprint or Certificate.

I generally opt for the thumbprint…just because I do. It doesn’t matter if you lose the thumbprint or the entire certificate—if someone gets either of them, they can do all kinds of bad to your Windows Azure subscription. This is why I like to use a Windows PowerShell cmdlet to collect this information, and I do NOT store it in a plain text file. Although this is not fool proof, it is more secure because you cannot get the certificate unless you gain console access to a system where the Windows Azure module for Windows PowerShell is set up. A plain text file can simply be read or intercepted.

$mgmtCertThumb = (Get-AzureSubscription -Current).Certificate.Thumbprint

Time to invoke awesomeness!

Okay, it may not be awesomeness, but after going through the steps and getting it to work for the first time, it will feel like awesomeness! Putting everything together, you end up with a script like this:

# API method

$method = "POST"

# API header

$headerDate = '2012-03-01'

$headers = @{"x-ms-version"="$headerDate"}

# generate the API URI

$subID = (Get-AzureSubscription -Current).SubscriptionId

$VNetName = "MyVnet"

$URI = "https://management.core.windows.net/$subID/services/networking/$VNetName/gateway/clientrootcertificates"

# create Request Body

$cert = (Get-ChildItem Cert:\CurrentUser\My | ?{$_.Subject -eq "CN=TEST"}).RawData

$body = "<Binary>-----BEGIN CERTIFICATE-----`n$([convert]::ToBase64String($cert))`n-----END CERTIFICATE-----</Binary>"

# grab the Azure management certificate

$mgmtCertThumb = (Get-AzureSubscription -Current).Certificate.Thumbprint

# execute the Azure REST API

Invoke-RestMethod -Uri $URI -Method $method -Headers $headers -CertificateThumbprint $mgmtCertThumb -Body $body

All I did with the Invoke-RestMethod cmdlet is plug the conveniently named variables into the parameter names that conveniently correspond to the variable name. The pairings are pretty self-explanatory, so I won’t bore you with the obvious.

If everything works, you will get an object back in the $results variable. There is some XML inside the variable that you can use to validate whether the certificate was successful uploaded, but because this post is about invoking the Windows Azure REST APIs, I’ll use a second API call to check:

## validate the upload was successful ##

# update the method

$method = "GET"

# update the URI

$URI = "https://management.core.windows.net/$subID/services/networking/$VNetName/gateway/clientrootcertificates"

# get the list of client root certificates for the vnet

$verify = Invoke-RestMethod -Verbose -Uri $URI -CertificateThumbprint $mgmtCertThumb -Headers $headers -Method $method

# make sure the uploaded certificate is listed

if ($verify.ClientRootCertificates.ClientRootCertificate.Subject -contains "CN=TEST") {

    Write-Host "Root certificate upload was successful!"

} else {

    Write-Host "Uh oh! Something went wrong!"

That covers the basics of using the Windows Azure REST APIs through Windows PowerShell. After you get the basics down, it’s really not that hard. Parts of the APIs may take some trial and error to get working. Hair may be lost. Beards might go gray, but admins can, in fact, use an API. I still recommend using a cmdlet if it’s available. Seriously, it’s a lot easier.

Now go make those programmers jealous and show off your amazing API skills!

~James

Thank you, James, for sharing your time and knowledge. This is way cool, and it makes me want to fire up my MSDN subscription and start playing with Windows Azure.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Very nice post! Dealing with the Azure API can sometimes be frustrating...sadly I've dealt with worse so this isn't too bad ;-)

  • Thanks for this article Ed,
    I've been tasked with developing a script we can run to quickly find out which of the Azure Cloud Services in a given subscription has autoscale enabled. I was unable to find a cmdlet to do so, but I found this article and another describing the use of the REST API to get autoscale (http://msdn.microsoft.com/en-us/library/azure/dn510367.aspxsettings).
    Near the end of your article you mention that "If everything works, you will get an object back in the $results variable." I can't figure out how to write any of that back to the console.
    I am a noob, I admit it. Can you help me?