admin管理员组文章数量:1279244
Setup
I'm running a .NET 6 web app inside an Azure Container App and accessing Azure Key Vault for secrets and connection strings. Dependency Injection works fine for injecting my custom Key Vault client (wrapper of Azure.Security.KeyVault.Secrets.SecretClient), and I can successfully retrieve secrets after Build() (even from within Program.cs).
Problem
I need to fetch a connection string from Key Vault before Build() so I can pass it to a service constructor when registering dependencies. The SecretClient.GetSecret(keyName) call fails at this point.
I don't face the same problem when I run locally.
Logs
When the Container app is trying to activate the revision (and runs the Program.cs), I see this error from the Container app Log stream:
What I've Tried
- Ensuring Managed Identity is correctly assigned to the Container App.
- Confirming Key Vault permissions (it works later, so permissions shouldn't be the issue)
Question
How can I securely retrieve a secret before calling Build() in a .NET 6 containerized web app? Or is there a better pattern for handling this scenario?
Any insights would be greatly appreciated!
Code
public class KeyVaultService : IKeyVaultService
{
private readonly SecretClient _secretClient;
public KeyVaultService(string keyVaultName)
{
_secretClient = new SecretClient(new Uri($"https://{keyVaultName}.vault.azure"), new DefaultAzureCredential());
}
public string GetSecret(string keyName)
{
KeyVaultSecret secret = _secretClient.GetSecret(keyName);
return secret.Value;
}
}
public class Program
{
public static void Main(string[] args)
{
WebApplicationBuilder? builder = WebApplication.CreateBuilder(args);
// ... configuring logging, etc ...
var keyVault = new KeyVaultService(keyVaultName);
// this is where it fails, but only in the Container app
// locally works fine.
string conn = keyVault.GetSecret("ConnectionString");
// registering a service that needs connectionString in its constructor.
var app = builder.Build();
// after Build() the Key Vault works fine.
keyVault = new KeyVaultService(keyVaultName);
conn = keyVault.GetSecret("ConnectionString");
// omitting unrelated code, like app.Run()
}
}
UPDATE
I tried adding the Key Vault to Configuration and does not work in my Azure Container App (works locally though).
From logs I noticed that before Build() the builder.Configuration["ConnectionString"] returns null or empty string, so I guess that Key Vault was not loaded as a provider.
WebApplicationBuilder? builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddAzureKeyVault(XXXXX, new DefaultAzureCredential());
builder.ConfigureDatabase(builder.Configuration["ConnectionString"]);
var app = builder.Build();
Setup
I'm running a .NET 6 web app inside an Azure Container App and accessing Azure Key Vault for secrets and connection strings. Dependency Injection works fine for injecting my custom Key Vault client (wrapper of Azure.Security.KeyVault.Secrets.SecretClient), and I can successfully retrieve secrets after Build() (even from within Program.cs).
Problem
I need to fetch a connection string from Key Vault before Build() so I can pass it to a service constructor when registering dependencies. The SecretClient.GetSecret(keyName) call fails at this point.
I don't face the same problem when I run locally.
Logs
When the Container app is trying to activate the revision (and runs the Program.cs), I see this error from the Container app Log stream:
What I've Tried
- Ensuring Managed Identity is correctly assigned to the Container App.
- Confirming Key Vault permissions (it works later, so permissions shouldn't be the issue)
Question
How can I securely retrieve a secret before calling Build() in a .NET 6 containerized web app? Or is there a better pattern for handling this scenario?
Any insights would be greatly appreciated!
Code
public class KeyVaultService : IKeyVaultService
{
private readonly SecretClient _secretClient;
public KeyVaultService(string keyVaultName)
{
_secretClient = new SecretClient(new Uri($"https://{keyVaultName}.vault.azure"), new DefaultAzureCredential());
}
public string GetSecret(string keyName)
{
KeyVaultSecret secret = _secretClient.GetSecret(keyName);
return secret.Value;
}
}
public class Program
{
public static void Main(string[] args)
{
WebApplicationBuilder? builder = WebApplication.CreateBuilder(args);
// ... configuring logging, etc ...
var keyVault = new KeyVaultService(keyVaultName);
// this is where it fails, but only in the Container app
// locally works fine.
string conn = keyVault.GetSecret("ConnectionString");
// registering a service that needs connectionString in its constructor.
var app = builder.Build();
// after Build() the Key Vault works fine.
keyVault = new KeyVaultService(keyVaultName);
conn = keyVault.GetSecret("ConnectionString");
// omitting unrelated code, like app.Run()
}
}
UPDATE
I tried adding the Key Vault to Configuration and does not work in my Azure Container App (works locally though).
From logs I noticed that before Build() the builder.Configuration["ConnectionString"] returns null or empty string, so I guess that Key Vault was not loaded as a provider.
WebApplicationBuilder? builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddAzureKeyVault(XXXXX, new DefaultAzureCredential());
builder.ConfigureDatabase(builder.Configuration["ConnectionString"]);
var app = builder.Build();
Share
Improve this question
edited Feb 26 at 15:29
spiros
asked Feb 25 at 10:04
spirosspiros
3815 silver badges11 bronze badges
2
- 1 Please share your code that you've tried. – Sirra Sneha Commented Feb 25 at 10:13
- @SirraSneha, I added the code in the post – spiros Commented Feb 25 at 10:29
1 Answer
Reset to default 1The overall design of loading configuration, registering services, and using IOptions
to expose that configuration to services has gone through a number of revisions during the development of dotnet core. So there are many ways to solve this design problem.
Personally I would implement something like the following. Adding all secrets in the vault as configuration values, then follow the "options pattern" to inject those options into services.
builder.Configuration.AddAzureKeyVault(
new Uri(builder.Configuration["AzureKeyVaultConnectionString"]),
new DefaultAzureCredential()
);
builder.Services.Configure<OptionsType>(
builder.Configuration.GetSection("SectionName")
);
However that will depend on exactly how your connection string is being configured. When calling WebApplication.CreateBuilder
, "only configuration that is necessary for the host" will be loaded immediately. This certainly includes any ASPNETCORE_
environment variables.
The remaining "application configuration providers" will be registered and only loaded while building the web application. You can follow this pattern yourself to register additional application configuration;
builder.ConfigureAppConfiguration((context, builder) => {
builder.AddAzureKeyVault(
new Uri(context.Configuration["AzureKeyVaultConnectionString"]),
new DefaultAzureCredential()
);
});
Similarly you can delay registering services;
builder.ConfigureServices((context, services) => {
// etc
});
Though I would probably just tweak how you are passing in your connection string to ensure it is considered "host configuration".
You should never need to create any instance of your services manually. If you think you need to create a service before calling .Build
, then you are mistaken. If you explain what you are actually trying to do, then I could explain how you are supposed to achieve what you want. Either during the process of building the host, during startup of the host, or lazily when your service dependency is first required.
本文标签: cAzure Key Vault Not Accessible before Build() in NET 6 Container AppStack Overflow
版权声明:本文标题:c# - Azure Key Vault Not Accessible before Build() in .NET 6 Container App - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741213345a2359564.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论