3
votes

I have an ASP .Net Core 3.1 Web API which uses Azure Storage. For this, it was using the Microsoft.Azure.Storage.Blob library. The NuGet package manager informed me that this package was deprecated, and I should rather use Azure.Storage.Blobs. I did this, and proceeded to fix all the broken code as a result of this change. I didn't have many issues until I hit the code which generates a Shared Access Signature (SAS) for the client. Anyway, I managed to make it work, by following this code:

https://docs.microsoft.com/en-us/azure/storage/blobs/sas-service-create?tabs=dotnet

private static Uri GetServiceSasUriForContainer(BlobContainerClient containerClient,
                                          string storedPolicyName = null)
{
    // Check whether this BlobContainerClient object has been authorized with Shared Key.
    if (containerClient.CanGenerateSasUri)
    {
        // Create a SAS token that's valid for one hour.
        BlobSasBuilder sasBuilder = new BlobSasBuilder()
        {
            BlobContainerName = containerClient.Name,
            Resource = "c"
        };

        if (storedPolicyName == null)
        {
            sasBuilder.ExpiresOn = DateTimeOffset.UtcNow.AddHours(1);
            sasBuilder.SetPermissions(BlobContainerSasPermissions.Read);
        }
        else
        {
            sasBuilder.Identifier = storedPolicyName;
        }

        Uri sasUri = containerClient.GenerateSasUri(sasBuilder);
        Console.WriteLine("SAS URI for blob container is: {0}", sasUri);
        Console.WriteLine();

        return sasUri;
    }
    else
    {
        Console.WriteLine(@"BlobContainerClient must be authorized with Shared Key 
                          credentials to create a service SAS.");
        return null;
    }
}

However, the line Uri sasUri = containerClient.GenerateSasUri(sasBuilder); was giving me an error, and after further research I instead used:

StorageSharedKeyCredential credential = new StorageSharedKeyCredential(storageAccountName, storageAccountKey);

However, I also had to comment out the line if (containerClient.CanGenerateSasUri) because it is always false. Apparently it's because "BlobContainerClient must be authorized with Shared Key credentials to create a service SAS." But as far as I know, I am using Shared Key credentials. I am not using Azure AD Authentication (even though I am aware this is the recommended mnethod). My BlobServiceClient is being initialized like this:

string storageConnectionString = $"DefaultEndpointsProtocol=https;AccountName=propworx;AccountKey={storageAccountKey};EndpointSuffix=core.windows.net";
BlobServiceClient blobServiceClient = new BlobServiceClient(storageConnectionString);

Does anyone have any idea why CanGenerateSasUri is false?

1
You're affected by a bug, it was fixed in 12.8.0 version of nuget. More info: github.com/Azure/azure-sdk-for-net/issues/16979user963935
@user963935 is completely right and I even think that this could be "the answer" if it wasn't a comment. The accepted answer fixes the problem but forces us to use the credentials more than we need. If you upgrade the Azure.Storage.Blobs package to 12.8.0 (or Azure.Storage.Files.DataLake to 12.6.0) every object created from the top BlobServiceClient (or system client) will inherit the CanGenerateSasUri's value (true).Luis Gouveia

1 Answers

6
votes

"BlobContainerClient must be authorized with Shared Key credentials to create a service SAS." means that you should directly build the BlobContainerClient instead of BlobServiceClient with credentials, then pass it to GetServiceSasUriForContainer method.

For example, use the code below:

        var storageAccountName = "xxx";
        var storageAccountKey = "xxx";
        var container_name = "xxx";

        StorageSharedKeyCredential credential = new StorageSharedKeyCredential(storageAccountName, storageAccountKey);

        //build the blob container url
        string blobcontainer_url = string.Format("https://{0}.blob.core.windows.net/{1}", storageAccountName, container_name);

        //directly build BlobContainerClient, then pass it to GetServiceSasUriForContainer() method
        BlobContainerClient blobContainer = new BlobContainerClient(new Uri(blobcontainer_url), credential);

        Uri mysasuri = GetServiceSasUriForContainer(blobContainer, null);

The test result:

enter image description here