diff --git a/aspnetcore/security/key-vault-configuration.md b/aspnetcore/security/key-vault-configuration.md index 72e386303ae8..14a6a73c7a94 100644 --- a/aspnetcore/security/key-vault-configuration.md +++ b/aspnetcore/security/key-vault-configuration.md @@ -1,11 +1,11 @@ --- title: Azure Key Vault configuration provider author: guardrex -description: How to use the Azure Key Vault Configuration Provider to configure an application using name-value pairs loaded at runtime. +description: Learn how to use the Azure Key Vault Configuration Provider to configure an application using name-value pairs loaded at runtime. keywords: ASP.NET Core, configuration, Azure Key Vault ms.author: riande manager: wpickett -ms.date: 02/11/2017 +ms.date: 08/09/2017 ms.topic: article ms.assetid: 0292bdae-b3ed-4637-bd67-19b9bb8b65cb ms.prod: asp.net-core @@ -15,73 +15,100 @@ uid: security/key-vault-configuration By [Luke Latham](https://github.com/GuardRex) and [Andrew Stanton-Nurse](https://github.com/anurse) -[View or download sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/key-vault-configuration/sample) +# [ASP.NET Core 2.x](#tab/aspnetcore2x) -This document explains how to use the [Microsoft Azure Key Vault](https://azure.microsoft.com/services/key-vault/) configuration provider to load application configuration values from Azure Key Vault. Azure Key Vault is a cloud-based service that helps you safeguard cryptographic keys and secrets used by apps and services. Common scenarios include controlling access to sensitive configuration data and meeting the requirement for FIPS 140-2 Level 2 validated Hardware Security Modules (HSM's) when storing configuration data. +View or download sample code for 2.x: + +* [Basic sample](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x) - Reads secret values into an app. +* [Key name prefix sample](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x) - Reads secret values using a key name prefix that represents the version of an app, which allows you to load a different set of secret values for each app version. + +# [ASP.NET Core 1.x](#tab/aspnetcore1x) + +View or download sample code for 1.x: + +* [Basic sample](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x) - Reads secret values into an app. +* [Key name prefix sample](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x) - Reads secret values using a key name prefix that represents the version of an app, which allows you to load a different set of secret values for each app version. + +--- + +This document explains how to use the [Microsoft Azure Key Vault](https://azure.microsoft.com/services/key-vault/) configuration provider to load application configuration values from Azure Key Vault secrets. Azure Key Vault is a cloud-based service that helps you safeguard cryptographic keys and secrets used by apps and services. Common scenarios include controlling access to sensitive configuration data and meeting the requirement for FIPS 140-2 Level 2 validated Hardware Security Modules (HSM's) when storing configuration data. This feature is available for applications that target ASP.NET Core 1.1 or higher. ## Package -To use the provider, add a reference to the [`Microsoft.Extensions.Configuration.AzureKeyVault`](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.AzureKeyVault/) package. The provider depends on .NET Framework 4.5.1 or .NET Standard 1.5 or higher. This feature is available for applications that target ASP.NET Core 1.1.0 or higher. +To use the provider, add a reference to the [Microsoft.Extensions.Configuration.AzureKeyVault](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.AzureKeyVault/) package. ## Application configuration -You can explore the provider with the [sample application](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/key-vault-configuration/sample). Once you establish a key vault and create a pair of secrets in the vault by [following the guidance below](#creating-key-vault-secrets-and-loading-configuration-values), the sample app securely loads the secret values into its configuration and displays them in a webpage. +You can explore the provider with the [sample apps](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/key-vault-configuration/samples). Once you establish a key vault and create secrets in the vault, the sample apps securely load the secret values into their configurations and display them in webpages. -The provider is added to the `ConfigurationBuilder` with the `AddAzureKeyVault` extension. In the sample app, the extension uses three configuration values loaded from the *appsettings.json* file. +The provider is added to the `ConfigurationBuilder` with the `AddAzureKeyVault` extension. In the sample apps, the extension uses three configuration values loaded from the *appsettings.json* file. -App Setting | Description | Example ---- | --- | --- -`Vault` | Azure Key Vault name | contosovault -`ClientId` | Azure Active Directory App Id | 627e911e-43cc-61d4-992e-12db9c81b413 -`ClientSecret` | Azure Active Directory App Key | g58K3dtg59o1Pa+e59v2Tx829w6VxTB2yv9sv/101di= +| App Setting | Description | Example | +| -------------- | ------------------------------ | -------------------------------------------- | +| `Vault` | Azure Key Vault name | contosovault | +| `ClientId` | Azure Active Directory App Id | 627e911e-43cc-61d4-992e-12db9c81b413 | +| `ClientSecret` | Azure Active Directory App Key | g58K3dtg59o1Pa+e59v2Tx829w6VxTB2yv9sv/101di= | -[!code-csharp[Main](key-vault-configuration/sample/Startup.cs?name=snippet1&highlight=5,10-13)] +[!code-csharp[Program](key-vault-configuration/samples/basic-sample/2.x/Program.cs?name=snippet1&highlight=2,7-10)] -`AddAzureKeyVault` also provides an overload that accepts an implementation of `IKeyVaultSecretManager`, which allows you to control how key vault secrets are converted into configuration keys. For example, you can implement the interface to load configuration values by application, where you prefix app names to configuration secrets you've stored in the key vault. This allows you to maintain secrets for multiple apps in one key vault. +## Creating key vault secrets and loading configuration values (basic-sample) +1. Create a key vault and set up Azure Active Directory (Azure AD) for the application following the guidance in [Get started with Azure Key Vault](https://azure.microsoft.com/documentation/articles/key-vault-get-started/). + * Add secrets to the key vault using the [AzureRM Key Vault PowerShell Module](/powershell/module/azurerm.keyvault) available from the [PowerShell Gallery](https://www.powershellgallery.com/packages/AzureRM.KeyVault), the [Azure Key Vault REST API](/rest/api/keyvault/), or the [Azure Portal](https://portal.azure.com/). Secrets are created as either *Manual* or *Certificate* secrets. *Certificate* secrets are certificates for use by apps and services but are not supported by the configuration provider. You should use the *Manual* option to create name-value pair secrets for use with the configuration provider. + * Simple secrets are created 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 separator in the sample. Colons, which are normally used to delimit a section from a subkey in [ASP.NET Core configuration](xref:fundamentals/configuration), aren't allowed in secret names. Therefore, two dashes are used and swapped for a colon when the secrets are loaded into the app's configuration. + * Create two *Manual* secrets with the following name-value pairs. The first secret is a simple name and value, and the second secret creates a secret value with a section and subkey in the secret name: + * `SecretName`: `secret_value_1` + * `Section--SecretName`: `secret_value_2` + * Register the sample app with Azure Active Directory. + * Authorize the app to access the key vault. When you use the `Set-AzureRmKeyVaultAccessPolicy` PowerShell cmdlet to authorize the app to access the key vault, provide `List` and `Get` access to secrets with `-PermissionsToKeys list,get`. +2. Update the app's *appsettings.json* file with the values of `Vault`, `ClientId`, and `ClientSecret`. +3. Run the sample app, which obtains its configuration values from `IConfigurationRoot` with the same name as the secret name. + * Non-hierarchical values: The value for `SecretName` is obtained with `config["SecretName"]`. + * Hierarchical values (sections): Use `:` (colon) notation or the `GetSection` extension method. Use either of these approaches to obtain the configuration value: + * `config["Section:SecretName"]` + * `config.GetSection("Section")["SecretName"]` -Assume we have several `ConnectionString` key vault secrets with the application name prefixed. For the sample app, we create a secret in the key vault for `KeyVaultConfigProviderSample-ConnectionString` and its value. For a second app, we create a secret for `SomeOtherApplicationName-ConnectionString` and its value. We want each app to load its own `ConnectionString` secret into its configuration as `ConnectionString`. An example of this implementation is shown below: +When you run the app, a webpage shows the loaded secret values: -```csharp -public class EnvironmentSecretManager : IKeyVaultSecretManager -{ - private readonly string _appNamePrefix; - - public EnvironmentSecretManager(string appName) - { - _appNamePrefix = appName + "-"; - } - - public bool Load(SecretItem secret) - { - return secret.Identifier.Name.StartsWith(_appNamePrefix); - } - - public string GetKey(SecretBundle secret) - { - return secret.SecretIdentifier.Name - .Substring(_appNamePrefix.Length) - .Replace("--", ConfigurationPath.KeyDelimiter); - } -} -``` +![Browser window showing secret values loaded via the Azure Key Vault Configuration Provider](key-vault-configuration/_static/sample1.png) -```csharp -builder.AddAzureKeyVault( - $"https://{config["Vault"]}.vault.azure.net/", - config["ClientId"], - config["ClientSecret"], - new EnvironmentSecretManager(env.ApplicationName)); - -Configuration = builder.Build(); -``` +## Creating prefixed key vault secrets and loading configuration values (key-name-prefix-sample) +`AddAzureKeyVault` also provides an overload that accepts an implementation of `IKeyVaultSecretManager`, 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* verus *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. -The `Load` method is called by a provider algorithm that iterates through the secrets to find the one that matches the application name as a prefix to the secret's name. When a match is found with `Load`, the algorithm uses the `GetKey` method to return the configuration name of the secret name. It strips off the app name prefix from the secret's name and returns the name for loading into the app's configuration name-value pairs. +Using the second sample app, you create a secret in the key vault for `5000-AppSecret` (periods aren't allowed in key vault secret names) representing an app secret for version 5.0.0.0 of your app. For another version, 5.1.0.0, you create a secret for `5100-AppSecret`. Each app version loads its own secret value into its configuration as `AppSecret`, stripping off the version as it loads the secret. The sample's implementation is shown below: -If you implement this approach: +[!code-csharp[Configuration builder](key-vault-configuration/samples/key-name-prefix-sample/2.x/Program.cs?name=snippet1&highlight=12)] + +[!code-csharp[PrefixKeyVaultSecretManager](key-vault-configuration/samples/key-name-prefix-sample/2.x/Startup.cs?name=snippet1)] + +The `Load` method is called by a provider algorithm that iterates through the vault secrets to find the ones that have the version prefix. When a version prefix is found with `Load`, the algorithm uses the `GetKey` method to return the configuration name of the secret name. It strips off the version prefix from the secret's name and returns the rest of the secret name for loading into the app's configuration name-value pairs. + +When you implement this approach: 1. The key vault secrets are loaded. -2. The string secret for `KeyVaultConfigProviderSample-ConnectionString` is matched. -3. The application name `KeyVaultConfigProviderSample` (with the dash) is stripped off and used to load `ConnectionString` with its value into the app's configuration. +2. The string secret for `5000-AppSecret` is matched. +3. The version, `5000` (with the dash), is stripped off of the key name leaving `AppSecret` to load with the secret value into the app's configuration. + +> [!NOTE] +> You can also provide your own `KeyVaultClient` implementation to `AddAzureKeyVault`. Supplying a custom client allows you to share a single instance of the client between the configuration provider and other parts of your app. + +1. Create a key vault and set up Azure Active Directory (Azure AD) for the application following the guidance in [Get started with Azure Key Vault](https://azure.microsoft.com/documentation/articles/key-vault-get-started/). + * Add secrets to the key vault using the [AzureRM Key Vault PowerShell Module](/powershell/module/azurerm.keyvault) available from the [PowerShell Gallery](https://www.powershellgallery.com/packages/AzureRM.KeyVault), the [Azure Key Vault REST API](/rest/api/keyvault/), or the [Azure Portal](https://portal.azure.com/). Secrets are created as either *Manual* or *Certificate* secrets. *Certificate* secrets are certificates for use by apps and services but are not supported by the configuration provider. You should use the *Manual* option to create name-value pair secrets for use with the configuration provider. + * Hierarchical values (configuration sections) use `--` (two dashes) as a separator. + * Create two *Manual* secrets with the following name-value pairs: + * `5000-AppSecret`: `5.0.0.0_secret_value` + * `5100-AppSecret`: `5.1.0.0_secret_value` + * Register the sample app with Azure Active Directory. + * Authorize the app to access the key vault. When you use the `Set-AzureRmKeyVaultAccessPolicy` PowerShell cmdlet to authorize the app to access the key vault, provide `List` and `Get` access to secrets with `-PermissionsToKeys list,get`. +2. Update the app's *appsettings.json* file with the values of `Vault`, `ClientId`, and `ClientSecret`. +3. Run the sample app, which obtains its configuration values from `IConfigurationRoot` with the same name as the prefixed secret name. In this sample, the prefix is the app's version, which you provided to the `PrefixKeyVaultSecretManager` when you added the Azure Key Vault configuration provider. The value for `AppSecret` is obtained with `config["AppSecret"]`. The webpage generated by the app shows the loaded value: + + ![Browser window showing a secret value loaded via the Azure Key Vault Configuration Provider when the app's version is 5.0.0.0](key-vault-configuration/_static/sample2-1.png) -You can also provide your own `KeyVaultClient` implementation to `AddAzureKeyVault`. Supplying a custom client allows you to share a single instance of the client between the configuration provider and other parts of your app. +4. Change the version of the app assembly in the project file from `5.0.0.0` to `5.1.0.0` and run the app again. This time, the secret value returned is `5.1.0.0_secret_value`. The webpage generated by the app shows the loaded value: + + ![Browser window showing a secret value loaded via the Azure Key Vault Configuration Provider when the app's version is 5.1.0.0](key-vault-configuration/_static/sample2-2.png) ## Controlling access to the ClientSecret Use the [Secret Manager tool](xref:security/app-secrets) to maintain the `ClientSecret` outside of your project source tree. With Secret Manager, you associate app secrets with a specific project and share them across multiple projects. @@ -103,24 +130,6 @@ store.Close(); Configuration = builder.Build(); ``` -## Creating key vault secrets and loading configuration values -1. Create a key vault and set up Azure Active Directory (Azure AD) for the application following the guidance in [Get started with Azure Key Vault](https://azure.microsoft.com/documentation/articles/key-vault-get-started/). - * Add secrets to the key vault using the Azure PowerShell Module, the Azure Management API, or the Azure Portal. Secrets are uploaded as either *Manual* or *Certificate* secrets. *Certificate* secrets are certificates for use by apps and services but are not supported by the configuration provider. You should use the *Manual* option to create name-value pair secrets for use with the configuration provider. - * Hierarchical values (configuration sections) use `--` (two dashes) as a separator. - * For the sample app, create two *Manual* secrets with the following name-value pairs: - * `MySecret`: `secret_value_1` - * `Section--MySecret`: `secret_value_2` - * Register the sample app with Azure Active Directory. - * Authorize the app to access the key vault. When you use the `Set-AzureRmKeyVaultAccessPolicy` PowerShell cmdlet to authorize the app to access the key vault, provide `List` and `Get` access to secrets with `-PermissionsToKeys list,get`. -2. Update the app's *appsettings.json* file with the values of `Vault`, `ClientId`, and `ClientSecret`. -3. Run the sample app, which obtains its configuration values from `IConfigurationRoot` with the same name as the secret name. - * Non-hierarchical Values: The value for `MySecret` is obtained with `config["MySecret"]`. - * Hierarchical Values (sections): Use `:` (colon) notation or the `GetSection` extension method. - * `config["Section:MySecret"]` - * `config.GetSection("Section")["MySecret"]` - -![Browser window showing secret values loaded via the Azure Key Vault Configuration Provider](key-vault-configuration/_static/sample-output.png) - ## Reloading secrets Secrets are cached until `IConfigurationRoot.Reload()` is called. Expired, disabled, and updated secrets in the key vault are not respected by the application until `Reload` is executed. @@ -142,9 +151,6 @@ When the application fails to load configuration using the provider, an error me * The Azure AD Key (`ClientSecret`) is expired. * The configuration key (name) is incorrect in the app for the value you're trying to load. -`System.TypeLoadException` exception during startup using `.NET 4.6.x`: -* Add a NuGet reference to the package for `System.Net.Http` version 4.3.1 or later. - ## Additional resources * * [Microsoft Azure: Key Vault](https://azure.microsoft.com/services/key-vault/) diff --git a/aspnetcore/security/key-vault-configuration/_static/sample-output.png b/aspnetcore/security/key-vault-configuration/_static/sample-output.png deleted file mode 100644 index b50043649ad0..000000000000 Binary files a/aspnetcore/security/key-vault-configuration/_static/sample-output.png and /dev/null differ diff --git a/aspnetcore/security/key-vault-configuration/_static/sample1.png b/aspnetcore/security/key-vault-configuration/_static/sample1.png new file mode 100644 index 000000000000..3b526b39db5d Binary files /dev/null and b/aspnetcore/security/key-vault-configuration/_static/sample1.png differ diff --git a/aspnetcore/security/key-vault-configuration/_static/sample2-1.png b/aspnetcore/security/key-vault-configuration/_static/sample2-1.png new file mode 100644 index 000000000000..dc605c51f7c2 Binary files /dev/null and b/aspnetcore/security/key-vault-configuration/_static/sample2-1.png differ diff --git a/aspnetcore/security/key-vault-configuration/_static/sample2-2.png b/aspnetcore/security/key-vault-configuration/_static/sample2-2.png new file mode 100644 index 000000000000..c346eff61d91 Binary files /dev/null and b/aspnetcore/security/key-vault-configuration/_static/sample2-2.png differ diff --git a/aspnetcore/security/key-vault-configuration/sample/Properties/launchSettings.json b/aspnetcore/security/key-vault-configuration/sample/Properties/launchSettings.json deleted file mode 100644 index 711172311913..000000000000 --- a/aspnetcore/security/key-vault-configuration/sample/Properties/launchSettings.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:5000/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "http://localhost:5000/", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/aspnetcore/security/key-vault-configuration/sample/README.md b/aspnetcore/security/key-vault-configuration/sample/README.md deleted file mode 100644 index d33c2f46917a..000000000000 --- a/aspnetcore/security/key-vault-configuration/sample/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Key Vault Configuration Provider sample application - -This sample illustrates the use of the Azure Key Vault Configuration Provider. - -## Using the sample -1. Following the guidance in [Get started with Azure Key Vault](https://docs.microsoft.com/azure/key-vault/key-vault-get-started): - * Create a key vault - * Create key vault secrets - - `MySecret`: `secret_value_1` - - `Section--MySecret`: `secret_value_2` - * Register the application with Azure Active Directory - * Authorize the application to use the secrets -2. Provide the configuration data to the `appsettings.json` file of the sample: - * `Vault`: Azure Key Vault Name - * `ClientId`: Azure AD Application Id - * `ClientSecret`: Azure AD Application Key -3. Restore, build, and run the application diff --git a/aspnetcore/security/key-vault-configuration/sample/KeyVaultConfigProviderSample.csproj b/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/KeyVaultConfigProviderSample.csproj similarity index 92% rename from aspnetcore/security/key-vault-configuration/sample/KeyVaultConfigProviderSample.csproj rename to aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/KeyVaultConfigProviderSample.csproj index b75568126ecb..3942f85a3138 100644 --- a/aspnetcore/security/key-vault-configuration/sample/KeyVaultConfigProviderSample.csproj +++ b/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/KeyVaultConfigProviderSample.csproj @@ -3,7 +3,6 @@ net451;netcoreapp1.1 Exe - win7-x64 diff --git a/aspnetcore/security/key-vault-configuration/sample/Markup.cs b/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/Markup.cs similarity index 74% rename from aspnetcore/security/key-vault-configuration/sample/Markup.cs rename to aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/Markup.cs index a9b502434a99..5322a1981ab5 100644 --- a/aspnetcore/security/key-vault-configuration/sample/Markup.cs +++ b/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/Markup.cs @@ -7,7 +7,7 @@ internal static class Markup Key Vault Configuration Provider Sample - +

Key Vault Configuration Provider Sample

@@ -23,19 +23,19 @@ internal static class Markup - MySecret - MySecret - Configuration[""MySecret""] + SecretName + SecretName + Configuration[""SecretName""] {0} - Section:MySecret - Section--MySecret - Configuration[""Section:MySecret""] + Section:SecretName + Section--SecretName + Configuration[""Section:SecretName""] {1} - Configuration.GetSection(""Section"")[""MySecret""] + Configuration.GetSection(""Section"")[""SecretName""] {2} diff --git a/aspnetcore/security/key-vault-configuration/sample/Program.cs b/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/Program.cs similarity index 100% rename from aspnetcore/security/key-vault-configuration/sample/Program.cs rename to aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/Program.cs diff --git a/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/README.md b/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/README.md new file mode 100644 index 000000000000..7bf7ecd1d168 --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/README.md @@ -0,0 +1,25 @@ +# Key Vault Configuration Provider sample application (ASP.NET Core 1.x) + +This sample illustrates the use of the Azure Key Vault Configuration Provider for ASP.NET Core 1.x. For the ASP.NET Core 2.x sample, see [Key Vault Configuration Provider sample application (ASP.NET Core 2.x)](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x). + +> [!NOTE] +> The configuration provider isn't available for ASP.NET Core 1.0. If you want to implement the configuration provider and the app is an ASP.NET Core 1.0 app, upgrade the app to 1.1 or later first. + +For more information on how the sample works, see the [Azure Key Vault configuration provider](xref:security/key-vault-configuration) topic. + +## Using the sample +1. Create a key vault and set up Azure Active Directory (Azure AD) for the application following the guidance in [Get started with Azure Key Vault](https://azure.microsoft.com/documentation/articles/key-vault-get-started/). + * Add secrets to the key vault using the [AzureRM Key Vault PowerShell Module](/powershell/module/azurerm.keyvault) available from the [PowerShell Gallery](https://www.powershellgallery.com/packages/AzureRM.KeyVault), the [Azure Key Vault REST API](/rest/api/keyvault/), or the [Azure Portal](https://portal.azure.com/). Secrets are created as either *Manual* or *Certificate* secrets. *Certificate* secrets are certificates for use by apps and services but are not supported by the configuration provider. You should use the *Manual* option to create name-value pair secrets for use with the configuration provider. + * Simple secrets are created 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 separator in the sample. Colons, which are normally used to delimit a section from a subkey in [ASP.NET Core configuration](xref:fundamentals/configuration), aren't allowed in secret names. Therefore, two dashes are used and swapped for a colon when the secrets are loaded into the app's configuration. + * Create two *Manual* secrets with the following name-value pairs. The first secret is a simple name and value, and the second secret creates a secret value with a section and subkey in the secret name: + * `SecretName`: `secret_value_1` + * `Section--SecretName`: `secret_value_2` + * Register the sample app with Azure Active Directory. + * Authorize the app to access the key vault. When you use the `Set-AzureRmKeyVaultAccessPolicy` PowerShell cmdlet to authorize the app to access the key vault, provide `List` and `Get` access to secrets with `-PermissionsToKeys list,get`. +2. Update the app's *appsettings.json* file with the values of `Vault`, `ClientId`, and `ClientSecret`. +3. Run the sample app, which obtains its configuration values from `IConfigurationRoot` with the same name as the secret name. + * Non-hierarchical values: The value for `SecretName` is obtained with `config["SecretName"]`. + * Hierarchical values (sections): Use `:` (colon) notation or the `GetSection` extension method. Use either of these approaches to obtain the configuration value: + * `config["Section:SecretName"]` + * `config.GetSection("Section")["SecretName"]` diff --git a/aspnetcore/security/key-vault-configuration/sample/Startup.cs b/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/Startup.cs similarity index 92% rename from aspnetcore/security/key-vault-configuration/sample/Startup.cs rename to aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/Startup.cs index 69253499d8c8..45921b1924b9 100644 --- a/aspnetcore/security/key-vault-configuration/sample/Startup.cs +++ b/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/Startup.cs @@ -35,7 +35,7 @@ public void Configure(IApplicationBuilder app) app.Run(async context => { var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); - var document = string.Format(Markup.Text, Configuration["MySecret"], Configuration["Section:MySecret"], Configuration.GetSection("Section")["MySecret"]); + var document = string.Format(Markup.Text, Configuration["SecretName"], Configuration["Section:SecretName"], Configuration.GetSection("Section")["SecretName"]); context.Response.ContentLength = encoding.GetByteCount(document); context.Response.ContentType = "text/html"; await context.Response.WriteAsync(document); diff --git a/aspnetcore/security/key-vault-configuration/sample/appsettings.json b/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/appsettings.json similarity index 100% rename from aspnetcore/security/key-vault-configuration/sample/appsettings.json rename to aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/appsettings.json diff --git a/aspnetcore/security/key-vault-configuration/sample/web.config b/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/web.config similarity index 73% rename from aspnetcore/security/key-vault-configuration/sample/web.config rename to aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/web.config index 10acd03c16c2..51faac366eeb 100644 --- a/aspnetcore/security/key-vault-configuration/sample/web.config +++ b/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x/web.config @@ -4,6 +4,6 @@ - + diff --git a/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/KeyVaultConfigProviderSample.csproj b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/KeyVaultConfigProviderSample.csproj new file mode 100644 index 000000000000..f7c90f0ce698 --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/KeyVaultConfigProviderSample.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp2.0 + Exe + + + + + + + diff --git a/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/Markup.cs b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/Markup.cs new file mode 100644 index 000000000000..5322a1981ab5 --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/Markup.cs @@ -0,0 +1,47 @@ +namespace KeyVaultConfigProviderSample +{ + internal static class Markup + { + internal const string Text = @" + + + + Key Vault Configuration Provider Sample + + + +

Key Vault Configuration Provider Sample

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
SecretName in Key VaultObtained from ConfigurationValue
SecretNameSecretNameConfiguration[""SecretName""]{0}
Section:SecretNameSection--SecretNameConfiguration[""Section:SecretName""]{1}
Configuration.GetSection(""Section"")[""SecretName""]{2}
+
+ + "; + } +} diff --git a/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/Program.cs b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/Program.cs new file mode 100644 index 000000000000..f6d4ecb2d858 --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/Program.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.AzureKeyVault; +using System.IO; + +namespace KeyVaultConfigProviderSample +{ + public static class Program + { + public static void Main(string[] args) + { + BuildWebHost(args).Run(); + } + + public static IWebHost BuildWebHost(string[] args) => + WebHost.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((context, config) => + { + #region snippet1 + config.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false) + .AddEnvironmentVariables(); + + var builtConfig = config.Build(); + + config.AddAzureKeyVault( + $"https://{builtConfig["Vault"]}.vault.azure.net/", + builtConfig["ClientId"], + builtConfig["ClientSecret"]); + #endregion + }) + .UseStartup() + .Build(); + } +} diff --git a/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/README.md b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/README.md new file mode 100644 index 000000000000..e35fe9a635ef --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/README.md @@ -0,0 +1,22 @@ +# Key Vault Configuration Provider sample application (ASP.NET Core 2.x) + +This sample illustrates the use of the Azure Key Vault Configuration Provider for ASP.NET Core 2.x. For the ASP.NET Core 1.x sample, see [Key Vault Configuration Provider sample application (ASP.NET Core 1.x)](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/key-vault-configuration/samples/basic-sample/1.x). + +For more information on how the sample works, see the [Azure Key Vault configuration provider](xref:security/key-vault-configuration) topic. + +## Using the sample +1. Create a key vault and set up Azure Active Directory (Azure AD) for the application following the guidance in [Get started with Azure Key Vault](https://azure.microsoft.com/documentation/articles/key-vault-get-started/). + * Add secrets to the key vault using the [AzureRM Key Vault PowerShell Module](/powershell/module/azurerm.keyvault) available from the [PowerShell Gallery](https://www.powershellgallery.com/packages/AzureRM.KeyVault), the [Azure Key Vault REST API](/rest/api/keyvault/), or the [Azure Portal](https://portal.azure.com/). Secrets are created as either *Manual* or *Certificate* secrets. *Certificate* secrets are certificates for use by apps and services but are not supported by the configuration provider. You should use the *Manual* option to create name-value pair secrets for use with the configuration provider. + * Simple secrets are created 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 separator in the sample. Colons, which are normally used to delimit a section from a subkey in [ASP.NET Core configuration](xref:fundamentals/configuration), aren't allowed in secret names. Therefore, two dashes are used and swapped for a colon when the secrets are loaded into the app's configuration. + * Create two *Manual* secrets with the following name-value pairs. The first secret is a simple name and value, and the second secret creates a secret value with a section and subkey in the secret name: + * `SecretName`: `secret_value_1` + * `Section--SecretName`: `secret_value_2` + * Register the sample app with Azure Active Directory. + * Authorize the app to access the key vault. When you use the `Set-AzureRmKeyVaultAccessPolicy` PowerShell cmdlet to authorize the app to access the key vault, provide `List` and `Get` access to secrets with `-PermissionsToKeys list,get`. +2. Update the app's *appsettings.json* file with the values of `Vault`, `ClientId`, and `ClientSecret`. +3. Run the sample app, which obtains its configuration values from `IConfigurationRoot` with the same name as the secret name. + * Non-hierarchical values: The value for `SecretName` is obtained with `config["SecretName"]`. + * Hierarchical values (sections): Use `:` (colon) notation or the `GetSection` extension method. Use either of these approaches to obtain the configuration value: + * `config["Section:SecretName"]` + * `config.GetSection("Section")["SecretName"]` diff --git a/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/Startup.cs b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/Startup.cs new file mode 100644 index 000000000000..5fa585960ffe --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/Startup.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.KeyVault.Models; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.AzureKeyVault; +using System.Reflection; +using System.Text; + +namespace KeyVaultConfigProviderSample +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; set; } + + public void Configure(IApplicationBuilder app) + { + app.Run(async context => + { + var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); + var document = string.Format(Markup.Text, Configuration["SecretName"], Configuration["Section:SecretName"], Configuration.GetSection("Section")["SecretName"]); + context.Response.ContentLength = encoding.GetByteCount(document); + context.Response.ContentType = "text/html"; + await context.Response.WriteAsync(document); + }); + } + } +} diff --git a/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/appsettings.json b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/appsettings.json new file mode 100644 index 000000000000..1803cdd7f32f --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/appsettings.json @@ -0,0 +1,5 @@ +{ + "Vault": "Azure Key Vault name (for example: contosovault)", + "ClientId": "Azure Active Directory Application Id (for example: 627e911e-43cc-61d4-992e-12db9c81b413)", + "ClientSecret": "Azure Active Directory Key (for example: g58K3dtg59o1Pa+e59v2Tx829w6VxTB2yv9sv/101di=)" +} diff --git a/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/web.config b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/web.config new file mode 100644 index 000000000000..51faac366eeb --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/basic-sample/2.x/web.config @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/Markup.cs b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/Markup.cs new file mode 100644 index 000000000000..f8c273d8b7e6 --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/Markup.cs @@ -0,0 +1,37 @@ +namespace KeyVaultConfigProviderSample +{ + internal static class Markup + { + internal const string Text = @" + + + + Key Vault Configuration Provider Sample + + + +

Key Vault Configuration Provider Sample

+
+ + + + + + + + + + + + + + + + + +
SecretName in Key VaultObtained from ConfigurationValue
AppSecret{0}-AppSecretConfiguration[""AppSecret""]{1}
+
+ + "; + } +} diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/PrefixKeyVaultConfigProviderSample.csproj b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/PrefixKeyVaultConfigProviderSample.csproj new file mode 100644 index 000000000000..996c02e677c6 --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/PrefixKeyVaultConfigProviderSample.csproj @@ -0,0 +1,25 @@ + + + + net451;netcoreapp1.1 + Exe + win7-x64 + 5.0.0.0 + + + + + + + + + + + + + + + + + + diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/Program.cs b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/Program.cs new file mode 100644 index 000000000000..4f4329debc20 --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/Program.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Logging; + +namespace KeyVaultConfigProviderSample +{ + public static class Program + { + public static void Main(string[] args) + { + var host = new WebHostBuilder() + .ConfigureLogging(factory => + { + factory.AddConsole(LogLevel.Debug); + }) + .UseKestrel() + .UseIISIntegration() + .UseStartup() + .Build(); + + host.Run(); + } + } +} diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/README.md b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/README.md new file mode 100644 index 000000000000..c55fcb47d412 --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/README.md @@ -0,0 +1,21 @@ +# Prefix Key Vault Configuration Provider sample application (ASP.NET Core 1.x) + +This sample illustrates the use of the Azure Key Vault Configuration Provider for ASP.NET Core 1.x using key name prefixes. For the ASP.NET Core 2.x sample, see [Prefix Key Vault Configuration Provider sample application (ASP.NET Core 2.x)](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x). + +> [!NOTE] +> The configuration provider isn't available for ASP.NET Core 1.0. If you want to implement the configuration provider and the app is an ASP.NET Core 1.0 app, upgrade the app to 1.1 or later first. + +For more information on how the sample works, see the [Azure Key Vault configuration provider](xref:security/key-vault-configuration) topic. + +## Using the sample +1. Create a key vault and set up Azure Active Directory (Azure AD) for the application following the guidance in [Get started with Azure Key Vault](https://azure.microsoft.com/documentation/articles/key-vault-get-started/). + * Add secrets to the key vault using the Azure PowerShell Module, the Azure Management API, or the Azure Portal. Secrets are created as either *Manual* or *Certificate* secrets. *Certificate* secrets are certificates for use by apps and services but are not supported by the configuration provider. You should use the *Manual* option to create name-value pair secrets for use with the configuration provider. + * Hierarchical values (configuration sections) use `--` (two dashes) as a separator. + * For the sample app, create two *Manual* secrets with the following name-value pairs: + * `5000-AppSecret`: `5.0.0.0_secret_value` + * `5100-AppSecret`: `5.1.0.0_secret_value` + * Register the sample app with Azure Active Directory. + * Authorize the app to access the key vault. When you use the `Set-AzureRmKeyVaultAccessPolicy` PowerShell cmdlet to authorize the app to access the key vault, provide `List` and `Get` access to secrets with `-PermissionsToKeys list,get`. +2. Update the app's *appsettings.json* file with the values of `Vault`, `ClientId`, and `ClientSecret`. +3. Run the sample app, which obtains its configuration values from `IConfigurationRoot` with the same name as the prefixed secret name. In this sample, the prefix is the app's version, which you provided to the `PrefixKeyVaultSecretManager` when you added the Azure Key Vault configuration provider. The value for `AppSecret` is obtained with `config["AppSecret"]`. +4. Change the version of the app assembly in the project file from `5.0.0.0` to `5.1.0.0` and run the app again. This time, the secret value returned is `5.1.0.0_secret_value`. diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/Startup.cs b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/Startup.cs new file mode 100644 index 000000000000..b4cb4401f128 --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/Startup.cs @@ -0,0 +1,84 @@ +using System.IO; +using System.Text; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; + +namespace KeyVaultConfigProviderSample +{ + public class Startup + { + public Startup() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false) + .AddEnvironmentVariables(); + + var config = builder.Build(); + + #region snippet1 + var appVersion = Assembly.GetEntryAssembly().GetName().Version.ToString(); + // Remove periods from the version. Azure Key Vault secret names + // are alphanumeric with dashes, so periods aren't allowed in + // the prefixes. + var versionPrefix = appVersion.Replace(".", string.Empty); + + builder.AddAzureKeyVault( + $"https://{config["Vault"]}.vault.azure.net/", + config["ClientId"], + config["ClientSecret"]), + new PrefixKeyVaultSecretManager(versionPrefix)); + #endregion + + Configuration = builder.Build(); + } + + public IConfigurationRoot Configuration { get; set; } + + public void Configure(IApplicationBuilder app) + { + app.Run(async context => + { + var appVersion = Assembly.GetEntryAssembly().GetName().Version.ToString(); + var versionPrefix = appVersion.Replace(".", string.Empty); + var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); + var document = string.Format(Markup.Text, versionPrefix, Configuration["AppSecret"]); + context.Response.ContentLength = encoding.GetByteCount(document); + context.Response.ContentType = "text/html"; + await context.Response.WriteAsync(document); + }); + } + } + + #region snippet2 + public class PrefixKeyVaultSecretManager : IKeyVaultSecretManager + { + private readonly string _prefix; + + public PrefixKeyVaultSecretManager(string prefix) + { + _prefix = $"{prefix}-"; + } + + public bool Load(SecretItem secret) + { + // Load a vault secret when its secret name starts with the + // prefix. Other secrets won't be loaded. + return secret.Identifier.Name.StartsWith(_prefix); + } + + public string GetKey(SecretBundle secret) + { + // Remove the prefix from the secret name and replace 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. + return secret.SecretIdentifier.Name + .Substring(_prefix.Length) + .Replace("--", ConfigurationPath.KeyDelimiter); + } + } + #endregion +} diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/appsettings.json b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/appsettings.json new file mode 100644 index 000000000000..1803cdd7f32f --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/appsettings.json @@ -0,0 +1,5 @@ +{ + "Vault": "Azure Key Vault name (for example: contosovault)", + "ClientId": "Azure Active Directory Application Id (for example: 627e911e-43cc-61d4-992e-12db9c81b413)", + "ClientSecret": "Azure Active Directory Key (for example: g58K3dtg59o1Pa+e59v2Tx829w6VxTB2yv9sv/101di=)" +} diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/web.config b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/web.config new file mode 100644 index 000000000000..51faac366eeb --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x/web.config @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/Markup.cs b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/Markup.cs new file mode 100644 index 000000000000..f8c273d8b7e6 --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/Markup.cs @@ -0,0 +1,37 @@ +namespace KeyVaultConfigProviderSample +{ + internal static class Markup + { + internal const string Text = @" + + + + Key Vault Configuration Provider Sample + + + +

Key Vault Configuration Provider Sample

+
+ + + + + + + + + + + + + + + + + +
SecretName in Key VaultObtained from ConfigurationValue
AppSecret{0}-AppSecretConfiguration[""AppSecret""]{1}
+
+ + "; + } +} diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/PrefixKeyVaultConfigProviderSample.csproj b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/PrefixKeyVaultConfigProviderSample.csproj new file mode 100644 index 000000000000..a55159c7022e --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/PrefixKeyVaultConfigProviderSample.csproj @@ -0,0 +1,13 @@ + + + + netcoreapp2.0 + Exe + 5.0.0.0 + + + + + + + diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/Program.cs b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/Program.cs new file mode 100644 index 000000000000..d9d2c469760c --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/Program.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.AzureKeyVault; +using System.IO; + +namespace KeyVaultConfigProviderSample +{ + public static class Program + { + public static void Main(string[] args) + { + BuildWebHost(args).Run(); + } + + public static IWebHost BuildWebHost(string[] args) => + WebHost.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((context, config) => + { + config.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false) + .AddEnvironmentVariables(); + + var builtConfig = config.Build(); + + #region snippet1 + // The appVersion obtains the app version (5.0.0.0), which + // is set in the project file and obtained from the entry + // assembly. The versionPrefix holds the version without + // dot notation for the PrefixKeyVaultSecretManager. + var appVersion = Assembly.GetEntryAssembly().GetName().Version.ToString(); + var versionPrefix = appVersion.Replace(".", string.Empty); + + config.AddAzureKeyVault( + $"https://{builtConfig["Vault"]}.vault.azure.net/", + builtConfig["ClientId"], + builtConfig["ClientSecret"], + new PrefixKeyVaultSecretManager(versionPrefix)); + #endregion + }) + .UseStartup() + .Build(); + } +} diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/README.md b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/README.md new file mode 100644 index 000000000000..227f1411eb2d --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/README.md @@ -0,0 +1,18 @@ +# Prefix Key Vault Configuration Provider sample application (ASP.NET Core 2.x) + +This sample illustrates the use of the Azure Key Vault Configuration Provider for ASP.NET Core 2.x using key name prefixes. For the ASP.NET Core 1.x sample, see [Prefix Key Vault Configuration Provider sample application (ASP.NET Core 1.x)](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/1.x). + +For more information on how the sample works, see the [Azure Key Vault configuration provider](xref:security/key-vault-configuration) topic. + +## Using the sample +1. Create a key vault and set up Azure Active Directory (Azure AD) for the application following the guidance in [Get started with Azure Key Vault](https://azure.microsoft.com/documentation/articles/key-vault-get-started/). + * Add secrets to the key vault using the Azure PowerShell Module, the Azure Management API, or the Azure Portal. Secrets are created as either *Manual* or *Certificate* secrets. *Certificate* secrets are certificates for use by apps and services but are not supported by the configuration provider. You should use the *Manual* option to create name-value pair secrets for use with the configuration provider. + * Hierarchical values (configuration sections) use `--` (two dashes) as a separator. + * For the sample app, create two *Manual* secrets with the following name-value pairs: + * `5000-AppSecret`: `5.0.0.0_secret_value` + * `5100-AppSecret`: `5.1.0.0_secret_value` + * Register the sample app with Azure Active Directory. + * Authorize the app to access the key vault. When you use the `Set-AzureRmKeyVaultAccessPolicy` PowerShell cmdlet to authorize the app to access the key vault, provide `List` and `Get` access to secrets with `-PermissionsToKeys list,get`. +2. Update the app's *appsettings.json* file with the values of `Vault`, `ClientId`, and `ClientSecret`. +3. Run the sample app, which obtains its configuration values from `IConfigurationRoot` with the same name as the prefixed secret name. In this sample, the prefix is the app's version, which you provided to the `PrefixKeyVaultSecretManager` when you added the Azure Key Vault configuration provider. The value for `AppSecret` is obtained with `config["AppSecret"]`. +4. Change the version of the app assembly in the project file from `5.0.0.0` to `5.1.0.0` and run the app again. This time, the secret value returned is `5.1.0.0_secret_value`. diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/Startup.cs b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/Startup.cs new file mode 100644 index 000000000000..497088beb9fe --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/Startup.cs @@ -0,0 +1,64 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.KeyVault.Models; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.AzureKeyVault; +using System.Reflection; +using System.Text; + +namespace KeyVaultConfigProviderSample +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; set; } + + public void Configure(IApplicationBuilder app) + { + app.Run(async context => + { + var appVersion = Assembly.GetEntryAssembly().GetName().Version.ToString(); + var versionPrefix = appVersion.Replace(".", string.Empty); + var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); + var document = string.Format(Markup.Text, versionPrefix, Configuration["AppSecret"]); + context.Response.ContentLength = encoding.GetByteCount(document); + context.Response.ContentType = "text/html"; + await context.Response.WriteAsync(document); + }); + } + } + + #region snippet1 + public class PrefixKeyVaultSecretManager : IKeyVaultSecretManager + { + private readonly string _prefix; + + public PrefixKeyVaultSecretManager(string prefix) + { + _prefix = $"{prefix}-"; + } + + public bool Load(SecretItem secret) + { + // Load a vault secret when its secret name starts with the + // prefix. Other secrets won't be loaded. + return secret.Identifier.Name.StartsWith(_prefix); + } + + public string GetKey(SecretBundle secret) + { + // Remove the prefix from the secret name and replace 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. + return secret.SecretIdentifier.Name + .Substring(_prefix.Length) + .Replace("--", ConfigurationPath.KeyDelimiter); + } + } + #endregion +} diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/appsettings.json b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/appsettings.json new file mode 100644 index 000000000000..1803cdd7f32f --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/appsettings.json @@ -0,0 +1,5 @@ +{ + "Vault": "Azure Key Vault name (for example: contosovault)", + "ClientId": "Azure Active Directory Application Id (for example: 627e911e-43cc-61d4-992e-12db9c81b413)", + "ClientSecret": "Azure Active Directory Key (for example: g58K3dtg59o1Pa+e59v2Tx829w6VxTB2yv9sv/101di=)" +} diff --git a/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/web.config b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/web.config new file mode 100644 index 000000000000..51faac366eeb --- /dev/null +++ b/aspnetcore/security/key-vault-configuration/samples/key-name-prefix-sample/2.x/web.config @@ -0,0 +1,9 @@ + + + + + + + + +