A week with Azure API

I am a senior software engineer at ServMask Inc. I recently created an extension to our All-in-One WP Migration plugin that connects to Microsoft Azure Storage. This facilitates backing up WordPress websites directly into Microsoft Azure Storage.

From the beginning I was skeptical about this platform. We all know about the many problems we often encounter when using products created or maintained by Microsoft, and this has led me to think that it will not even be different here. This thought followed me constantly in the development of this plugin and at least for a moment I was right.

What made me very impressed was that they had a very large set of services. Undoubtedly, this has led to problems with my orientation in their documentation. To find what I needed, I had to spend some time. However, I managed to find the resources I needed and I was able to continue with the development.

Surprisingly for me, everything was well described. We’ve all come across documentation that is not well structured, and it’s pretty hard to get into them. Here the problem was absent.

It also worked as described. I have often encountered any problems with other similar services. Most often, problems are related to poorly functioning methods or not working at all. However, I have not been able to encounter such problems here. Everything worked smoothly, and here was the moment when I began to change my mind about this service.

In the process I learned more about the service itself and I thought it would be useful to share them.

Microsoft Azure Storage Account

Azure’s storage service provides durable, scalable, and redundant storage. All access to storage services takes place through the storage account. The storage account is the highest level of the namespace for accessing each of the fundamental services. It is also the basis for authentication.

Azure Storage Service elements

The REST APIs for storage services expose the storage account as a resource. For example:

https://[storage account name]/blob.core.windows.net/[container]/[blob name]

There are 3 types of storage accounts:

One Azure Storage subscription can host up to 100 storage accounts, each of which can hold 500 TB. This can be increased to 250 storage accounts but putting a business case to the Azure Storage team.

Security

Every account has two authentication keys: primary and secondary — either of which can be used for any operation.

The most challenging part of creating the All-in-one WP migration Azure extension was to determine the correct usage of the Authorization Header which is required to be passed to use the REST APIs. This header complicates application builds and had me stumped for a while, so I’ll share what I learned here

There are five basic steps for calculating this header. In this example I will provide the PHP code that I wrote:

function prepare_canonical_request( $method, $path, $query = array(), $headers = array() ) {
    $canonical_url = array();

    // Start with the HTTP request method (GET, PUT, POST, etc.)
    $canonical_url[] = $method;

    // Set the canonical headers
    $canonical_headers = array();
    foreach ( $headers as $key => $value ) {
        $canonical_headers[ strtolower( $key ) ] = trim( $value );
    }

    // Sort the canonical headers
    ksort( $canonical_headers );

    // Add the canonical headers
    foreach ( $canonical_headers as $key => $value ) {
        if ( stripos( $key, 'x-ms-' ) === 0 ) {
            $canonical_url[] = sprintf( '%s:%s', $key, $value );
        } else {
            if ( strcasecmp( $key, 'Content-Length' ) === 0 && intval( $value ) === 0 ) {
                $canonical_url[] = null;
            } else {
                $canonical_url[] = sprintf( '%s', $value );
            }
        }
    }

    // Add the account name and path
    $canonical_url[] = '/' . $this->account_name . $path;

    // Set the canonical query parameters
    $canonical_query = array();
    foreach ( $query as $key => $value ) {
        $canonical_query[ strtolower( $key ) ] = trim( $value );
    }

    // Sort the canonical query parameters
    ksort( $canonical_query );

    // Add the canonical query string
    foreach ( $canonical_query as $key => $value ) {
        $canonical_url[] = sprintf( '%s:%s', $key, $value );
    }

    return implode( "\n", $canonical_url );
}
function prepare_string_to_sign( $canonical_url ) {
    return utf8_encode( $canonical_url );
}
function calculate_signature( $string_to_sign ) {
    // Calculate the signature
    $signature = hash_hmac( 'sha256', $string_to_sign, base64_decode( $this->account_key ), true );

    // Base64 the signature
    $str_base64_signature = base64_encode( $signature );

    return $str_base64_signature;
}
function build_authorization_string( $str_signature ) {
    return 'SharedKey ' . $this->account_name . ':' . $str_signature;
}

What are your thoughts?