Author avatar

Benney Au

Access Azure Storage Account Resources from Postman

Benney Au

  • Oct 8, 2020
  • 7 Min read
  • 89 Views
  • Oct 8, 2020
  • 7 Min read
  • 89 Views
Postman
Languages, Frameworks, and Tools
Developer Testing Tools/Frameworks
Developer Tools

Introduction

Azure storage accounts provide low-cost and high-availability data services. The primary way to access these data services is using REST APIs. For your business' custom REST APIs, you may already be comfortable with using Postman to test, debug and monitor them. It would be convenient if you could stay inside Postman to also interact with your Azure storage account, perhaps to make end to end testing easier or share requests with your team members.

In this guide, you will learn how to use Postman to interact with your storage account. You will set up authentication and then query storage tables and storage blobs. This guide builds on a previous guide, Set Up Postman and Automatically Add Bearer Tokens.

Using Shared Keys to Authorize to Table Storage

Azure storage accounts offer several ways to authenticate, including managed identity for storage blobs and storage queues, Azure AD authentication, shared keys, and shared access signatures (SAS) tokens. However, the simplest solution is using shared keys. Two keys are provided for you when you create a storage account. They have permissions to do everything on the storage account, so it is important to take care to store them securely.

The below code snippet shows how you can generate the required authorization values to make a request to a storage table.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const storageAccount = pm.variables.get('azure_storage_account');
const accountKey = pm.variables.get('azure_storage_key');

const date = new Date();
const UTCstring = date.toUTCString();

const dataToEncode = UTCstring + "\n" + `/${storageAccount}${pm.request.url.getPath()}`;

const encodedData = unescape(encodeURIComponent(dataToEncode));

var hash = CryptoJS.HmacSHA256(encodedData, CryptoJS.enc.Base64.parse(accountKey));
var signature = hash.toString(CryptoJS.enc.Base64);

var auth = "SharedKeyLite " + storageAccount + ":" + signature;

pm.variables.set("header_authorization", auth);
pm.variables.set("header_date", UTCstring);
javascript

With this code snippet set as the pre-request script, you also need to set up headers to read the variables saved.

1
2
3
4
5
6
GET https://{{azure_storage_account}}.table.core.windows.net/MyTable()?$filter=(PartitionKey eq 'PartitionA')

x-ms-date:{{header_date}}
Authorization:{{header_authorization}}
x-ms-version:2019-02-02
Accept:application/json;odata=nometadata

The header details above demonstrate how to configure he URL and headers of a table storage query. When you run this request, it will return a JSON list of rows that match the specified filter.

Using Shared Keys to Authorize to Blob Storage

You can also use shared keys to connect to blob storage. However, the script to do this is slightly more complex. It is demonstrated below and sourced from Kamran Ayub's azure-storage-rest-postman GitHub Repository.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// Set Date header value for authorization
// Should be UTC GMT string
pm.variables.set("header_date", new Date().toUTCString());

// Get hash of all header-name:value
const headers = pm.request.getHeaders({ ignoreCase: true, enabled: true });

// Construct Signature value for Authorization header
var signatureParts = [
    pm.request.method.toUpperCase(),
    headers["content-encoding"] || "",
    headers["content-language"] || "",
    headers["content-length"]  || "",
//    pm.request.body ? pm.request.body.toString().length || "" : "",
    headers["content-md5"] || "",
    headers["content-type"] || "",
    headers["x-ms-date"] ? "" : (pm.variables.get("header_date") || ""),
    headers["if-modified-since"] || "",
    headers["if-match"] || "",
    headers["if-none-match"] || "",
    headers["if-unmodified-since"] || "",
    headers["range"] || ""
];

// Construct CanonicalizedHeaders
const canonicalHeaderNames = [];
Object.keys(headers).forEach(key => {
    if (key.startsWith("x-ms-")) {
        canonicalHeaderNames.push(key);
    }
});
// Sort headers lexographically by name
canonicalHeaderNames.sort();

const canonicalHeaderParts = [];
canonicalHeaderNames.forEach(key => {
    let value = pm.request.getHeaders({ ignoreCase: true, enabled: true })[key];

    // Populate variables
    value = pm.variables.replaceIn(value);

    // Replace whitespace in value but not if its within quotes
    if (!value.startsWith("\"")) {
        value = value.replace(/\s+/, " ");
    }

    canonicalHeaderParts.push(`${key}:${value}`);
});

// Add headers to signature
signatureParts.push.apply(signatureParts, canonicalHeaderParts);

// Construct CanonicalizedResource
const canonicalResourceParts = [
    `/${pm.variables.get("azure_storage_account")}${pm.request.url.getPath()}`
];
const canonicalQueryNames = [];
pm.request.url.query.each(query => {
    canonicalQueryNames.push(query.key.toLowerCase());
});
canonicalQueryNames.sort();
canonicalQueryNames.forEach(queryName => {
    const value = pm.request.url.query.get(queryName);

    // NOTE: This does not properly explode multiple same query params' values
    // and turn them into comma-separated list
    canonicalResourceParts.push(`${queryName}:${value}`);
});
// Add resource to signature
signatureParts.push.apply(signatureParts, canonicalResourceParts);

console.log("Signature Parts", signatureParts);

// Now, construct signature raw string
const signatureRaw = signatureParts.join("\n");

console.log("Signature String", JSON.stringify(signatureRaw));

// Hash it using HMAC-SHA256 and then encode using base64
const storageKey = pm.variables.get("azure_storage_key");
const signatureBytes = CryptoJS.HmacSHA256(signatureRaw, CryptoJS.enc.Base64.parse(storageKey));
const signatureEncoded = signatureBytes.toString(CryptoJS.enc.Base64);

console.log("Storage Account", pm.variables.get("azure_storage_account"));
console.log("Storage Key", storageKey);

// Finally, make it available for headers
pm.variables.set("header_authorization",
    `SharedKey ${pm.variables.get("azure_storage_account")}:${signatureEncoded}`);
javascript

Next, you need to set the request headers, similar to the table storage example above.

1
2
3
4
GET https://{{azure_storage_account}}.blob.core.windows.net/?restype=service&comp=properties
x-ms-date:{{header_date}}
Authorization:{{header_authorization}}
x-ms-version:2018-03-28

This requests the blob metadata properties. For more details on what is optional and required in the header, read Microsoft's documentation on authorizing with shared keys.

Conclusion

As demonstrated, you can use shared keys from inside Postman to query Azure storage account resources such as blobs and tables. This can be helpful for performing end-to-end API testing. You can run Postman requests on your custom APIs and verify everything is working by querying the storage account.

1