For some reason, we are unable to retrieve secret value from Azure KeyVault in Start up. While the secret seems to be available in the methods used during the process via IConfiguration Interface. I guess we are doing it wrong somewhere or may be data from Azure available only services initialization is complete.
We are using .net core 3.1 web api and Azure Key Vault Managed Identity.
Here is the code related to it. Let me know in case if you see anything odd.
KeyVault Secret Name we have: MyApp-AppSettings--Key
in Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
var configurationRoot = ConfigurationBuilder.Build();
var keyVaultUrl = configurationRoot["KeyVaultUrl"];
config.AddAzureKeyVault(new Uri(keyVaultUrl), new DefaultAzureCredential(), new PrefixKeyVaultSecretManager("myApp"));
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Our Startup File is something like below.
public class Startup
{
public Startup(IWebHostEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
ContentRootPath = env.ContentRootPath;
}
public IConfiguration Configuration { get; }
public string ContentRootPath { get; set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddHsts(options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(365);
});
**var conf = Configuration.GetSection("AppSettings").Get<Config>();**
**services.Configure<Config>(Configuration.GetSection("AppSettings"));**
**services.AddSingleton<IAppSettings>(c => conf);**
services.RegisterStorage(new StorageConfiguration
{
ConnectionString = "ConnectionString",
Key = Convert.FromBase64String(**conf.Key**) // conf.Key is "". Does not work
});
services.AddScoped<IProcessHandler, ProcessHandler>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHsts();
app.UseHttpsRedirection();
app.UseHttpStatusCodeExceptionMiddleware();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
private static void LoadMediatorHandlers(IServiceCollection services)
{
foreach (var assembly in Assembly
.GetEntryAssembly()
.GetReferencedAssemblies()
.Select(Assembly.Load)
.Where(name => (name.FullName.Contains("Queries") || name.FullName.Contains("Commands"))))
{
services.AddMediatR(assembly);
}
services.AddMediatR(typeof(Startup));
services.AddScoped<IMediator, Mediator>();
}
}
The value is available in IProcessHandler when we invoke ProcessXyz Method during the process. This was done to test if we are able to retrieve values from KeyVault.
public class ProcessHandler : IProcessHandler
{
private readonly IConfiguration _configuration;
public ProcessHandler(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task<ClassA> ProcessXyz()
{
var KeyVaultValue = _configuration["AppSettings:Key"]; //This works
}
}
Please Note: I have just added the required code and replaced actual names for security issues.
Thanks in advance
CodePudding user response:
In the startup constructor, you create a new configuration builder that has a limited set of sources and lacking the Azure Key Vault provider. So instead of creating a new configuration builder perhaps you could inject the one that is readily available in the DI container.
So change the startup constructor with this.
public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
Configuration = configuration;
ContentRootPath = env.ContentRootPath;
}
Then the configuration prepared in the Program class will be used to inject it in the startup setup and thus take all different default sources that are configured (including the command line source) and the one that is additionally added.