FluentAzure 0.3.0-rc.1
dotnet add package FluentAzure --version 0.3.0-rc.1
NuGet\Install-Package FluentAzure -Version 0.3.0-rc.1
<PackageReference Include="FluentAzure" Version="0.3.0-rc.1" />
<PackageVersion Include="FluentAzure" Version="0.3.0-rc.1" />
<PackageReference Include="FluentAzure" />
paket add FluentAzure --version 0.3.0-rc.1
#r "nuget: FluentAzure, 0.3.0-rc.1"
#:package FluentAzure@0.3.0-rc.1
#addin nuget:?package=FluentAzure&version=0.3.0-rc.1&prerelease
#tool nuget:?package=FluentAzure&version=0.3.0-rc.1&prerelease
FluentAzure
Guard rails for .NET configuration on Azure: fail-fast validation, secret redaction, health checks and compile-time analyzers. It works on top of Microsoft's App Configuration and Key Vault providers, or with FluentAzure's own configuration pipeline.
What's new in 0.3.0-rc.1:
- A configuration guard that validates, health-checks and redacts configuration from any provider, including Microsoft's
AddAzureAppConfiguration()andAddAzureKeyVault().- Analyzers that check Microsoft's APIs and the Azure SDK clients as well as FluentAzure's.
- An
IConfigurationprovider withIOptionsMonitor<T>reload, an App Configuration source, managed/workload identity, OpenTelemetry and .NET 10 / Native AOT support.- One binder for every typed API, and deprecation of the build-once
AddFluentAzure<T>()registrations in favour of the options pattern.See the changelog and the upgrade guide.
π‘οΈ Why FluentAzure?
Microsoft's packages load configuration. FluentAzure makes sure it's complete, stays secret, and stays healthy, without changing how you load it.
| Microsoft's providers on their own | With FluentAzure | |
|---|---|---|
| A required setting is missing or invalid | Found at first use, often as a null or an exception inside a request |
Startup stops, listing every missing or invalid key |
| Logging the configuration | GetDebugView() prints secrets |
GetRedactedDebugView() masks Key Vault values and anything that looks like a credential |
| A refresh brings in a bad value | Nothing notices | The health check re-runs your rules |
| Code review | Nothing flags insecure setup | Analyzers flag http:// endpoints, hard-coded Secret= connection strings and GetDebugView() at compile time |
| Error messages | Can echo the value | Name the key and the rule, never the value |
β‘ Quick Start
Keep loading configuration the way you do today, and add the guard:
using Azure.Identity;
using FluentAzure;
var builder = WebApplication.CreateBuilder(args);
var credential = new DefaultAzureCredential();
// Microsoft's providers, unchanged
builder.Configuration
.AddAzureAppConfiguration(o => o.Connect(new Uri("https://myconfig.azconfig.io"), credential))
.AddAzureKeyVault(new Uri("https://myvault.vault.azure.net/"), credential);
// FluentAzure: fail fast at startup, with every problem listed by key (never by value)
builder.Services.AddFluentAzureGuard(guard => guard
.Required("Database:ConnectionString", "Jwt:SecretKey", "Jwt:Issuer")
.Validate("Jwt:SecretKey", v => v.Length >= 32, "must be at least 32 characters")
.Sensitive("Payments:*"));
// ...and keep checking after configuration refreshes
builder.Services.AddHealthChecks().AddFluentAzureGuard();
If Jwt:SecretKey is too short, the app doesn't start:
OptionsValidationException: Configuration key 'Jwt:SecretKey' must be at least 32 characters.
The analyzers ship in the same package and need no setup. See Configuration guard for the full guide and examples/WebApi.Example for a complete app.
Prefer a single fluent pipeline instead of Microsoft's providers? FluentAzure has its own sources for environment variables, JSON, Key Vault and App Configuration, with the same safety features. See Where to Start and the usage patterns below.
π§ Where to Start
| You are building⦠| Use | Docs |
|---|---|---|
An app that already uses Microsoft's AddAzureAppConfiguration() / AddAzureKeyVault() |
The configuration guard: builder.Services.AddFluentAzureGuard(...) adds fail-fast startup validation, a health check and secret redaction on top of what you have |
Configuration guard |
| An ASP.NET Core app, Azure Functions (isolated) or a worker service | The IConfiguration provider: builder.Configuration.AddFluentAzureAsync(...) + AddFluentAzureOptions<T>(). You get reload, IOptionsMonitor<T>, health checks and fail-fast startup |
Configuration integration |
| A console tool, script or test | The standalone pipeline: FluentConfig.Create()...BuildAsync() returns a Result you can Match |
Examples below |
| A trimmed or Native AOT app | The provider + the configuration binding source generator | Native AOT |
Whichever you choose:
- Identity: pick one identity for Azure with
UseManagedIdentity(). Identity & secrets - Where settings live: put settings and feature flags in App Configuration and secrets in Key Vault. App Configuration, Key Vault
π¦ Installation
dotnet add package FluentAzure --prerelease
The current version is shown on the NuGet badge above. The package includes the FluentAzure analyzers (see docs/analyzers.md).
π’ Version Information
You can access the current version programmatically:
using FluentAzure;
// Get the current version
string version = FluentConfig.CurrentVersion; // e.g. "1.2.0" or "1.3.0-rc.1"
// Or access version details directly
int major = FluentAzure.Version.Major;
int minor = FluentAzure.Version.Minor;
int patch = FluentAzure.Version.Patch;
bool isPreRelease = FluentAzure.Version.IsPreRelease;
π Example Usage Patterns
Standalone Pipeline (console apps, scripts, tests)
using FluentAzure; // Single using statement!
var config = await FluentConfig
.Create() // Ultra clean - just FluentConfig.Create()!
.FromEnvironment()
.Required("DATABASE_URL")
.Optional("CACHE_TTL", "300")
.BuildAsync();
Complex Enterprise Setup
using FluentAzure; // Single using statement!
var configResult = await FluentConfig
.Create() // Ultra clean - just FluentConfig.Create()!
.FromJsonFile("appsettings.json")
.FromEnvironment()
.FromKeyVault("https://company-prod-kv.vault.azure.net")
.Transform("ConnectionString", DecryptConnectionString)
.Validate(c => Uri.IsWellFormedUriString(c["ServiceUrl"], UriKind.Absolute) ? null : "ServiceUrl must be a valid URI")
.BuildAsync();
var config = configResult.Bind<AppConfiguration>();
Dependency Injection
Register settings with the options pattern: bind from IConfiguration (fed by Microsoft's providers and guarded by AddFluentAzureGuard(), or by FluentAzure's provider below) with AddFluentAzureOptions<T>(), and inject IOptions<T> or IOptionsMonitor<T>.
Configuration keys are case-insensitive, and environment variables using the standard __ separator (e.g. ConnectionStrings__Default) are also available under their : form (ConnectionStrings:Default).
The older
services.AddFluentAzure<T>(...)/AddFluentAzureAsync<T>(...)registrations are obsolete and will be removed in 1.0. They build once at startup and registerTas a singleton, with no reload. See the upgrade guide for the replacement.
FluentAzure's sources in ASP.NET Core / Functions (IConfiguration + IOptionsMonitor)
Plug a FluentAzure pipeline into the standard configuration system. Required keys and validations still fail fast at startup, and values become available to IConfiguration, IOptions<T> and IOptionsMonitor<T>:
using FluentAzure;
var builder = WebApplication.CreateBuilder(args);
await builder.Configuration.AddFluentAzureAsync(
fluent => fluent
.FromKeyVault(builder.Configuration["KeyVault:Url"]!)
.Required("Database:ConnectionString"),
reloadInterval: TimeSpan.FromMinutes(5) // pick up rotated secrets without a restart
);
// Binds the "Database" section, validates [Required]/[Range] etc. and fails at startup if invalid
builder.Services.AddFluentAzureOptions<DatabaseOptions>(builder.Configuration, "Database");
// Consumers: inject IOptionsMonitor<DatabaseOptions> to see reloaded values
- Keys using the
__separator (environment variables, FluentAzure JSON flattening) are exposed with the standard:separator. - A failed reload keeps the last good values; observe failures with
options.OnReloadErrorvia theAddFluentAzure(configure, configureSource)overload. builder.Configuration.AddFluentAzure(...)is the synchronous equivalent.
Azure App Configuration
Load settings, feature flags and Key Vault references from App Configuration, with label layering, snapshots and sentinel-key refresh:
var config = await FluentConfig
.Create()
.FromAppConfiguration("https://myconfig.azconfig.io", options =>
{
options.Labels.Add("Production"); // overrides unlabelled defaults
options.IncludeFeatureFlags = true; // exposed under FeatureManagement:*
options.SentinelKey = "Sentinel"; // cheap change detection when reloading
})
.Required("Database:ConnectionString") // may be a Key Vault reference; resolved automatically
.BuildAsync();
See docs/app-configuration-source.md for all options.
Identity & Secret Redaction
Choose one identity for every Azure source in the pipeline (in any order relative to the sources), and keep secrets out of logs:
builder.Configuration.AddFluentAzure(fluent => fluent
.UseManagedIdentity() // or UseWorkloadIdentity() / UseCredential(...)
.FromAppConfiguration("https://myconfig.azconfig.io")
.FromKeyVault("https://myvault.vault.azure.net")
.Sensitive("Jwt:SigningKey")); // mark secrets that come from other sources
logger.LogDebug("{Config}", builder.Configuration.GetRedactedDebugView()); // Key Vault values appear as ***
A per-source credential (KeyVaultConfiguration.Credential, AppConfigurationOptions.Credential) still takes precedence. Binding and conversion errors never include configuration values.
Observability: OpenTelemetry & Health Checks
FluentAzure emits traces and metrics through System.Diagnostics (no extra dependency), ready for OpenTelemetry:
builder.Services.AddOpenTelemetry()
.WithTracing(t => t.AddSource(FluentAzureDiagnostics.ActivitySourceName))
.WithMetrics(m => m.AddMeter(FluentAzureDiagnostics.MeterName));
builder.Services.AddHealthChecks().AddFluentAzure(tags: new[] { "ready" });
| Signal | Name | Tags |
|---|---|---|
| Span | FluentAzure.Build, with a child FluentAzure.LoadSource per source |
fluentazure.source, fluentazure.outcome, fluentazure.reload, key/error counts |
| Histogram (s) | fluentazure.build.duration, fluentazure.source.load.duration |
fluentazure.source, fluentazure.outcome, fluentazure.reload |
| Counter | fluentazure.provider.reloads |
fluentazure.outcome = changed / unchanged / failure |
Telemetry and health data contain source names, outcomes, durations, counts and timestamps only β never configuration values or error messages.
The health check does not call Azure. It reports what the configuration providers last observed:
- Healthy: every load succeeded.
- Degraded: the last reload failed, so the last good values are still being served.
- Unhealthy (or your
failureStatus): no FluentAzure provider is registered, or values are older thanstaleAfter(by default, three reload intervals).
Trimming & Native AOT
FluentAzure targets .NET 8 and .NET 10 and is annotated for trimming and Native AOT. The pipeline, all sources (environment, JSON, Key Vault, App Configuration) and the IConfiguration provider are AOT-safe. The reflection-based binders (BuildAsync<T>(), Bind<T>(), AddFluentAzure<T>(), AddFluentAzureOptions<T>()) are marked [RequiresUnreferencedCode]/[RequiresDynamicCode], so the compiler warns if you use them in a trimmed or AOT app. In those apps, feed IConfiguration and bind with the configuration binding source generator:
<PublishAot>true</PublishAot>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
IConfigurationRoot configuration = new ConfigurationBuilder()
.AddFluentAzure(fluent => fluent.UseManagedIdentity().FromKeyVault(vaultUrl).Required("App:Name"))
.Build();
services.AddOptions<AppOptions>().Bind(configuration.GetSection("App")).ValidateOnStart(); // source-generated
See examples/Aot.Example. CI publishes it with Native AOT and fails on any trimming or AOT warning.
Analyzers
The package includes compile-time analyzers (docs/analyzers.md):
- FAZ0001: a
Required("key")that theBuildAsync<T>()type never binds (usually a typo). - FAZ0002: a Key Vault or App Configuration endpoint that isn't HTTPS, including in Microsoft's
AddAzureKeyVault/AddAzureAppConfigurationand the Azure SDK clients. - FAZ0003: a hard-coded App Configuration connection string containing a secret, in the same APIs.
- FAZ0004:
GetDebugView(), which prints secrets; useGetRedactedDebugView()instead.
Web API with FluentAzure's provider
// Program.cs
using FluentAzure;
var builder = WebApplication.CreateBuilder(args);
await builder.Configuration.AddFluentAzureAsync(fluent => fluent
.UseManagedIdentity()
.FromKeyVault(builder.Configuration["KeyVault:Url"]!)
.Required("ConnectionStrings:DefaultConnection")
.Required("Jwt:SecretKey"));
builder.Services.AddFluentAzureOptions<JwtOptions>(builder.Configuration, "Jwt");
builder.Services.AddHealthChecks().AddFluentAzure();
var app = builder.Build();
app.MapHealthChecks("/health");
A missing required key stops startup with a FluentAzureConfigurationException that lists every problem. Values are available through IConfiguration, IOptions<JwtOptions> and builder.Configuration.GetConnectionString("DefaultConnection").
π Getting Started
Prerequisites
- .NET 8 or .NET 10.
- For Azure sources: an identity with access to your Key Vault and/or App Configuration store. No Azure subscription is needed for environment, JSON or in-memory sources. For local work, the App Configuration emulator runs in Docker.
Quick Start
- Install the package:
dotnet add package FluentAzure --prerelease. - Add
using FluentAzure;. - Choose your starting point from Where to Start.
- Handle results with
Match, or let the provider fail fast at startup.
Features
- Configuration guard: startup validation, a health check and secret redaction for configuration from any provider, including Microsoft's App Configuration and Key Vault providers.
- Fluent pipeline: chain sources,
Required,Optional,TransformandValidate, and get aResultwith every error, not just the first. - Sources: environment variables, JSON files, in-memory values, Azure Key Vault, and Azure App Configuration (labels, snapshots, Key Vault references, feature flags).
IConfigurationprovider: works with the options pattern, reloads intoIOptionsMonitor<T>, and keeps the last good values when a reload fails.- Identity: one managed/workload identity for every Azure source.
- Secret safety: secrets are tracked and redacted, and never appear in errors, telemetry or health data.
- Observability: OpenTelemetry traces and metrics, and a health check.
- Analyzers: compile-time checks for key typos, insecure endpoints, hard-coded secrets and unredacted debug output, in FluentAzure's APIs, Microsoft's providers and the Azure SDK clients.
- Platforms: .NET 8 and .NET 10, trimming and Native AOT compatible.
π Documentation
| Topic | Page |
|---|---|
| Configuration guard: startup validation, health check and redaction for any provider | docs/configuration-guard.md |
IConfiguration provider, reload, options, AOT |
docs/configuration-integration.md |
| Azure App Configuration | docs/app-configuration-source.md |
| Azure Key Vault | docs/key-vault-configuration-source.md |
| Identity & secret redaction | docs/identity-and-redaction.md |
| OpenTelemetry & health checks | docs/observability.md |
| Binding to typed objects | docs/enhanced-configuration-binding.md |
| Analyzers FAZ0001βFAZ0004 | docs/analyzers.md |
| Changelog & upgrading | CHANGELOG.md, docs/upgrade-guide.md |
| Examples | examples/ |
| Security | SECURITY.md |
π€ Contributing
We welcome contributions! Please see our Contributing Guide for details on:
- Code style and standards
- Testing requirements
- Pull request process
- Development setup
π Resources
Ready to revolutionize Azure configuration management? Let's build something awesome! π
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Azure.Data.AppConfiguration (>= 1.11.1)
- Azure.Identity (>= 1.21.0)
- Azure.Security.KeyVault.Secrets (>= 4.11.1)
- Microsoft.Extensions.Configuration (>= 10.0.12)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.12)
- Microsoft.Extensions.DependencyInjection (>= 10.0.12)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.12)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.12)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.12)
- Microsoft.Extensions.Options.DataAnnotations (>= 10.0.12)
- Polly (>= 8.8.0)
-
net8.0
- Azure.Data.AppConfiguration (>= 1.11.1)
- Azure.Identity (>= 1.21.0)
- Azure.Security.KeyVault.Secrets (>= 4.11.1)
- Microsoft.Extensions.Configuration (>= 10.0.12)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.12)
- Microsoft.Extensions.DependencyInjection (>= 10.0.12)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.12)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.12)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.12)
- Microsoft.Extensions.Options.DataAnnotations (>= 10.0.12)
- Polly (>= 8.8.0)
- System.Text.Json (>= 10.0.12)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on FluentAzure:
| Package | Downloads |
|---|---|
|
AzureFunctions.Example
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.3.0-rc.1 | 66 | 10/6/2026 |
| 0.2.0-rc.5 | 145 | 7/11/2025 |
| 0.2.0-rc.4 | 120 | 7/11/2025 |
| 0.2.0-rc.3 | 115 | 7/11/2025 |
| 0.2.0-rc.1 | 169 | 7/8/2025 |
| 0.1.0-alpha.10 | 168 | 7/8/2025 |
| 0.1.0-alpha.9 | 382 | 7/8/2025 |
| 0.1.0-alpha.8 | 168 | 7/8/2025 |
| 0.1.0-alpha.6 | 176 | 7/8/2025 |
| 0.1.0-alpha.5 | 379 | 7/8/2025 |
| 0.1.0-alpha.4 | 171 | 7/8/2025 |
| 0.1.0-alpha.3 | 367 | 7/8/2025 |
| 0.1.0-alpha.2 | 383 | 7/8/2025 |
See https://github.com/tonyjoanes/FluentAzure/releases for release notes.