Search code examples
asp.net-coreconfigurationazure-keyvaultappsettings

How to access Azure Key Vault secrets as *configuration values transparently* in a ASP.NET Core Web App which is deployed to Azure App Services?


My question is about the C# code of reading secrets stored in the Key Vault as configuration transparently. For example currently I have this appsettings.json:

"AzureAd": {
 "Instance": "https://login.microsoftonline.com/",
 "Domain": "mydomain.com",
 "TenantId": "...coming from Connected Services/Secrets",
 "ClientId": "...coming from Connected Services/Secrets",
 "ClientSecret": "...coming from Connected Services/Secrets",
 "CallbackPath": "/signin-oidc",
 "Scopes": "access_as_user"

},

In development time those secrets are configured using Services/Secrets, so the secrets are not pushed to the repo.

How can I use and configure a configuration reader in the release build, which reads those configuration values from my Azure Key Vault transparently? In transparently I mean, the application itself is not aware where the configured secret is coming?

For example I have the statement builder.Configuration.GetSection("AzureAd") and I would like this work transparently...

(I do know how to deploy my ASP.NET Core App to Azure App Services, how to create secrets in Azure Key Vault, and how to give access to the deployed App Service to the Key Vault, so this question is not about those)


Solution

  • You could do the following.

    For hierarchical Keys in the KeyVault, you can create them like:

    AzureAd--Secret1
    AzureAd--Level1--Secret2
    

    Your local secrets should be configured in your secrets.json. Right click on your project: "Manage User Secrets".

    Introduce a IS_LOCAL Flag to your launchSettings.json:

     "Your project Local": {
         "commandName": "Project",
     "launchBrowser": true,
         "applicationUrl": "https://localhost:5000",
         "launchUrl": "api/v2/swagger",
     "environmentVariables": {
         "ASPNETCORE_ENVIRONMENT": "Development",
         "IS_LOCAL": "true"                      
     }
    

    Then in your Startup.cs or Program.cs:

      if (!env.IsLocal())
      {
          configuration.AddAzureKeyVault(
              new Uri(configuration.GetValue<string>("YourKeyToAzureKeyVaultCredentials")),
              new DefaultAzureCredential());
      }
    

    IsLocal() is an Extension:

    public static class HostEnvironmentExtensions
    {
        public static bool IsLocal(this IHostEnvironment _) 
            => Environment.GetEnvironmentVariable("IS_LOCAL")?.Equals("true") ?? false;
    }
    

    And also in your Program.cs:

     services.Configure<YourConfiguration>(configuration);
    

    YourConfiguration Class should look like this:

    public class YourConfiguration 
    {
        public SecretConfiguration AzureAd{ get; set; }
    }
    

    And the SecretConfiguration is just a POCO class:

     public class SecretConfiguration
     {
         public string Secret1{ get; set; }
         public string Secret2{ get; set; }
     }
    

    Now you can transparently inject YourConfiguration Class in your services - and it will use your secrets.json in local mode or your Azure Key Vault on the server:

    // When Service is Scoped
    public class ServiceA (IOptionsSnapshot<YourConfiguration> configuration)
    {
          public void SomeMethod()
          {
               var secret1 = configuration.Value.AzureAd.Secret1;   
          }
    }