1
votes

I am using code copied and modified from this MSDN article:

MSDN article about Azure SAS usage

I am using the Azure Storage Emulator and can generate the SAS. Here is an example:

http://127.0.0.1:10000/devstoreaccount1/7373df60-ad5f-462e-a55d-15c21c2de0e1?sv=2015-04-05&sr=c&si=ac&sig=bQAsuNUsj6MycN0aTyurVugHBMOlokwsXJA9xv7VeiU%3D

I can use the Edge browser to list the blob container by appending:

&comp=list&restype=container

so that my link now looks like this:

http://127.0.0.1:10000/devstoreaccount1/7373df60-ad5f-462e-a55d-15c21c2de0e1?sv=2015-04-05&sr=c&si=ac&sig=bQAsuNUsj6MycN0aTyurVugHBMOlokwsXJA9xv7VeiU%3D&comp=list&restype=container

This makes me think that the SAS is correct and the storage emulator is working. The browser displays the info for the container and all of the blobs in it.

I can check the storage emulator log and see this message:

4/21/2016 3:56:10 PM [AuthorizationFailure] [ActivityId=a79d230e-6596-4e43-8ef9-58943ee91b58] Unauthorized: Signed access not supported for this request with FailureReason InvalidOperationSAS

Here is the code that I use to create the SAS:

    String policyName = "ac";

var storedPolicy = new SharedAccessBlobPolicy()
{
   SharedAccessExpiryTime = DateTime.UtcNow.AddHours(expireHours),
   Permissions = SharedAccessBlobPermissions.Read |
   SharedAccessBlobPermissions.List |
   SharedAccessBlobPermissions.Delete
};

var permissions = container.GetPermissions();

permissions.SharedAccessPolicies.Clear();

permissions.SharedAccessPolicies.Add(policyName, storedPolicy);

container.SetPermissions(permissions);

string sasContainerToken = container.GetSharedAccessSignature(null, policyName);

// Return the URI string for the container, including the SAS token.

return container.Uri + sasContainerToken;

And here is the code that I use to create the CloudBlobContainer using the SAS:

CloudBlobContainer container = new CloudBlobContainer( new Uri(sas) );   // AzureBlob.GetBlobContainer(sas); // gets a new container

if ( ! container.Exists() ) // throws exception
{
   throw new Exception("Container no longer exists for sas " + sas);
}

container.FetchAttributes();

Here is the exception:

   Microsoft.WindowsAzure.Storage.StorageException: The remote server returned an error: (403) Forbidden. ---> System.Net.WebException: The remote server returned an error: (403) Forbidden.
   at System.Net.HttpWebRequest.GetResponse()
   at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteSync[T](RESTCommand`1 cmd, IRetryPolicy policy, OperationContext operationContext) in c:\\Program Files (x86)\\Jenkins\\workspace\\release_dotnet_master\\Lib\\ClassLibraryCommon\\Core\\Executor\\Executor.cs:line 677
   --- End of inner exception stack trace ---
   at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteSync[T](RESTCommand`1 cmd, IRetryPolicy policy, OperationContext operationContext) in c:\\Program Files (x86)\\Jenkins\\workspace\\release_dotnet_master\\Lib\\ClassLibraryCommon\\Core\\Executor\\Executor.cs:line 604
   at Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer.Exists(Boolean primaryOnly, BlobRequestOptions requestOptions, OperationContext operationContext) in c:
    \\Program Files (x86)\\Jenkins\\workspace\\release_dotnet_master\\Lib\\ClassLibraryCommon\\Blob\\CloudBlobContainer.cs:line 1406
   at Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer.Exists(BlobRequestOptions requestOptions, OperationContext operationContext) in c:\\Program Files (x86)\\Jenkins\\workspace\\release_dotnet_master\\Lib\\ClassLibraryCommon\\Blob\\CloudBlobContainer.cs:line 1393

Here is a link to an article that seems to be a distant relative.

SO question about SAS

2

2 Answers

3
votes

The reason your code is failing is because what you have created is a Service Shared Access Signature (Service SAS) instead of Account Shared Access Signature (Account SAS). In order to create a blob container, you would need to use an Account SAS instead of Service SAS.

From this page: https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx

An account-level SAS, introduced with version 2015-04-05. The account SAS delegates access to resources in one or more of the storage services. All of the operations available via a service SAS are also available via an account SAS. Additionally, with the account SAS, you can delegate access to operations that apply to a given service, such as Get/Set Service Properties and Get Service Stats. You can also delegate access to read, write, and delete operations on blob containers, tables, queues, and file shares that are not permitted with a service SAS. See Constructing an Account SAS for more information about account SAS.

Here's a sample code to create an Account SAS and use it to create a blob container:

        var storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
        var blobClient = storageAccount.CreateCloudBlobClient();
        var blobSharedAccessPolicy = new SharedAccessAccountPolicy()
        {
            Services = SharedAccessAccountServices.Blob,
            SharedAccessExpiryTime = DateTime.UtcNow.AddHours(1),
            Permissions = SharedAccessAccountPermissions.Write,
            ResourceTypes = SharedAccessAccountResourceTypes.Container
        };
        var sas = storageAccount.GetSharedAccessSignature(blobSharedAccessPolicy);
        var containerName = "created-using-account-sas";
        var containerUri = string.Format("{0}{1}", storageAccount.BlobEndpoint, containerName);
        var blobContainer = new CloudBlobContainer(new Uri(containerUri), new StorageCredentials(sas));
        blobContainer.Create();
        Console.WriteLine("Container created....");
0
votes

I'm not sure what you are running into exactly, but this is something that I put together based on information at https://azure.microsoft.com/en-us/documentation/articles/storage-dotnet-shared-access-signature-part-2/

Maybe compare your code with mine and see what might be different

public string CreateSharedAccessSignature(string blobUri, int timeout, TimeUnitType timeUnit)
    {
        try
        {
            var uri = new Uri(blobUri);
            var cloudBlob = _context.CloudBlobClient.GetBlobReferenceFromServer(uri);

            if (!cloudBlob.Exists())
            {
                throw new StorageException("Blob does not exist");
            }

            var sasConstraints = new SharedAccessBlobPolicy
            {
                SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5),
                Permissions = SharedAccessBlobPermissions.Read
            };

            //The shared access signature will be valid immediately.
            switch(timeUnit)
            {
                case TimeUnitType.Days:
                    sasConstraints.SharedAccessExpiryTime = DateTime.UtcNow.AddDays(timeout);
                    break;
                case TimeUnitType.Hours:
                    sasConstraints.SharedAccessExpiryTime = DateTime.UtcNow.AddHours(timeout);
                    break;
                case TimeUnitType.Minutes:
                    sasConstraints.SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(timeout);
                    break;
                case TimeUnitType.Seconds:
                    sasConstraints.SharedAccessExpiryTime = DateTime.UtcNow.AddSeconds(timeout);
                    break;
            }

            //Generate the shared access signature on the blob, setting the constraints directly on the signature.
            string sasBlobToken = cloudBlob.GetSharedAccessSignature(sasConstraints);

            return cloudBlob.Uri + sasBlobToken;
        }
        catch(Exception ex)
        {
            //new RaygunClient().Send(ex);
            throw;
        }
    }