Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This article explains how to use the Azure Key Vault configuration provider to load app configuration values from Key Vault secrets. Key Vault is a cloud-based service that helps safeguard cryptographic keys and secrets used by apps and services. Common scenarios for using Key Vault with ASP.NET Core apps include:
- Controlling access to sensitive configuration data.
- Meeting the requirement for FIPS 140-2 Level 2 validated Hardware Security Modules (HSMs) when storing configuration data.
Packages
Add package references for the following packages:
Sample app
The sample app runs in either of two modes determined by the #define preprocessor directive at the top of the Program.cs file:
Certificate: Demonstrates how to use a Key Vault Client ID and X.509 certificate to access secrets stored in a key vault. You can run the sample from any location, whether deployed to Azure App Service or any host that can serve an ASP.NET Core app.Managed: Demonstrates how to use Managed identities for Azure resources. The managed identity authenticates the app to Key Vault with Managed identities for Azure resources without storing credentials in the application code or configuration. TheManagedversion of the sample must be deployed to Azure. Follow the guidance in the Use the managed identities for Azure resources section.
For more information about configuring a sample app by using #define, see Preprocessor directives in sample code.
View or download sample code (how to download)
Secret storage in the Development environment
Set secrets locally by using the Secret Manager tool. When the sample app runs on the local machine in the Development environment, secrets are loaded from the local user secrets store.
Secret Manager requires a <UserSecretsId> property in the application project file. Set the property value ({GUID}) to any unique GUID:
<PropertyGroup>
<UserSecretsId>{GUID}</UserSecretsId>
</PropertyGroup>
Secrets are created as name-value pairs. Hierarchical values (configuration sections) use colons (:) as separators in ASP.NET Core configuration key names.
Secret Manager is used in a command shell (or terminal) opened to the project's content root, where {SECRET NAME} is the name and {SECRET VALUE} is the value:
dotnet user-secrets set "{SECRET NAME}" "{SECRET VALUE}"
Run the following commands in a command shell from the project's content root. The commands set the secrets for the sample app:
dotnet user-secrets set "SecretName" "secret_value_1_dev"
dotnet user-secrets set "Section:SecretName" "secret_value_2_dev"
When these secrets are stored in Key Vault, as described in the Secret storage in the Production environment with Key Vault section, the _dev suffix changes to _prod. The suffix provides a visual cue in the application output indicating the source of the configuration values.
Secret storage in the Production environment with Key Vault
Create an Azure key vault and store the sample application secrets by completing the following steps. For more information, see Quickstart: Set and retrieve a secret from Key Vault with the Azure CLI.
Open Azure Cloud Shell by using any one of the following methods in the Azure portal:
- Select Try It in the upper-right corner of a code block. Use the search string "Azure CLI" in the text box.
- Open Cloud Shell in your browser with the Launch Cloud Shell button.
- Select the Cloud Shell button on the menu in the upper-right corner of the Azure portal.
For more information, see the Azure CLI documentation and Overview of Azure Cloud Shell.
If you aren't already authenticated, sign in with the
az logincommand.Create a resource group with the following command, where
{RESOURCE GROUP NAME}is the name for the new resource group and{LOCATION}is the Azure region:az group create --name "{RESOURCE GROUP NAME}" --location {LOCATION}Create an Azure key vault in the resource group with the following command, where
{KEY VAULT NAME}is the name for the new vault and{LOCATION}is the Azure region:az keyvault create --name {KEY VAULT NAME} --resource-group "{RESOURCE GROUP NAME}" --location {LOCATION}Create secrets in the vault as name-value pairs.
Key Vault secret names can consist of alphanumeric characters and dashes (
-). Hierarchical values (configuration sections) use two dashes or hyphens (--) as a delimiter. The colon character isn't allowed in Key Vault secret names. Colons delimit a section from a subkey in ASP.NET Core configuration. The two-dash sequence is replaced with a colon when the secrets are loaded into the application configuration.The following secrets are for use with the sample app. The values include a
_prodsuffix that distinguishes them from the_devsuffix values loaded in theDevelopmentenvironment via Secret Manager. Replace{KEY VAULT NAME}with the name of the key vault you created earlier:az keyvault secret set --vault-name {KEY VAULT NAME} --name "SecretName" --value "secret_value_1_prod" az keyvault secret set --vault-name {KEY VAULT NAME} --name "Section--SecretName" --value "secret_value_2_prod"
Use Application ID and X.509 certificate for non-Azure-hosted apps
Configure Key Vault and the app to use a Microsoft Entra ID Application ID and X.509 certificate to authenticate to a vault when the app is hosted outside of Azure. For more information, see About keys, secrets, and certificates.
Note
Although using an Application ID and X.509 certificate is supported for apps hosted in Azure, this approach isn't recommended. Instead, use Managed identities for Azure resources when hosting an app in Azure. Managed identities don't require storing a certificate in the app or in the Development environment.
The sample app uses an Application ID and X.509 certificate when the #define preprocessor directive at the top of the Program.cs file is set to Certificate.
Create a PKCS#12 archive (.pfx) certificate. Options for creating certificates include New-SelfSignedCertificate on Windows and OpenSSL.
Install the certificate into the current user's personal certificate store. Marking the key as exportable is optional. Note the certificate's thumbprint, which is used later in this process.
Export the PKCS#12 archive (.pfx) certificate as a DER-encoded certificate (.cer).
Register the app with Microsoft Entra ID (App registrations).
Upload the DER-encoded certificate (.cer) to Microsoft Entra ID:
- Select the app in Microsoft Entra ID.
- Go to Certificates & secrets.
- Select Upload certificate to upload the certificate, which contains the public key. A .cer, .pem, or .crt certificate is acceptable.
Store the key vault name, Application ID, and certificate thumbprint in the application appsettings.json file.
In the Azure portal, go to Key Vaults.
Select the key vault you created in the Secret storage in the Production environment with Azure Key Vault section.
Select Access policies, and then select Add Access Policy.
Open Secret permissions and provide the app with Get and List permissions.
Choose Select principal. Select the registered app by name, and then choose Select.
Select OK, and then select Save.
Deploy the app.
The Certificate sample app obtains its configuration values from IConfigurationRoot with the same name as the secret name:
- Nonhierarchical values: The value for
SecretNameis obtained withconfig["SecretName"]. - Hierarchical values (sections): Use colon (
:) notation or the GetSection method. Use one of the following approaches to obtain the configuration value:config["Section:SecretName"]config.GetSection("Section")["SecretName"]
The operating system manages the X.509 certificate. The app calls the AddAzureKeyVault method with values supplied by the appsettings.json file:
using System.Security.Cryptography.X509Certificates;
using Azure.Identity;
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsProduction())
{
using var x509Store = new X509Store(StoreLocation.CurrentUser);
x509Store.Open(OpenFlags.ReadOnly);
var x509Certificate = x509Store.Certificates
.Find(
X509FindType.FindByThumbprint,
builder.Configuration["AzureADCertThumbprint"],
validOnly: false)
.OfType<X509Certificate2>()
.Single();
builder.Configuration.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new ClientCertificateCredential(
builder.Configuration["AzureADDirectoryId"],
builder.Configuration["AzureADApplicationId"],
x509Certificate));
}
var app = builder.Build();
Example values:
- Key vault name:
contosovault - Application ID:
00001111-aaaa-2222-bbbb-3333cccc4444 - Certificate thumbprint:
fe14593dd66b2406c5269d742d04b6e1ab03adb1
The values in the appsettings.json file:
{
"KeyVaultName": "Key Vault Name",
"AzureADApplicationId": "Azure AD Application ID",
"AzureADCertThumbprint": "Azure AD Certificate Thumbprint",
"AzureADDirectoryId": "Azure AD Directory ID"
}
When you run the app, a webpage shows the loaded secret values. In the Development environment, secret values load with the _dev suffix. In the Production environment, the values load with the _prod suffix.
Use managed identities for Azure resources
An app deployed to Azure can take advantage of Managed identities for Azure resources. A managed identity allows the app to authenticate with Azure Key Vault by using Microsoft Entra ID authentication without storing credentials in the application code or configuration.
The sample app uses a system-assigned managed identity when the #define preprocessor directive at the top of the Program.cs file is set to Managed. To create a managed identity for an Azure App Service app, see How to use managed identities for App Service and Azure Functions. After the managed identity is created, take note of the application Object ID shown in the Azure portal on the Identity panel of the App Service.
Enter the key vault name into the application appsettings.json file. The sample app doesn't require an Application ID and Password (Client Secret) when set to the Managed version, so you can ignore those configuration entries. The app is deployed to Azure, and Azure authenticates the app to access Key Vault only by using the vault name stored in the appsettings.json file.
Deploy the sample app to Azure App Service.
Using the Azure CLI and the app's Object ID, provide the app with list and get permissions to access the key vault:
az keyvault set-policy --name {KEY VAULT NAME} --object-id {OBJECT ID} --secret-permissions get list
Restart the app by using the Azure CLI, Azure PowerShell, or the Azure portal.
The sample app creates an instance of the DefaultAzureCredential class. The credential attempts to obtain an access token from environment for Azure resources:
using Azure.Identity;
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsProduction())
{
builder.Configuration.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential());
}
Note
The preceding example uses the DefaultAzureCredential class to simplify authentication when developing apps that deploy to Azure. This approach combines credentials used in Azure hosting environments with credentials used in local development. When you move the implementation to production, an alternative is a better choice, such as the ManagedIdentityCredential class. For more information, see Authenticate Azure-hosted .NET apps to Azure resources by using a system-assigned managed identity.
Example name for the key vault: contosovault
The name value in the appsettings.json file:
{
"KeyVaultName": "Key Vault Name"
}
For apps that use a user-assigned managed identity, configure the managed identity's Client ID by using one of the following approaches:
Set the
AZURE_CLIENT_IDenvironment variable.Set the DefaultAzureCredentialOptions.ManagedIdentityClientId property when calling the
AddAzureKeyVaultmethod:builder.Configuration.AddAzureKeyVault( new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"), new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = builder.Configuration["AzureADManagedIdentityClientId"] }));
When you run the app, a webpage shows the loaded secret values. In the Development environment, secret values have the _dev suffix because they're provided by Secret Manager. In the Production environment, the values load with the _prod suffix because they're provided by Azure Key Vault.
If you receive an Access denied error, confirm the app is registered with Microsoft Entra ID and has access to the vault. Confirm you restarted the service in Azure.
For information on using the provider with a managed identity and Azure Pipelines, see Connect to Azure with an Azure Resource Manager service connection.
Configuration options
The AddAzureKeyVault method can accept an AzureKeyVaultConfigurationOptions object:
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
builder.Configuration.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential(),
new AzureKeyVaultConfigurationOptions
{
// ...
});
The AzureKeyVaultConfigurationOptions object contains the following properties:
| Property | Description |
|---|---|
| Manager | Specifies a KeyVaultSecretManager instance used to control secret loading. |
| ReloadInterval | Specifies the TimeSpan to wait between attempts at polling the key vault for changes. The default value is null (configuration isn't reloaded). |
Use a key name prefix
The AddAzureKeyVault method provides an overload that accepts an implementation of KeyVaultSecretManager, which allows you to control how Key Vault secrets are converted into configuration keys. For example, you can implement the interface to load secret values based on a prefix value you provide at app startup. This technique allows you, for example, to load secrets based on the version of the app.
Important
Don't use prefixes on Key Vault secrets to:
- Place secrets for multiple apps into the same key vault.
- Place environmental secrets (for example,
DevelopmentversusProductionsecrets) into the same key vault.
Different apps and development/production environments should use separate key vaults to isolate app environments for the highest level of security.
In the following example, a secret is established in Key Vault (by using Secret Manager for the Development environment) for 5000-AppSecret (periods aren't allowed in Key Vault secret names). This secret represents an app secret for version 5.0.0.0 of the app. For another version of the app, 5.1.0.0, a secret is added to the same key vault (by using Secret Manager) for 5100-AppSecret. Each app version loads its versioned secret value into its configuration as AppSecret, removing the version as it loads the secret.
The AddAzureKeyVault method is called with a custom KeyVaultSecretManager implementation:
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
builder.Configuration.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential(),
new SamplePrefixKeyVaultSecretManager("5000"));
The implementation reacts to the version prefixes of secrets to load the proper secret into configuration:
- The
Loadmethod loads a secret when its name starts with the prefix. Other secrets aren't loaded. - The
GetKeymethod:- Removes the prefix from the secret name.
- Replaces two dashes (
--) in any name with theKeyDelimiter. This delimiter (usually a colon:) is used in configuration because Key Vault doesn't allow a colon (:) in secret names.
public class SamplePrefixKeyVaultSecretManager : KeyVaultSecretManager
{
private readonly string _prefix;
public SamplePrefixKeyVaultSecretManager(string prefix)
=> _prefix = $"{prefix}-";
public override bool Load(SecretProperties properties)
=> properties.Name.StartsWith(_prefix);
public override string GetKey(KeyVaultSecret secret)
=> secret.Name[_prefix.Length..].Replace("--", ConfigurationPath.KeyDelimiter);
}
A provider algorithm calls the Load method and iterates through the key vault secrets to find the version-prefixed secrets. When a version prefix is found with Load, the algorithm uses the GetKey method to return the configuration name of the secret name. It removes the version prefix from the secret's name. The rest of the secret name is returned for loading into the application configuration name-value pairs.
When you implement this approach, complete the following steps:
Specify the application version in the application project file. In the following example, the application version is set to
5.0.0.0:<PropertyGroup> <Version>5.0.0.0</Version> </PropertyGroup>Confirm the
<UserSecretsId>property is defined in the application project file, where{GUID}is a user-supplied GUID:<PropertyGroup> <UserSecretsId>{GUID}</UserSecretsId> </PropertyGroup>Save the following secrets locally by using Secret Manager:
dotnet user-secrets set "5000-AppSecret" "5.0.0.0_secret_value_dev" dotnet user-secrets set "5100-AppSecret" "5.1.0.0_secret_value_dev"Save the secrets in Azure Key Vault by using the following Azure CLI commands:
az keyvault secret set --vault-name {KEY VAULT NAME} --name "5000-AppSecret" --value "5.0.0.0_secret_value_prod" az keyvault secret set --vault-name {KEY VAULT NAME} --name "5100-AppSecret" --value "5.1.0.0_secret_value_prod"
The process completes the following tasks:
When the app runs, the implementation loads the key vault secrets. The string secret for
5000-AppSecretis matched to the application version specified in the app's project file (5.0.0.0).The version,
5000(with the dash), is stripped from the key name. Throughout the app, reading configuration with the keyAppSecretloads the secret value.If the application version is changed in the project file to
5.1.0.0and the app is run again, the secret value returned is5.1.0.0_secret_value_devin theDevelopmentenvironment and5.1.0.0_secret_value_prodinProduction.
Note
You can also provide your own SecretClient implementation to the AddAzureKeyVault method. A custom client enables sharing a single instance of the client across the app.
Bind an array to a class
The provider can read configuration values into an array for binding to a POCO array.
When reading from a configuration source that allows keys to contain colon (:) separators, a numeric key segment is used to distinguish the keys that make up an array (:0:, :1:, … :{n}:). For more information, see Configuration: Bind an array to a class.
Key Vault keys can't use a colon (:) as a separator. The approach described in this article uses double dashes (--) as a separator for hierarchical values (sections). Array keys are stored in Key Vault with double dashes and numeric key segments (--0--, --1--, … --{n}--).
Examine the following Serilog logging provider configuration supplied in a JSON file. There are two object literals defined in the WriteTo array that reflect two Serilog sinks, which describe destinations for logging output:
"Serilog": {
"WriteTo": [
{
"Name": "AzureTableStorage",
"Args": {
"storageTableName": "logs",
"connectionString": "DefaultEnd...ountKey=Eby8...GMGw=="
}
},
{
"Name": "AzureDocumentDB",
"Args": {
"endpointUrl": "https://contoso.documents.azure.com:443",
"authorizationKey": "Eby8...GMGw=="
}
}
]
}
The configuration in the JSON file is stored in Key Vault by using double dash (--) notation and numeric segments:
| Key | Value |
|---|---|
Serilog--WriteTo--0--Name |
AzureTableStorage |
Serilog--WriteTo--0--Args--storageTableName |
logs |
Serilog--WriteTo--0--Args--connectionString |
DefaultEnd...ountKey=Eby8...GMGw== |
Serilog--WriteTo--1--Name |
AzureDocumentDB |
Serilog--WriteTo--1--Args--endpointUrl |
https://contoso.documents.azure.com:443 |
Serilog--WriteTo--1--Args--authorizationKey |
Eby8...GMGw== |
Reload secrets
By default, the configuration provider caches secrets for the application lifetime. The app ignores secrets that are later disabled or updated in the key vault.
To reload secrets, call the IConfigurationRoot.Reload method:
config.Reload();
To reload secrets periodically, at a specified interval, set the AzureKeyVaultConfigurationOptions.ReloadInterval property. For more information, see Configuration options.
Disabled and expired secrets
Expired secrets are included by default in the configuration provider. To exclude values for these secrets in app configuration, update the expired secret or provide the configuration by using a custom configuration provider:
class SampleKeyVaultSecretManager : KeyVaultSecretManager
{
public override bool Load(SecretProperties properties) =>
properties.ExpiresOn.HasValue &&
properties.ExpiresOn.Value > DateTimeOffset.Now;
}
Pass this custom KeyVaultSecretManager provider to the AddAzureKeyVault method:
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
builder.Configuration.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential(),
new SampleKeyVaultSecretManager());
Disabled secrets can't be retrieved from Key Vault and are never included.
Note
The preceding example uses the DefaultAzureCredential class to simplify authentication when developing apps that deploy to Azure. This approach combines credentials used in Azure hosting environments with credentials used in local development. When you move the implementation to production, an alternative is a better choice, such as the ManagedIdentityCredential class. For more information, see Authenticate Azure-hosted .NET apps to Azure resources by using a system-assigned managed identity.
Troubleshoot configuration load issues
When the app fails to load configuration by using the provider, an error message is written to the ASP.NET Core logging infrastructure.
The following conditions can prevent configuration from loading:
- The app or certificate isn't configured correctly in Microsoft Entra ID.
- The vault doesn't exist in Azure Key Vault.
- The app isn't authorized to access the vault.
- The access policy doesn't include
GetandListpermissions. - In the vault, the configuration data (name-value pair) is incorrectly named, missing, or disabled.
- The app has the wrong key vault name (
KeyVaultName), Microsoft Entra ID Application ID (AzureADApplicationId), Microsoft Entra ID certificate thumbprint (AzureADCertThumbprint), or Microsoft Entra ID Directory ID (AzureADDirectoryId). - When the Key Vault access policy is added for the app, the policy is successfully created, but the user didn't select Save in the Access policies dialog.
Related content
This article explains how to use the Azure Key Vault configuration provider to load app configuration values from Azure Key Vault secrets. Azure Key Vault is a cloud-based service that helps safeguard cryptographic keys and secrets used by apps and services. Common scenarios for using Azure Key Vault with ASP.NET Core apps include:
- Controlling access to sensitive configuration data.
- Meeting the requirement for FIPS 140-2 Level 2 validated Hardware Security Modules (HSMs) when storing configuration data.
Packages
Add package references for the following packages:
Sample app
The sample app runs in either of two modes determined by the #define preprocessor directive at the top of Program.cs:
Certificate: Demonstrates using an Azure Key Vault Client ID and X.509 certificate to access secrets stored in Azure Key Vault. This sample can be run from any location, whether deployed to Azure App Service or any host that can serve an ASP.NET Core app.Managed: Demonstrates how to use Managed identities for Azure resources. The managed identity authenticates the app to Azure Key Vault with Managed identities for Azure resources without credentials stored in the app's code or configuration. When using managed identities to authenticate, a Managed identities for Azure resources app ID and Password (Client Secret) aren't required. TheManagedversion of the sample must be deployed to Azure. Follow the guidance in the Use the managed identities for Azure resources section.
For more information configuring a sample app using preprocessor directives (#define), see Overview of ASP.NET Core.
View or download sample code (how to download)
Secret storage in the Development environment
Set secrets locally using Secret Manager. When the sample app runs on the local machine in the Development environment, secrets are loaded from the local user secrets store.
Secret Manager requires a <UserSecretsId> property in the app's project file. Set the property value ({GUID}) to any unique GUID:
<PropertyGroup>
<UserSecretsId>{GUID}</UserSecretsId>
</PropertyGroup>
Secrets are created as name-value pairs. Hierarchical values (configuration sections) use a : (colon) as a separator in ASP.NET Core configuration key names.
Secret Manager is used from a command shell opened to the project's content root, where {SECRET NAME} is the name and {SECRET VALUE} is the value:
dotnet user-secrets set "{SECRET NAME}" "{SECRET VALUE}"
Execute the following commands in a command shell from the project's content root to set the secrets for the sample app:
dotnet user-secrets set "SecretName" "secret_value_1_dev"
dotnet user-secrets set "Section:SecretName" "secret_value_2_dev"
When these secrets are stored in Azure Key Vault in the Secret storage in the Production environment with Azure Key Vault section, the _dev suffix is changed to _prod. The suffix provides a visual cue in the app's output indicating the source of the configuration values.
Secret storage in the Production environment with Azure Key Vault
Complete the following steps to create an Azure Key Vault and store the sample app's secrets in it. For more information, see Quickstart: Set and retrieve a secret from Azure Key Vault using Azure CLI.
Open Azure Cloud Shell using any one of the following methods in the Azure portal:
- Select Try It in the upper-right corner of a code block. Use the search string "Azure CLI" in the text box.
- Open Cloud Shell in your browser with the Launch Cloud Shell button.
- Select the Cloud Shell button on the menu in the upper-right corner of the Azure portal.
For more information, see Azure CLI and Overview of Azure Cloud Shell.
If you aren't already authenticated, sign in with the
az logincommand.Create a resource group with the following command, where
{RESOURCE GROUP NAME}is the new resource group's name and{LOCATION}is the Azure region:az group create --name "{RESOURCE GROUP NAME}" --location {LOCATION}Create a Key Vault in the resource group with the following command, where
{KEY VAULT NAME}is the new vault's name and{LOCATION}is the Azure region:az keyvault create --name {KEY VAULT NAME} --resource-group "{RESOURCE GROUP NAME}" --location {LOCATION}Create secrets in the vault as name-value pairs.
Azure Key Vault secret names are limited to alphanumeric characters and dashes. Hierarchical values (configuration sections) use
--(two dashes) as a delimiter, as colons aren't allowed in Key Vault secret names. Colons delimit a section from a subkey in ASP.NET Core configuration. The two-dash sequence is replaced with a colon when the secrets are loaded into the app's configuration.The following secrets are for use with the sample app. The values include a
_prodsuffix to distinguish them from the_devsuffix values loaded in theDevelopmentenvironment from Secret Manager. Replace{KEY VAULT NAME}with the name of the Key Vault you created in the prior step:az keyvault secret set --vault-name {KEY VAULT NAME} --name "SecretName" --value "secret_value_1_prod" az keyvault secret set --vault-name {KEY VAULT NAME} --name "Section--SecretName" --value "secret_value_2_prod"
Use Application ID and X.509 certificate for non-Azure-hosted apps
Configure Azure Key Vault and the app to use a Microsoft Entra ID Application ID and X.509 certificate to authenticate to a vault when the app is hosted outside of Azure. For more information, see About keys, secrets, and certificates.
Note
Although using an Application ID and X.509 certificate is supported for apps hosted in Azure, it's not recommended. Instead, use Managed identities for Azure resources when hosting an app in Azure. Managed identities don't require storing a certificate in the app or in the Development environment.
The sample app uses an Application ID and X.509 certificate when the #define preprocessor directive at the top of Program.cs is set to Certificate.
- Create a PKCS#12 archive (.pfx) certificate. Options for creating certificates include New-SelfSignedCertificate on Windows and OpenSSL.
- Install the certificate into the current user's personal certificate store. Marking the key as exportable is optional. Note the certificate's thumbprint, which is used later in this process.
- Export the PKCS#12 archive (.pfx) certificate as a DER-encoded certificate (.cer).
- Register the app with Microsoft Entra ID (App registrations).
- Upload the DER-encoded certificate (.cer) to Microsoft Entra ID:
- Select the app in Microsoft Entra ID.
- Navigate to Certificates & secrets.
- Select Upload certificate to upload the certificate, which contains the public key. A .cer, .pem, or .crt certificate is acceptable.
- Store the Key Vault name, Application ID, and certificate thumbprint in the app's
appsettings.jsonfile. - Navigate to Key Vaults in the Azure portal.
- Select the Key Vault you created in the Secret storage in the
Productionenvironment with Azure Key Vault section. - Select Access policies.
- Select Add Access Policy.
- Open Secret permissions and provide the app with Get and List permissions.
- Select Select principal and select the registered app by name. Select the Select button.
- Select OK.
- Select Save.
- Deploy the app.
The Certificate sample app obtains its configuration values from IConfigurationRoot with the same name as the secret name:
- Non-hierarchical values: The value for
SecretNameis obtained withconfig["SecretName"]. - Hierarchical values (sections): Use
:(colon) notation or the GetSection method. Use either of these approaches to obtain the configuration value:config["Section:SecretName"]config.GetSection("Section")["SecretName"]
The X.509 certificate is managed by the OS. The app calls AddAzureKeyVault with values supplied by the appsettings.json file:
// using System.Linq;
// using System.Security.Cryptography.X509Certificates;
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
// using Azure.Identity;
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
if (context.HostingEnvironment.IsProduction())
{
var builtConfig = config.Build();
using var store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(
X509FindType.FindByThumbprint,
builtConfig["AzureADCertThumbprint"], false);
config.AddAzureKeyVault(new Uri($"https://{builtConfig["KeyVaultName"]}.vault.azure.net/"),
new ClientCertificateCredential(builtConfig["AzureADDirectoryId"], builtConfig["AzureADApplicationId"], certs.OfType<X509Certificate2>().Single()),
new KeyVaultSecretManager());
store.Close();
}
})
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
Example values:
- Key Vault name:
contosovault - Application ID:
00001111-aaaa-2222-bbbb-3333cccc4444 - Certificate thumbprint:
fe14593dd66b2406c5269d742d04b6e1ab03adb1
appsettings.json:
{
"KeyVaultName": "Key Vault Name",
"AzureADApplicationId": "Azure AD Application ID",
"AzureADCertThumbprint": "Azure AD Certificate Thumbprint",
"AzureADDirectoryId": "Azure AD Directory ID"
}
When you run the app, a webpage shows the loaded secret values. In the Development environment, secret values load with the _dev suffix. In the Production environment, the values load with the _prod suffix.
Use managed identities for Azure resources
An app deployed to Azure can take advantage of Managed identities for Azure resources. A managed identity allows the app to authenticate with Azure Key Vault using Microsoft Entra ID authentication without credentials (Application ID and Password/Client Secret) stored in the app.
The sample app uses managed identities for Azure resources when the #define preprocessor directive at the top of Program.cs is set to Managed.
Enter the vault name into the app's appsettings.json file. The sample app doesn't require an Application ID and Password (Client Secret) when set to the Managed version, so you can ignore those configuration entries. The app is deployed to Azure, and Azure authenticates the app to access Azure Key Vault only using the vault name stored in the appsettings.json file.
Deploy the sample app to Azure App Service.
An app deployed to Azure App Service is automatically registered with Microsoft Entra ID when the service is created. Obtain the Object ID from the deployment for use in the following command. The Object ID is shown in the Azure portal on the Identity panel of the App Service.
Using Azure CLI and the app's Object ID, provide the app with list and get permissions to access the vault:
az keyvault set-policy --name {KEY VAULT NAME} --object-id {OBJECT ID} --secret-permissions get list
Restart the app using Azure CLI, PowerShell, or the Azure portal.
The sample app:
- Creates an instance of the DefaultAzureCredential class. The credential attempts to obtain an access token from environment for Azure resources.
- A new SecretClient is created with the
DefaultAzureCredentialinstance. - The
SecretClientinstance is used with a KeyVaultSecretManager instance, which loads secret values and replaces double-dashes (--) with colons (:) in key names.
// using Azure.Security.KeyVault.Secrets;
// using Azure.Identity;
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
if (context.HostingEnvironment.IsProduction())
{
var builtConfig = config.Build();
var secretClient = new SecretClient(
new Uri($"https://{builtConfig["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential());
config.AddAzureKeyVault(secretClient, new KeyVaultSecretManager());
}
})
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
Note
The preceding example uses DefaultAzureCredential to simplify authentication while developing apps that deploy to Azure by combining credentials used in Azure hosting environments with credentials used in local development. When moving to production, an alternative is a better choice, such as ManagedIdentityCredential. For more information, see Authenticate Azure-hosted .NET apps to Azure resources using a system-assigned managed identity.
Key Vault name example value: contosovault
appsettings.json:
{
"KeyVaultName": "Key Vault Name"
}
When you run the app, a webpage shows the loaded secret values. In the Development environment, secret values have the _dev suffix because they're provided by Secret Manager. In the Production environment, the values load with the _prod suffix because they're provided by Azure Key Vault.
If you receive an Access denied error, confirm that the app is registered with Microsoft Entra ID and provided access to the vault. Confirm that you've restarted the service in Azure.
For information on using the provider with a managed identity and Azure Pipelines, see Create an Azure Resource Manager service connection to a VM with a managed service identity.
Configuration options
AddAzureKeyVault can accept an AzureKeyVaultConfigurationOptions object:
config.AddAzureKeyVault(
new SecretClient(
new Uri("Your Key Vault Endpoint"),
new DefaultAzureCredential(),
new AzureKeyVaultConfigurationOptions())
{
...
});
Note
The preceding example uses DefaultAzureCredential to simplify authentication while developing apps that deploy to Azure by combining credentials used in Azure hosting environments with credentials used in local development. When moving to production, an alternative is a better choice, such as ManagedIdentityCredential. For more information, see Authenticate Azure-hosted .NET apps to Azure resources using a system-assigned managed identity.
The AzureKeyVaultConfigurationOptions object contains the following properties.
| Property | Description |
|---|---|
| Manager | KeyVaultSecretManager instance used to control secret loading. |
| ReloadInterval | TimeSpan to wait between attempts at polling the vault for changes. The default value is null (configuration isn't reloaded). |
Use a key name prefix
AddAzureKeyVault provides an overload that accepts an implementation of KeyVaultSecretManager, which allows you to control how Key Vault secrets are converted into configuration keys. For example, you can implement the interface to load secret values based on a prefix value you provide at app startup. This technique allows you, for example, to load secrets based on the version of the app.
Warning
Don't use prefixes on Key Vault secrets to:
- Place secrets for multiple apps into the same vault.
- Place environmental secrets (for example, development versus production secrets) into the same vault.
Different apps and development/production environments should use separate Key Vaults to isolate app environments for the highest level of security.
In the following example, a secret is established in Key Vault (and using Secret Manager for the Development environment) for 5000-AppSecret (periods aren't allowed in Key Vault secret names). This secret represents an app secret for version 5.0.0.0 of the app. For another version of the app, 5.1.0.0, a secret is added to the vault (and using Secret Manager) for 5100-AppSecret. Each app version loads its versioned secret value into its configuration as AppSecret, removing the version as it loads the secret.
AddAzureKeyVault is called with a custom KeyVaultSecretManager implementation:
config.AddAzureKeyVault(
$"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
builtConfig["AzureADApplicationId"],
certs.OfType<X509Certificate2>().Single(),
new PrefixKeyVaultSecretManager(versionPrefix));
The implementation reacts to the version prefixes of secrets to load the proper secret into configuration:
Loadloads a secret when its name starts with the prefix. Other secrets aren't loaded.GetKey:- Removes the prefix from the secret name.
- Replaces two dashes in any name with the
KeyDelimiter, which is the delimiter used in configuration (usually a colon). Azure Key Vault doesn't allow a colon in secret names.
public class PrefixKeyVaultSecretManager : KeyVaultSecretManager
{
private readonly string _prefix;
public PrefixKeyVaultSecretManager(string prefix)
{
_prefix = $"{prefix}-";
}
public override bool Load(SecretProperties secret)
{
return secret.Name.StartsWith(_prefix);
}
public override string GetKey(KeyVaultSecret secret)
{
return secret.Name
.Substring(_prefix.Length)
.Replace("--", ConfigurationPath.KeyDelimiter);
}
}
The Load method is called by a provider algorithm that iterates through the vault secrets to find the version-prefixed secrets. When a version prefix is found with Load, the algorithm uses the GetKey method to return the configuration name of the secret name. It removes the version prefix from the secret's name. The rest of the secret name is returned for loading into the app's configuration name-value pairs.
When this approach is implemented:
The app's version specified in the app's project file. In the following example, the app's version is set to
5.0.0.0:<PropertyGroup> <Version>5.0.0.0</Version> </PropertyGroup>Confirm that a
<UserSecretsId>property is present in the app's project file, where{GUID}is a user-supplied GUID:<PropertyGroup> <UserSecretsId>{GUID}</UserSecretsId> </PropertyGroup>Save the following secrets locally with Secret Manager:
dotnet user-secrets set "5000-AppSecret" "5.0.0.0_secret_value_dev" dotnet user-secrets set "5100-AppSecret" "5.1.0.0_secret_value_dev"Secrets are saved in Azure Key Vault using the following Azure CLI commands:
az keyvault secret set --vault-name {KEY VAULT NAME} --name "5000-AppSecret" --value "5.0.0.0_secret_value_prod" az keyvault secret set --vault-name {KEY VAULT NAME} --name "5100-AppSecret" --value "5.1.0.0_secret_value_prod"When the app is run, the Key Vault secrets are loaded. The string secret for
5000-AppSecretis matched to the app's version specified in the app's project file (5.0.0.0).The version,
5000(with the dash), is stripped from the key name. Throughout the app, reading configuration with the keyAppSecretloads the secret value.If the app's version is changed in the project file to
5.1.0.0and the app is run again, the secret value returned is5.1.0.0_secret_value_devin theDevelopmentenvironment and5.1.0.0_secret_value_prodinProduction.
Note
You can also provide your own SecretClient implementation to AddAzureKeyVault. A custom client permits sharing a single instance of the client across the app.
Bind an array to a class
The provider can read configuration values into an array for binding to a POCO array.
When reading from a configuration source that allows keys to contain colon (:) separators, a numeric key segment is used to distinguish the keys that make up an array (:0:, :1:, … :{n}:). For more information, see Configuration: Bind an array to a class.
Azure Key Vault keys can't use a colon as a separator. The approach described in this article uses double dashes (--) as a separator for hierarchical values (sections). Array keys are stored in Azure Key Vault with double dashes and numeric key segments (--0--, --1--, … --{n}--).
Examine the following Serilog logging provider configuration provided by a JSON file. There are two object literals defined in the WriteTo array that reflect two Serilog sinks, which describe destinations for logging output:
"Serilog": {
"WriteTo": [
{
"Name": "AzureTableStorage",
"Args": {
"storageTableName": "logs",
"connectionString": "DefaultEnd...ountKey=Eby8...GMGw=="
}
},
{
"Name": "AzureDocumentDB",
"Args": {
"endpointUrl": "https://contoso.documents.azure.com:443",
"authorizationKey": "Eby8...GMGw=="
}
}
]
}
The configuration shown in the preceding JSON file is stored in Azure Key Vault using double dash (--) notation and numeric segments:
| Key | Value |
|---|---|
Serilog--WriteTo--0--Name |
AzureTableStorage |
Serilog--WriteTo--0--Args--storageTableName |
logs |
Serilog--WriteTo--0--Args--connectionString |
DefaultEnd...ountKey=Eby8...GMGw== |
Serilog--WriteTo--1--Name |
AzureDocumentDB |
Serilog--WriteTo--1--Args--endpointUrl |
https://contoso.documents.azure.com:443 |
Serilog--WriteTo--1--Args--authorizationKey |
Eby8...GMGw== |
Reload secrets
Secrets are cached until IConfigurationRoot.Reload is called. Subsequently disabled or updated secrets in the vault aren't respected by the app until Reload is executed.
Configuration.Reload();
Disabled and expired secrets
Expired secrets are included by default in the configuration provider. To exclude values for these secrets in app configuration, update the expired secret or provide the configuration using a custom configuration provider:
class SampleKeyVaultSecretManager : KeyVaultSecretManager
{
public override bool Load(SecretProperties properties) =>
properties.ExpiresOn.HasValue &&
properties.ExpiresOn.Value > DateTimeOffset.Now;
}
Pass this custom KeyVaultSecretManager to AddAzureKeyVault:
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
config.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential(),
new SampleKeyVaultSecretManager());
Disabled secrets cannot be retrieved from Key Vault and are never included.
Note
The preceding example uses DefaultAzureCredential to simplify authentication while developing apps that deploy to Azure by combining credentials used in Azure hosting environments with credentials used in local development. When moving to production, an alternative is a better choice, such as ManagedIdentityCredential. For more information, see Authenticate Azure-hosted .NET apps to Azure resources using a system-assigned managed identity.
Troubleshoot
When the app fails to load configuration using the provider, an error message is written to the ASP.NET Core Logging infrastructure. The following conditions will prevent configuration from loading:
- The app or certificate isn't configured correctly in Microsoft Entra ID.
- The vault doesn't exist in Azure Key Vault.
- The app isn't authorized to access the vault.
- The access policy doesn't include
GetandListpermissions. - In the vault, the configuration data (name-value pair) is incorrectly named, missing, or disabled.
- The app has the wrong Key Vault name (
KeyVaultName), Microsoft Entra ID Application ID (AzureADApplicationId), or Microsoft Entra ID certificate thumbprint (AzureADCertThumbprint), or Microsoft Entra ID Directory ID (AzureADDirectoryId). - When adding the Key Vault access policy for the app, the policy was created, but the Save button wasn't selected in the Access policies UI.
Additional resources
- View or download sample code (how to download)
- Configuration in ASP.NET Core
- Microsoft Azure: Key Vault Documentation
- How to generate and transfer HSM-protected keys for Azure Key Vault
- Quickstart: Set and retrieve a secret from Azure Key Vault by using a .NET web app
- Tutorial: How to use Azure Key Vault with Azure Windows Virtual Machine in .NET
ASP.NET Core