Edit

Share via


.NET Aspire Azure Key Vault integration

Includes: Hosting integration and Client integration

Azure Key Vault is a cloud service for securely storing and accessing secrets. The .NET Aspire Azure Key Vault integration enables you to connect to Azure Key Vault instances from your .NET applications.

Hosting integration

The Azure Key Vault hosting integration models a Key Vault resource as the AzureKeyVaultResource type. To access this type and APIs for expressing them within your app host project, install the 📦 Aspire.Hosting.Azure.KeyVault NuGet package:

.NET CLI
dotnet add package Aspire.Hosting.Azure.KeyVault

For more information, see dotnet add package or Manage package dependencies in .NET applications.

Add Azure Key Vault resource

In your app host project, call AddAzureKeyVault on the builder instance to add an Azure Key Vault resource:

C#
var builder = DistributedApplication.CreateBuilder(args);

var keyVault = builder.AddAzureKeyVault("key-vault");

builder.AddProject<Projects.ExampleProject>()
       .WithReference(keyVault);

// After adding all resources, run the app...

The WithReference method configures a connection in the ExampleProject named "key-vault".

Important

By default, AddAzureKeyVault configures a Key Vault Administrator built-in role.

Tip

When you call AddAzureKeyVault, it implicitly calls AddAzureProvisioning, which adds support for generating Azure resources dynamically during app startup. The app must configure the appropriate subscription and location. For more information, see Local provisioning: Configuration.

Generated provisioning Bicep

If you're new to Bicep, it's a domain-specific language for defining Azure resources. With .NET Aspire, you don't need to write Bicep by-hand, instead the provisioning APIs generate Bicep for you. When you publish your app, the generated Bicep is output alongside the manifest file. When you add an Azure Key Vault resource, the following Bicep is generated:

Bicep
@description('The location for the resource(s) to be deployed.')
param location string = resourceGroup().location

param principalType string

param principalId string

resource key_vault 'Microsoft.KeyVault/vaults@2023-07-01' = {
  name: take('keyvault-${uniqueString(resourceGroup().id)}', 24)
  location: location
  properties: {
    tenantId: tenant().tenantId
    sku: {
      family: 'A'
      name: 'standard'
    }
    enableRbacAuthorization: true
  }
  tags: {
    'aspire-resource-name': 'key-vault'
  }
}

resource key_vault_KeyVaultAdministrator 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(key_vault.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483'))
  properties: {
    principalId: principalId
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')
    principalType: principalType
  }
  scope: key_vault
}

output vaultUri string = key_vault.properties.vaultUri

The preceding Bicep is a module that provisions an Azure Key Vault resource with the following defaults:

  • location: The location of the resource group.
  • principalId: The principal ID of the user or service principal.
  • principalType: The principal type of the user or service principal.
  • key_vault: The Azure Key Vault resource:
    • name: A unique name for the Azure Key Vault.
    • properties: The Azure Key Vault properties:
      • tenantId: The tenant ID of the Azure Key Vault.
      • sku: The Azure Key Vault SKU:
        • family: The SKU family.
        • name: The SKU name.
      • enableRbacAuthorization: A boolean value that indicates whether the Azure Key Vault has role-based access control (RBAC) authorization enabled.
    • tags: The Azure Key Vault tags.
  • key_vault_KeyVaultAdministrator: The Azure Key Vault administrator role assignment:
    • name: A unique name for the role assignment.
    • properties: The role assignment properties:
      • principalId: The principal ID of the user or service principal.
      • roleDefinitionId: The role definition ID of the Azure Key Vault administrator role.
      • principalType: The principal type of the user or service principal.
    • scope: The scope of the role assignment.
  • output: The Azure Key Vault URI.

The generated Bicep is a starting point and can be customized to meet your specific requirements.

Customize provisioning infrastructure

All .NET Aspire Azure resources are subclasses of the AzureProvisioningResource type. This type enables the customization of the generated Bicep by providing a fluent API to configure the Azure resources by using the ConfigureInfrastructure<T>(IResourceBuilder<T>, Action<AzureResourceInfrastructure>) API. For example, you can configure the sku, RBAC, tags, and more. The following example demonstrates how to customize the Azure Key Vault resource:

C#
builder.AddAzureKeyVault("key-vault")
    .ConfigureInfrastructure(infra =>
    {
        var keyVault = infra.GetProvisionableResources()
                            .OfType<KeyVaultService>()
                            .Single();

        keyVault.Properties.Sku = new()
        {
            Family = KeyVaultSkuFamily.A,
            Name = KeyVaultSkuName.Premium,
        };
        keyVault.Properties.EnableRbacAuthorization = true;
        keyVault.Tags.Add("ExampleKey", "Example value");
    });

The preceding code:

There are many more configuration options available to customize the Key Vault resource. For more information, see Azure.Provisioning.KeyVault and Azure.Provisioning customization.

Connect to an existing Azure Key Vault instance

You might have an existing Azure Key Vault instance that you want to connect to. Instead of representing a new Azure Key Vault resource, you can add a connection string to the app host. To add a connection to an existing Azure Key Vault resource, call the AddConnectionString method:

C#
var builder = DistributedApplication.CreateBuilder(args);

var keyVault = builder.AddConnectionString("key-vault");

builder.AddProject<Projects.WebApplication>("web")
       .WithReference(keyVault);

// After adding all resources, run the app...

Note

Connection strings are used to represent a wide range of connection information, including database connections, message brokers, endpoint URIs, and other services. In .NET Aspire nomenclature, the term "connection string" is used to represent any kind of connection information.

The connection string is configured in the app host's configuration, typically under User Secrets, under the ConnectionStrings section. The app host injects this connection string as an environment variable into all dependent resources, for example:

JSON
{
  "ConnectionStrings": {
    "key-vault": "https://{account_name}.vault.azure.net/"
  }
}

The dependent resource can access the injected connection string by calling the GetConnectionString method, and passing the connection name as the parameter, in this case "key-vault". The GetConnectionString API is shorthand for IConfiguration.GetSection("ConnectionStrings")[name].

Client integration

To get started with the .NET Aspire Azure Key Vault client integration, install the 📦 Aspire.Azure.Security.KeyVault NuGet package in the client-consuming project, that is, the project for the application that uses the Azure Key Vault client.

.NET CLI
dotnet add package Aspire.Azure.Security.KeyVault

The client integration provides two ways to access secrets from Azure Key Vault:

  • Add secrets to app configuration, using either the IConfiguration or the IOptions<T> pattern.
  • Use a SecretClient to retrieve secrets on demand.

Add secrets to configuration

In the Program.cs file of your client-consuming project, call the AddAzureKeyVaultSecrets extension method on the IConfiguration to add the secrets as part of your app's configuration. The method takes a connection name parameter.

C#
builder.Configuration.AddAzureKeyVaultSecrets(connectionName: "key-vault");

Note

The AddAzureKeyVaultSecrets API name has caused a bit of confusion. The method is used to configure the SecretClient based on the given connection name, and it's not used to add secrets to the configuration.

Tip

The connectionName parameter must match the name used when adding the Azure Key Vault resource in the app host project. For more information, see Add Azure Key Vault resource.

You can then retrieve a secret-based configuration value through the normal IConfiguration APIs, or even by binding to strongly-typed classes with the options pattern. To retrieve a secret from an example service class that's been registered with the dependency injection container, consider the following snippets:

Retrieve IConfiguration instance

C#
public class ExampleService(IConfiguration configuration)
{
    // Use configuration...
    private string _secretValue = configuration["SecretKey"];
}

The preceding example assumes that you've also registered the IConfiguration instance for dependency injection. For more information, see Dependency injection in .NET.

Retrieve IOptions<T> instance

C#
public class ExampleService(IOptions<SecretOptions> options)
{
    // Use options...
    private string _secretValue = options.Value.SecretKey;
}

The preceding example assumes that you've configured a SecretOptions class for use with the options pattern. For more information, see Options pattern in .NET.

Additional AddAzureKeyVaultSecrets API parameters are available optionally for the following scenarios:

  • Action<AzureSecurityKeyVaultSettings>? configureSettings: To set up some or all the options inline.
  • Action<SecretClientOptions>? configureClientOptions: To set up the SecretClientOptions inline.
  • AzureKeyVaultConfigurationOptions? options: To configure the AzureKeyVaultConfigurationOptions inline.

Add an Azure Secret client

Alternatively, you can use the SecretClient directly to retrieve the secrets on demand. This requires a slightly different registration API.

In the Program.cs file of your client-consuming project, call the AddAzureKeyVaultClient extension on the IHostApplicationBuilder instance to register a SecretClient for use via the dependency injection container.

C#
builder.AddAzureKeyVaultClient(connectionName: "key-vault");

Tip

The connectionName parameter must match the name used when adding the Azure Key Vault resource in the app host project. For more information, see Add Azure Key Vault resource.

After adding the SecretClient to the builder, you can get the SecretClient instance using dependency injection. For example, to retrieve the client from an example service define it as a constructor parameter and ensure the ExampleService class is registered with the dependency injection container:

C#
public class ExampleService(SecretClient client)
{
    // Use client...
}

For more information on dependency injection, see .NET dependency injection.

Add keyed Azure Key Vault client

There might be situations where you want to register multiple SecretClient instances with different connection names. To register keyed Azure Key Vault clients, call the AddKeyedAzureKeyVaultClient method:

C#
builder.AddKeyedAzureKeyVaultClient(name: "feature-toggles");
builder.AddKeyedAzureKeyVaultClient(name: "admin-portal");

Then you can retrieve the SecretClient instances using dependency injection. For example, to retrieve the client from an example service:

C#
public class ExampleService(
    [FromKeyedServices("feature-toggles")] SecretClient featureTogglesClient,
    [FromKeyedServices("admin-portal")] SecretClient adminPortalClient)
{
    // Use clients...
}

For more information on keyed services, see .NET dependency injection: Keyed services.

Configuration

The .NET Aspire Azure Key Vault integration provides multiple options to configure the SecretClient based on the requirements and conventions of your project.

Use configuration providers

The .NET Aspire Azure Key Vault integration supports Microsoft.Extensions.Configuration. It loads the AzureSecurityKeyVaultSettings from appsettings.json or other configuration files using Aspire:Azure:Security:KeyVault key.

JSON
{
  "Aspire": {
    "Azure": {
      "Security": {
        "KeyVault": {
          "DisableHealthChecks": true,
          "DisableTracing": false,
          "ClientOptions": {
            "Diagnostics": {
              "ApplicationId": "myapp"
            }
          }
        }
      }
    }
  }
}

For the complete Azure Key Vault client integration JSON schema, see Aspire.Azure.Security.KeyVault/ConfigurationSchema.json.

If you have set up your configurations in the Aspire:Azure:Security:KeyVault section of your appsettings.json file you can just call the method AddAzureKeyVaultSecrets without passing any parameters.

Use inline delegates

You can also pass the Action<AzureSecurityKeyVaultSettings> delegate to set up some or all the options inline, for example to set the AzureSecurityKeyVaultSettings.VaultUri:

C#
builder.AddAzureKeyVaultSecrets(
    connectionName: "key-vault",
    configureSettings: settings => settings.VaultUri = new Uri("KEY_VAULT_URI"));

You can also set up the SecretClientOptions using Action<SecretClientOptions> delegate, which is an optional parameter of the AddAzureKeyVaultSecrets method. For example to set the KeyClientOptions.DisableChallengeResourceVerification ID to identify the client:

C#
builder.AddAzureKeyVaultSecrets(
    connectionName: "key-vault",
    configureClientOptions: options => options.DisableChallengeResourceVerification = true))

Configuration options

The following configurable options are exposed through the AzureSecurityKeyVaultSettings class:

Name Description
AzureSecurityKeyVaultSettings.Credential The credential used to authenticate to the Azure Key Vault.
AzureSecurityKeyVaultSettings.DisableHealthChecks A boolean value that indicates whether the Key Vault health check is disabled or not.
AzureSecurityKeyVaultSettings.DisableTracing A boolean value that indicates whether the OpenTelemetry tracing is disabled or not.
AzureSecurityKeyVaultSettings.VaultUri A URI to the vault on which the client operates. Appears as "DNS Name" in the Azure portal.

Client integration health checks

By default, .NET Aspire client integrations have health checks enabled for all services. Similarly, many .NET Aspire hosting integrations also enable health check endpoints. For more information, see:

The .NET Aspire Azure Key Vault integration includes the following health checks:

  • Adds the AzureKeyVaultSecretsHealthCheck health check, which attempts to connect to and query the Key Vault
  • Integrates with the /health HTTP endpoint, which specifies all registered health checks must pass for app to be considered ready to accept traffic

Observability and telemetry

.NET Aspire integrations automatically set up Logging, Tracing, and Metrics configurations, which are sometimes known as the pillars of observability. For more information about integration observability and telemetry, see .NET Aspire integrations overview. Depending on the backing service, some integrations may only support some of these features. For example, some integrations support logging and tracing, but not metrics. Telemetry features can also be disabled using the techniques presented in the Configuration section.

Logging

The .NET Aspire Azure Key Vault integration uses the following log categories:

  • Azure.Core
  • Azure.Identity

Tracing

The .NET Aspire Azure Key Vault integration will emit the following tracing activities using OpenTelemetry:

  • Azure.Security.KeyVault.Secrets.SecretClient

Metrics

The .NET Aspire Azure Key Vault integration currently does not support metrics by default due to limitations with the Azure SDK.

See also