Search code examples
azure-blob-storageazure-storageazure-managed-identity

Creating a SAS URL for Azure BLOB Storage


The following code works. My question is, is it correct? I ask because I've seen a lot of code where it either doesn't work or there are comments saying it's the wrong approach. (I think the API for this as evolved a lot of the examples are still catching up)

var connStr = config.GetConnectionString("AzureStorage");
var blobServiceClient = new BlobServiceClient(connStr);
var container = blobServiceClient.GetBlobContainerClient("organization");

var blobSasBuilder = new BlobSasBuilder
{
    BlobContainerName = "organization",
    BlobName = "thumbnail.png",
    Resource = "b",
    // if no StartsOn or ExpiresOn is specified, the SAS starts when the key is created
    StartsOn = DateTimeOffset.UtcNow.AddMinutes(-5),
    ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(5)
};
blobSasBuilder.SetPermissions(BlobSasPermissions.Read);

StorageSharedKeyCredential sharedKeyCredential = new StorageSharedKeyCredential
    ("louishowe", "abcdefg");
BlobUriBuilder sasUriBuilder = new BlobUriBuilder(container.Uri)
{
    Query = blobSasBuilder.ToSasQueryParameters(sharedKeyCredential).ToString()
};

// Now we have the container SAS Uri we can use to contruct blob client for blobs in the container.
Uri containerSasUri = sasUriBuilder.ToUri();

BlobUriBuilder blobUriBuilder = new BlobUriBuilder(containerSasUri)
{
    BlobName = "thumbnail.png"
};

PageBlobClient sasPageBlob = new PageBlobClient(blobUriBuilder.ToUri());

And second giant question - This requires the storage account key. If I have set managed identity for both my app service and storage account, can that be used to create the StorageSharedKeyCredential()? Because otherwise I need to put the account key up on the server (granted in the key vault, but still better if not there at all).

My guess is it's needed because this SAS generation is all done on the client side and the key is needed to sign the parameter list.


Solution

  • Creating a SAS token using account key is a perfectly valid option. Caveat being the issues you mentioned in your question (you would need to have access to account key in your code).

    Because of this, Microsoft is recommending to create User Delegation SAS. If you have created a Managed Identity for your Web App, then you can use that identity to create a User Delegation SAS token using that identity. However, please note that this managed identity needs Blob Storage Data Roles on the storage account.

    In this scenario, instead of using StorageSharedKeyCredential, you would be using Managed Identity Credential. You would do something like:

    var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions()
    {
        ManagedIdentityClientId = "web-app-managed-identity-id"
    });
    

    and then create a blob service client using something like:

    var blobServiceClient = new BlobServiceClient("https://account.blob.core.windows.net", credential)
    

    Next would be to create a user delegation key using something like:

    DateTimeOffset expiryTime = DateTimeOffset.UtcNow.AddMinutes(5);
    var userDelegationKey = await blobServiceClient.GetUserDelegationKeyAsync(null, expiryTime, CancellationToken.None);
    

    and finally create the SAS token:

    var blobContainerClient = blobServiceClient.GetBlobContainerClient(containerName);
    var sasBuilder = new BlobSasBuilder()
    {
        BlobContainerName = containerName,
        BlobName = blobName,
        Resource = "b",
        ExpiresOn = expiryTime
    };
    sasBuilder.SetPermissions(BlobSasPermissions.Read);
    var blobClient = blobContainerClient.GetBlobClient(blobName);
    var blobUriBuilder = new BlobUriBuilder(blobClient.Uri)
    {
        Sas = sasBuilder.ToSasQueryParameters(userDelegationKey,
            serviceClient.AccountName)
    };