Friday, 19 July 2024

Resolve "System.ArgumentNullException: Value cannot be null. (Parameter 'sharedKeyCredential')" in Generaiting SaS Uri for Azure Storage Blob while using DefaultAzureCredential

 To share an Azure blob for  downloading or editing requires sharing a link with a shared access signature (SaS) with required permissions. The BlobContainerClient.GenerateSasUri Method helps to generate a Uri to share. The  BlobContainerClient.GenerateSasUri Method works only if the BlobServiceClient is created using storage access signature as shown below.

string connectionString = "DefaultEndpointsProtocol=https;AccountName=cheuw001assetssthot;AccountKey=xxxxxxxxxxxxxxxxxxxxxxx==;EndpointSuffix=core.windows.net";
BlobServiceClient blobServiceClient = new(connectionString);

The usage of paswordless authentication using managed identities is the recommended approach to use Azure resources.  If the DefaultAzureCredential is used with managed identity (user assigned or system assigned) to create BlobServiceClient as shown below,  BlobContainerClient.GenerateSasUri Method  failes with error "System.ArgumentNullException: Value cannot be null. (Parameter 'sharedKeyCredential')".

BlobServiceClient blobServiceClient = new(
    new Uri("https://cheuw001assetssthot.blob.core.windows.net/"),
    new DefaultAzureCredential());


The issue


Below code to generate a SaS Uri for blob works fine as it uses the connection string with SaS.

string connectionString = "DefaultEndpointsProtocol=https;AccountName=cheuw001assetssthot;AccountKey=xxxxxxxxxxxxxxxxxxxxxxx==;EndpointSuffix=core.windows.net";
BlobServiceClient blobServiceClient = new(connectionString);

BlobContainerClient blobContainer = blobServiceClient.GetBlobContainerClient("dotnet-1e049c87-ce56-4c54-afc8-0c5a01a97bf3");
BlobClient blob = blobContainer.GetBlobClient("boatnewyork_005.png");
DateTimeOffset startsOn = DateTimeOffset.UtcNow;
DateTimeOffset expiresOn = startsOn.AddHours(1);

Uri uri = blob.GenerateSasUri(BlobSasPermissions.Read, expiresOn);

Console.WriteLine(uri.AbsoluteUri);

However if tblob service client is changed to use DefaultAzureCredential as below, an exception is trown with "System.ArgumentNullException: Value cannot be null. (Parameter 'sharedKeyCredential')"

BlobServiceClient blobServiceClient = new(
    new Uri("https://cheuw001assetssthot.blob.core.windows.net/"),
    new DefaultAzureCredential());

BlobContainerClient blobContainer = blobServiceClient.GetBlobContainerClient("dotnet-1e049c87-ce56-4c54-afc8-0c5a01a97bf3");
BlobClient blob = blobContainer.GetBlobClient("boatnewyork_005.png");
DateTimeOffset startsOn = DateTimeOffset.UtcNow;
DateTimeOffset expiresOn = startsOn.AddHours(1);

Uri uri = blob.GenerateSasUri(BlobSasPermissions.Read, expiresOn);

Console.WriteLine(uri.AbsoluteUri);

How to resolve

To create SaS Uri for a blob with managed identity usage, we have to create a user delegation key. 

BlobServiceClient blobServiceClient = new(
    new Uri("https://cheuw001assetssthot.blob.core.windows.net/"),
    new DefaultAzureCredential());

BlobContainerClient blobContainer = blobServiceClient.GetBlobContainerClient("dotnet-1e049c87-ce56-4c54-afc8-0c5a01a97bf3");
BlobClient blob = blobContainer.GetBlobClient("boatnewyork_005.png");
DateTimeOffset startsOn = DateTimeOffset.UtcNow;
DateTimeOffset expiresOn = startsOn.AddHours(1);

UserDelegationKey userDelegationKey = await blobServiceClient.GetUserDelegationKeyAsync(
    startsOn, expiresOn);

BlobSasBuilder sasBuilder = new()
{
    BlobContainerName = blob.BlobContainerName,
    BlobName = blob.Name,
    Resource = "b",
    StartsOn = startsOn,
    ExpiresOn = expiresOn
};

sasBuilder.SetPermissions(BlobSasPermissions.Read);

BlobUriBuilder blobUriBuilder = new(blob.Uri)
{
    Sas = sasBuilder.ToSasQueryParameters(
            userDelegationKey,
            blobServiceClient.AccountName)
};

Uri uri = blobUriBuilder.ToUri();

Console.WriteLine(uri.AbsoluteUri);

No comments:

Popular Posts