diff --git a/aspnetcore/security/key-vault-configuration.md b/aspnetcore/security/key-vault-configuration.md index f351493978..4c6ecfa2dc 100644 --- a/aspnetcore/security/key-vault-configuration.md +++ b/aspnetcore/security/key-vault-configuration.md @@ -15,7 +15,7 @@ By [Andrew Stanton-Nurse](https://github.com/anurse) ::: moniker range=">= aspnetcore-3.0" -This document explains how to use the [Microsoft Azure Key Vault](https://azure.microsoft.com/services/key-vault/) Configuration Provider to load app configuration values from Azure Key Vault secrets. Azure Key Vault is a cloud-based service that assists in safeguarding cryptographic keys and secrets used by apps and services. Common scenarios for using Azure Key Vault with ASP.NET Core apps include: +This document explains how to use the [Azure Key Vault](https://azure.microsoft.com/services/key-vault/) Configuration Provider to load app configuration values from Azure Key Vault secrets. Azure Key Vault is a cloud-based service that assists in safeguarding 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 (HSM's) when storing configuration data. @@ -24,7 +24,7 @@ This document explains how to use the [Microsoft Azure Key Vault](https://azure. ## Packages -Add a package reference to the [Microsoft.Extensions.Configuration.AzureKeyVault](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.AzureKeyVault/) package. +Add a package reference to the [Azure.Extensions.AspNetCore.Configuration.Secrets](https://www.nuget.org/packages/Azure.Extensions.AspNetCore.Configuration.Secrets/) package. ## Sample app @@ -136,9 +136,9 @@ The `Certificate` sample app obtains its configuration values from `IConfigurati * `config["Section:SecretName"]` * `config.GetSection("Section")["SecretName"]` -The X.509 certificate is managed by the OS. The app calls with values supplied by the *appsettings.json* file: +The X.509 certificate is managed by the OS. The app calls **AddAzureKeyVault** with values supplied by the *appsettings.json* file: -[!code-csharp[](key-vault-configuration/samples/3.x/SampleApp/Program.cs?name=snippet1&highlight=20-23)] +[!code-csharp[](key-vault-configuration/samples/3.x/SampleApp/Program.cs?name=snippet1&highlight=46-49)] Example values: @@ -174,14 +174,14 @@ az keyvault set-policy --name {KEY VAULT NAME} --object-id {OBJECT ID} --secret- The sample app: -* Creates an instance of the `AzureServiceTokenProvider` class without a connection string. When a connection string isn't provided, the provider attempts to obtain an access token from Managed identities for Azure resources. -* A new is created with the `AzureServiceTokenProvider` instance token callback. -* The instance is used with a default implementation of that loads all secret values and replaces double-dashes (`--`) with colons (`:`) in key names. +* Creates an instance of the `DefaultAzureCredential` class, the credential attempts to obtain an access token from environment for Azure resources. +* A new is created with the `DefaultAzureCredential` instance. +* The instance is used with a default implementation of that loads all secret values and replaces double-dashes (`--`) with colons (`:`) in key names. -[!code-csharp[](key-vault-configuration/samples/3.x/SampleApp/Program.cs?name=snippet2&highlight=13-21)] +[!code-csharp[](key-vault-configuration/samples/3.x/SampleApp/Program.cs?name=snippet2&highlight=12-14)] Key vault name example value: `contosovault` - + *appsettings.json*: ```json @@ -198,11 +198,11 @@ For information on using the provider with a managed identity and an Azure DevOp ## Configuration options - can accept an : +AddAzureKeyVault can accept an AzureKeyVaultConfigurationOptions: ```csharp -config.AddAzureKeyVault( - new AzureKeyVaultConfigurationOptions() +config.AddAzureKeyVault(new SecretClient(new URI("Your Key Vault Endpoint"), new DefaultAzureCredential()), + new AzureKeyVaultConfigurationOptions()) { ... }); @@ -210,25 +210,23 @@ config.AddAzureKeyVault( | Property | Description | | ---------------- | ----------- | -| `Client` | to use for retrieving values. | -| `Manager` | instance used to control secret loading. | +| `Manager` | instance used to control secret loading. | | `ReloadInterval` | `Timespan` to wait between attempts at polling the key vault for changes. The default value is `null` (configuration isn't reloaded). | -| `Vault` | Key vault URI. | ## Use a key name prefix - provides an overload that accepts an implementation of , 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 allows you, for example, to load secrets based on the version of the app. +AddAzureKeyVault provides an overload that accepts an implementation of , 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 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 key vault or to place environmental secrets (for example, *development* versus *production* secrets) into the same vault. We recommend that different apps and development/production environments use separate key vaults to isolate app environments for the highest level of security. In the following example, a secret is established in the key vault (and using the Secret Manager tool 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 key vault (and using the Secret Manager tool) for `5100-AppSecret`. Each app version loads its versioned secret value into its configuration as `AppSecret`, stripping off the version as it loads the secret. - is called with a custom : +AddAzureKeyVault is called with a custom : [!code-csharp[](key-vault-configuration/samples_snapshot/Program.cs)] -The implementation reacts to the version prefixes of secrets to load the proper secret into configuration: +The implementation reacts to the version prefixes of secrets to load the proper secret into configuration: * `Load` loads a secret when its name starts with the prefix. Other secrets aren't loaded. * `GetKey`: @@ -278,7 +276,7 @@ When this approach is implemented: 1. If the app's version is changed in the project file to `5.1.0.0` and the app is run again, the secret value returned is `5.1.0.0_secret_value_dev` in the Development environment and `5.1.0.0_secret_value_prod` in Production. > [!NOTE] -> You can also provide your own implementation to . A custom client permits sharing a single instance of the client across the app. +> You can also provide your own implementation to AddAzureKeyVault. A custom client permits sharing a single instance of the client across the app. ## Bind an array to a class @@ -332,7 +330,7 @@ Configuration.Reload(); ## Disabled and expired secrets -Disabled and expired secrets throw a . To prevent the app from throwing, provide the configuration using a different configuration provider or update the disabled or expired secret. +Disabled and expired secrets throw a . To prevent the app from throwing, provide the configuration using a different configuration provider or update the disabled or expired secret. ## Troubleshoot @@ -343,7 +341,7 @@ When the app fails to load configuration using the provider, an error message is * The app isn't authorized to access the key vault. * The access policy doesn't include `Get` and `List` permissions. * In the key vault, the configuration data (name-value pair) is incorrectly named, missing, disabled, or expired. -* The app has the wrong key vault name (`KeyVaultName`), Azure AD Application Id (`AzureADApplicationId`), or Azure AD certificate thumbprint (`AzureADCertThumbprint`). +* The app has the wrong key vault name (`KeyVaultName`), Azure AD Application Id (`AzureADApplicationId`), or Azure AD certificate thumbprint (`AzureADCertThumbprint`), or Azure AD DirectoryId (`AzureADDirectoryId`). * The configuration key (name) is incorrect in the app for the value you're trying to load. * When adding the access policy for the app to the key vault, the policy was created, but the **Save** button wasn't selected in the **Access policies** UI. @@ -685,4 +683,3 @@ When the app fails to load configuration using the provider, an error message is * [Tutorial: How to use Azure Key Vault with Azure Windows Virtual Machine in .NET](/azure/key-vault/tutorial-net-windows-virtual-machine) ::: moniker-end - diff --git a/aspnetcore/security/key-vault-configuration/samples/3.x/SampleApp/Program.cs b/aspnetcore/security/key-vault-configuration/samples/3.x/SampleApp/Program.cs index 6442ccc304..c4468c70d4 100644 --- a/aspnetcore/security/key-vault-configuration/samples/3.x/SampleApp/Program.cs +++ b/aspnetcore/security/key-vault-configuration/samples/3.x/SampleApp/Program.cs @@ -3,13 +3,14 @@ // For details, see the Azure Key Vault Configuration Provider topic: // https://docs.microsoft.com/aspnet/core/security/key-vault-configuration +using Azure.Extensions.AspNetCore.Configuration.Secrets; +using Azure.Identity; +using Azure.Security.KeyVault.Secrets; +using System; using System.Linq; using System.Security.Cryptography.X509Certificates; using Microsoft.AspNetCore.Hosting; -using Microsoft.Azure.KeyVault; -using Microsoft.Azure.Services.AppAuthentication; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Configuration.AzureKeyVault; using Microsoft.Extensions.Hosting; namespace SampleApp @@ -25,7 +26,8 @@ namespace SampleApp #region snippet1 // using System.Linq; // using System.Security.Cryptography.X509Certificates; - // using Microsoft.Extensions.Configuration; + // using Azure.Extensions.AspNetCore.Configuration.Secrets; + // using Azure.Identity; public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) @@ -42,10 +44,10 @@ namespace SampleApp .Find(X509FindType.FindByThumbprint, builtConfig["AzureADCertThumbprint"], false); - config.AddAzureKeyVault( - $"https://{builtConfig["KeyVaultName"]}.vault.azure.net/", - builtConfig["AzureADApplicationId"], - certs.OfType().Single()); + config.AddAzureKeyVault(new Uri($"https://{builtConfig["KeyVaultName"]}.vault.azure.net/"), + new ClientCertificateCredential(builtConfig["AzureADDirectoryId"], builtConfig["AzureADApplicationId"], certs.OfType().Single()), + new KeyVaultSecretManager()); + store.Close(); } @@ -60,9 +62,9 @@ namespace SampleApp #if Managed #region snippet2 - // using Microsoft.Azure.KeyVault; - // using Microsoft.Azure.Services.AppAuthentication; - // using Microsoft.Extensions.Configuration.AzureKeyVault; + // using Azure.Security.KeyVault.Secrets; + // using Azure.Identity; + // using Azure.Extensions.AspNetCore.Configuration.Secrets; public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) @@ -71,16 +73,11 @@ namespace SampleApp 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()); - var azureServiceTokenProvider = new AzureServiceTokenProvider(); - var keyVaultClient = new KeyVaultClient( - new KeyVaultClient.AuthenticationCallback( - azureServiceTokenProvider.KeyVaultTokenCallback)); - config.AddAzureKeyVault( - $"https://{builtConfig["KeyVaultName"]}.vault.azure.net/", - keyVaultClient, - new DefaultKeyVaultSecretManager()); } }) .ConfigureWebHostDefaults(webBuilder => diff --git a/aspnetcore/security/key-vault-configuration/samples/3.x/SampleApp/SampleApp.csproj b/aspnetcore/security/key-vault-configuration/samples/3.x/SampleApp/SampleApp.csproj index f75b1bd058..03a9301bf9 100644 --- a/aspnetcore/security/key-vault-configuration/samples/3.x/SampleApp/SampleApp.csproj +++ b/aspnetcore/security/key-vault-configuration/samples/3.x/SampleApp/SampleApp.csproj @@ -7,7 +7,8 @@ - + + diff --git a/aspnetcore/security/key-vault-configuration/samples/3.x/SampleApp/appsettings.json b/aspnetcore/security/key-vault-configuration/samples/3.x/SampleApp/appsettings.json index ff2e451ee6..89469b0ed6 100644 --- a/aspnetcore/security/key-vault-configuration/samples/3.x/SampleApp/appsettings.json +++ b/aspnetcore/security/key-vault-configuration/samples/3.x/SampleApp/appsettings.json @@ -1,5 +1,6 @@ { "KeyVaultName": "Key Vault Name", "AzureADApplicationId": "Azure AD Application ID", - "AzureADCertThumbprint": "Azure AD Certificate Thumbprint" + "AzureADCertThumbprint": "Azure AD Certificate Thumbprint", + "AzureADDirectoryId": "Azure AD Directory ID" } diff --git a/aspnetcore/security/key-vault-configuration/samples_snapshot/Startup.cs b/aspnetcore/security/key-vault-configuration/samples_snapshot/Startup.cs index 4c221af166..224cdc4dda 100644 --- a/aspnetcore/security/key-vault-configuration/samples_snapshot/Startup.cs +++ b/aspnetcore/security/key-vault-configuration/samples_snapshot/Startup.cs @@ -1,4 +1,4 @@ -public class PrefixKeyVaultSecretManager : IKeyVaultSecretManager +public class PrefixKeyVaultSecretManager : KeyVaultSecretManager { private readonly string _prefix; @@ -7,14 +7,14 @@ public class PrefixKeyVaultSecretManager : IKeyVaultSecretManager _prefix = $"{prefix}-"; } - public bool Load(SecretItem secret) + public override bool Load(SecretProperties secret) { - return secret.Identifier.Name.StartsWith(_prefix); + return secret.Name.StartsWith(_prefix); } - public string GetKey(SecretBundle secret) + public override string GetKey(KeyVaultSecret secret) { - return secret.SecretIdentifier.Name + return secret.Name .Substring(_prefix.Length) .Replace("--", ConfigurationPath.KeyDelimiter); }