Skip to main content

Overview

NuGet.Services.Configuration is a shared infrastructure library that bridges the standard Microsoft.Extensions.Configuration pipeline with Azure Key Vault secret injection. It provides custom IConfigurationSource and IConfigurationProvider wrappers that transparently resolve Key Vault secret references embedded inside configuration values — no application code needs to know whether a value came from a JSON file, an environment variable, or Key Vault. The library also ships an older, attribute-driven configuration model built around its own IConfigurationProvider and IConfigurationFactory abstractions. This model lets configuration POCO classes declare their Key Vault key names and prefixes through [ConfigurationKey] and [ConfigurationKeyPrefix] attributes and uses ConfigurationFactory to reflectively populate them at startup. This older pattern predates the Microsoft.Extensions.Configuration binder integration and coexists with the newer SecretInjectedConfiguration wrappers. Supporting both approaches, the library also provides ConfigurationRootSecretReaderFactory, which reads Key Vault connection details (KeyVault_VaultName, KeyVault_UseManagedIdentity, etc.) from the configuration root itself and produces the ISecretReader / ISecretInjector used by everything else. A NonCachingOptionsSnapshot<T> implementation ensures that when KeyVaultInjectingConfigurationProvider is in use, options objects are not cached across requests, allowing Key Vault secret rotation to take effect without a service restart.

Role in System

Key Vault Injection at Read Time

KeyVaultInjectingConfigurationProvider wraps any existing IConfigurationProvider and calls ISecretInjector.InjectAsync on every TryGet call. A hardcoded exclusion list prevents injection on known connection-string keys that must be passed as-is.

Attribute-Driven POCO Binding

ConfigurationFactory uses reflection and TypeDescriptor to populate Configuration subclass properties. [ConfigurationKey] overrides the key name; [ConfigurationKeyPrefix] adds a prefix at the class or property level; [Required] and [DefaultValue] control error vs. fallback behavior.

Cached Secret Injection Wrapper

SecretInjectedConfiguration wraps any IConfiguration after the builder stage. It uses ICachingSecretInjector.TryInjectCached first, falling back to a live Key Vault call only on a cache miss, reducing latency and request volume.

Managed Identity and Certificate Auth

ConfigurationRootSecretReaderFactory supports two Key Vault auth modes: managed identity (with an optional client ID) and certificate-based auth (thumbprint, store name, store location). It rejects configurations that supply both simultaneously.

Key Files and Classes

Dependencies

NuGet Package References

Internal Project References

Notable Patterns and Implementation Details

Sync-over-async in KeyVaultInjectingConfigurationProvider.TryGet. Secret injection calls _secretInjector.InjectAsync(...).ConfigureAwait(false).GetAwaiter().GetResult() synchronously. This is a known limitation: Microsoft.Extensions.Configuration providers expose only synchronous TryGet, so there is no async path available at this layer. This can cause thread-pool starvation under high load if Key Vault calls are slow.
Hardcoded injection exclusions. KeyVaultInjectingConfigurationProvider skips secret injection for four specific keys: GalleryDb:ConnectionString, ValidationDb:ConnectionString, SupportRequestDb:ConnectionString, and StatisticsDb:ConnectionString. These are expected to contain raw ADO.NET connection strings that must not be processed as Key Vault references.
Two distinct secret injection lifecycles coexist. KeyVaultInjectingConfigurationProvider (via ConfigurationBuilderExtensions) injects on every TryGet call during the provider pipeline. SecretInjectedConfiguration (via ConfigurationUtility.ConfigureInjected) injects at options-binding time using a caching injector. SecretConfigurationReader injects eagerly at construction and on Reload. Services may use any of these; newer code generally prefers the SecretInjectedConfiguration path.
NonCachingOptionsSnapshot<T> must be registered explicitly. The default IOptionsSnapshot<T> caches the bound options object for the lifetime of the current scope (request). When Key Vault secret rotation is required, the consuming service must replace the default registration with NonCachingOptionsSnapshot<T> using services.Add(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(NonCachingOptionsSnapshot<>))) before services.AddOptions().
Local development mode. When Local_Development is true in configuration, ConfigurationRootSecretReaderFactory passes a localDevelopment: true flag into KeyVaultConfiguration. Combined with ConfigurationExtensions.GetTokenCredential returning DefaultAzureCredential in DEBUG builds, this allows developers to authenticate via their local Azure CLI or Visual Studio credentials instead of a managed identity or certificate.
SecretConfigurationReader.Providers throws NotImplementedException. Any code that tries to enumerate the underlying providers via IConfigurationRoot.Providers on a SecretConfigurationReader instance will receive a NotImplementedException. This is intentional but can cause failures if middleware or libraries inspect the provider list.