kmerkle
kmerkle

Reputation: 58

Azure DefaultAzureCredential Fails in Visual Studio locally

We are using Azure System Assigned Managed Identity in our Azure environment to authenticate calls between our Azure cloud resources.

We have attempted to implement the DefaultAzureCredential when registering our BlobServiceClient as such:

        services.AddAzureClients(builder =>
        {

            builder.UseCredential(new DefaultAzureCredential());

            string url = string.Format("https://{0}.blob.core.windows.net/{1}",
                                                       blobStorageOptions.AccountName,
                                                       blobStorageOptions.ContainerName);
            builder.AddBlobServiceClient(new Uri(url));
        });

Per the documentation, the DefaultAzureCredential should execute the various TokenCredential implmentations until one of them succeeds. If none succeed, then a CredentialUnavailableException is thrown.

The default sequence of DefaultAzureCredential authentication calls the ManagedIdentityCredential before the VisualStudioCredential is called. So, when the app is run in VS locally, the ManagedIdentityCredential should fail with the VisualStudioCredential eventually succeeding and the code running as designed.

However, the app fails locally with a CredentialUnavailableException thrown when the ManagedIdentityCredential is called instead of continuing to the VisualStudioCredential which should succeed. The app succeeds when it is deployed to our Azure environment. Note that our VS Tools>Options>AzureServiceAuthentication is correctly configured and the accounts have the required permissions.

We also have used this approach:

        services.AddAzureClients(builder =>
        {
            TokenCredential credential = new ChainedTokenCredential(
                new ManagedIdentityCredential(),
                new VisualStudioCredential());

            builder.UseCredential(credential);

            string url = string.Format("https://{0}.blob.core.windows.net/{1}",
                                                       blobStorageOptions.AccountName,
                                                       blobStorageOptions.ContainerName);
            builder.AddBlobServiceClient(new Uri(url));
        });

as recommended here Credential chains in the Azure Identity library for .NET (for better performance).

This also fails locally in the same manner (exception thrown by ManagedIdentityCredential).

If we change the sequence of the preceding code (VisualStudioCredential before ManagedIdentityCredential) the app succeeds both locally and in the Azure environment:

            TokenCredential credential = new ChainedTokenCredential(
                new VisualStudioCredential(),
                new ManagedIdentityCredential());

We can use the previous implementation, but the efficiency gains described in the documentation will not be realized.

My only conclusion is that DefaultAzureCredential does not work as designed. Am I missing something?

Upvotes: 0

Views: 153

Answers (1)

Dasari Kamali
Dasari Kamali

Reputation: 3649

I tried the below code to authenticate Azure Storage locally using DefaultAzureCredential and ManagedIdentityCredential in the Azure Portal.

I have assigned the Storage Blob Data Contributor role to both the Service Principal and the Web App in Azure Storage to avoid the error shown below.

enter image description here

Program.cs :

using Azure.Core;
using Azure.Identity;
using Microsoft.Extensions.Azure;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSwaggerGen();
builder.Services.AddAzureClients(clientBuilder =>
{
    TokenCredential credential;
    if (builder.Environment.IsDevelopment())
    {
        credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions
        {
        });
    }
    else
    {
        credential = new ManagedIdentityCredential();
    }

    clientBuilder.UseCredential(credential);

    string blobUri = "https://<StorageName>.blob.core.windows.net";
    clientBuilder.AddBlobServiceClient(new Uri(blobUri));
});
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "Web API V1");
    c.RoutePrefix = app.Environment.IsDevelopment() ? "swagger" : string.Empty;
});
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

BlobController.cs :

using Azure.Storage.Blobs;
using Microsoft.AspNetCore.Mvc;

namespace WebApplication19.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BlobController : ControllerBase
    {
        private readonly BlobServiceClient _blobServiceClient;
        public BlobController(BlobServiceClient blobServiceClient)
        {
            _blobServiceClient = blobServiceClient;
        }
        [HttpGet("list-blobs")]
        public async Task<IActionResult> ListBlobsAsync()
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient("kamcontainer");
                var blobItems = new List<string>();
                await foreach (var blobItem in containerClient.GetBlobsAsync())
                {
                    blobItems.Add(blobItem.Name);
                }
                return Ok(blobItems);
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Internal server error: {ex.Message}");
            }
        }
    }
}

Local Output :

After assigning the Storage Blob Data Contributor role to the Service Principal, I successfully retrieved the list of blobs.

enter image description here

Azure Web App Output :

enter image description here

Upvotes: 1

Related Questions