I have an Azure storage account with no public blob access. I can access blob, table and query via the (.NET) APIs using one of the storage account access keys. For REST I tried the Microsoft demo application on https://docs.microsoft.com/en-us/azure/storage/common/storage-rest-api-auth, of course with my storage account name and one of the storage account access keys. This demo application just lists the blob containers. It results in a HTTP 403 (Forbidden) when trying to connect.
I cannot find a reason. Is the storage account access key the right key to use (I cannot create shared access signatiures for some reason to try them)? Ideas are appreciated.
Here is the complete code (please note that I replaced the storage account name and access key by "xxx"):
using System;
using System.Globalization;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
internal static class Program
{
static string StorageAccountName = "xxx";
static string StorageAccountKey = "xxx";
private static void Main()
{
// List the containers in a storage account.
ListContainersAsyncREST(StorageAccountName, StorageAccountKey, CancellationToken.None).GetAwaiter().GetResult();
Console.WriteLine("Press any key to continue.");
Console.ReadLine();
}
/// <summary>
/// This is the method to call the REST API to retrieve a list of
/// containers in the specific storage account.
/// This will call CreateRESTRequest to create the request,
/// then check the returned status code. If it's OK (200), it will
/// parse the response and show the list of containers found.
/// </summary>
private static async Task ListContainersAsyncREST(string storageAccountName, string storageAccountKey, CancellationToken cancellationToken)
{
// Construct the URI. This will look like this:
// https://myaccount.blob.core.windows.net/resource
String uri = string.Format("http://{0}.blob.core.windows.net?comp=list", storageAccountName);
// Set this to whatever payload you desire. Ours is null because
// we're not passing anything in.
Byte[] requestPayload = null;
//Instantiate the request message with a null payload.
using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri)
{ Content = (requestPayload == null) ? null : new ByteArrayContent(requestPayload) })
{
// Add the request headers for x-ms-date and x-ms-version.
DateTime now = DateTime.UtcNow;
httpRequestMessage.Headers.Add("x-ms-date", now.ToString("R", CultureInfo.InvariantCulture));
httpRequestMessage.Headers.Add("x-ms-version", "2017-04-17");
// If you need any additional headers, add them here before creating
// the authorization header.
// Add the authorization header.
httpRequestMessage.Headers.Authorization = AzureStorageAuthenticationHelper.GetAuthorizationHeader(
storageAccountName, storageAccountKey, now, httpRequestMessage);
// Send the request.
using (HttpResponseMessage httpResponseMessage = await new HttpClient().SendAsync(httpRequestMessage, cancellationToken))
{
// If successful (status code = 200),
// parse the XML response for the container names.
if (httpResponseMessage.StatusCode == HttpStatusCode.OK)
{
String xmlString = await httpResponseMessage.Content.ReadAsStringAsync();
XElement x = XElement.Parse(xmlString);
foreach (XElement container in x.Element("Containers").Elements("Container"))
{
Console.WriteLine("Container name = {0}", container.Element("Name").Value);
}
}
}
}
}
}