J Weezy
J Weezy

Reputation: 3945

How to get AzureKeyVault Secret without storing the ClientSecret in appsettings.json?

How to get AzureKeyVault Secret without storing the ClientSecret in appsettings.json? We have a WinForms application in .NET 5 using Dependency Injection and we want to add two services to the ServiceProvider while only requiring one SSO login prompt and removing the KeyVault secret string from the appsettings.json file.

The call to builder.AddAzureKeyVault(SecretClient, new KeyVaultSecretManager()) works when signed in to Azure with Visual Studio. This breaks when we deploy the application for a virtual machine where Visual Studio is not signed in. We can get this to work by passing in a secret value, but we don't want to store that in either code or appsettings. How do we store the secret in the KeyVault and then retrieve that so that we can add the AddAzureKeyVault service?

When we log out of Visual Studio (i.e., ExcludeVisualStudioCredential = true) then we get the following error:

Azure.Identity.CredentialUnavailableException: 'DefaultAzureCredential failed to retrieve a token from the included credentials.

  • EnvironmentCredential authentication unavailable. Environment variables are not fully configured.
  • ManagedIdentityCredential authentication unavailable. No Managed Identity endpoint found.
  • Process "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\Extensions\gwrwzu2y.rwb\TokenService\Microsoft.Asal.TokenService.exe" has failed with unexpected error: TS003: Error, TS005: No accounts found. Please go to Tools->Options->Azure Services Authentication, and add an account to be to authenticate to Azure services during development..
  • Stored credentials not found. Need to authenticate user in VSCode Azure Account.
  • Azure CLI not installed
  • Please run 'Connect-AzAccount' to set up account.'

Note 1: We are using SSO and can retrieve the AccessToken. So, we have access to the AccessToken.

Note 2: we are using SlowCheetah for multiple environment configs, so we do not have ENVIRONMENT_VARIABLES setup.

From Program.cs

Static Void Main()
{
...
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
    .ConfigureAppConfiguration((context, builder) =>
    {
        builder.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
            ;

        builder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            ;

        // Set the Configuration. We need to build the Configuration here so that we can get access to the Appsettings in order to
        // set the SecretClient and add the Azure Key Vault. We will then need to set the Configuration again in order to capture this
        // new service.
        Configuration = builder.Build();

        SetSecretClient(Configuration);

        // Add the Azure Key Vault. This only works when signed into Azure via Visual Studio
        builder.AddAzureKeyVault(SecretClient, new KeyVaultSecretManager());
    })
    .ConfigureServices((context, services) =>
    {
        Configuration = context.Configuration;

        SignInUserSSO();

        ConfigureServices(Configuration, services);
    })
    .Build();
...
}

private static void SetSecretClient(IConfiguration objConfiguration)
{
    DefaultAzureCredentialOptions objDefaultAzureCredentialOptions;
    SecretClientOptions objSecretClientOptions;

    string strAzureKeyVaultResourceIdentifier = objConfiguration.GetValue<string>("Azure:FirmId:ResourceIdentifiers:KeyVault");
    string strAzureKeyVaultName = objConfiguration.GetValue<string>("Azure:FirmId:KeyVaults:NameOfKeyVault");
    string strAzureKeyVaultUri = strAzureKeyVaultResourceIdentifier.Replace("{KeyVaultName}", strAzureKeyVaultName);

    // Set the options on the SecretClient. These are default values recommended by Microsoft.
    objSecretClientOptions = new SecretClientOptions()
    {
        Retry =
        {
            Delay= TimeSpan.FromSeconds(2),
            MaxDelay = TimeSpan.FromSeconds(16),
            MaxRetries = 5,
            Mode = RetryMode.Exponential
            }
    };

    objDefaultAzureCredentialOptions = new DefaultAzureCredentialOptions
    {
        ExcludeEnvironmentCredential = true,
        ExcludeManagedIdentityCredential = false,
        ExcludeSharedTokenCacheCredential = true,
        ExcludeVisualStudioCredential = true,
        ExcludeVisualStudioCodeCredential = true,
        ExcludeAzureCliCredential = true,
        ExcludeInteractiveBrowserCredential = true
    };

    SecretClient = new SecretClient(
        vaultUri: new Uri(strAzureKeyVaultUri),
        credential: new DefaultAzureCredential(objDefaultAzureCredentialOptions),
        objSecretClientOptions
        );
}

Upvotes: 0

Views: 1798

Answers (1)

Heath
Heath

Reputation: 3292

The DefaultAzureCredential uses a number of authentication methods to support both production and development environments without changing code. So while you can use your Visual Studio, Azure CLI, or other credentials during development, you can use environment variables with service principals or managed identity in production. For VMs, managed identity is probably the easiest to configure, and for services that don't support managed identity you can use environment variables configured on your service that are authorized to access your Key Vault.

FWIW, recommended default retry settings are already the default without specifying a SecretClientOptions instance if you want to simplify code.

Upvotes: 2

Related Questions