Add Bitwarden Secrets Manager hosting and client integrations#1329
Add Bitwarden Secrets Manager hosting and client integrations#1329sliekens wants to merge 99 commits into
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/CommunityToolkit/Aspire/main/eng/scripts/dogfood-pr.sh | bash -s -- 1329Or
iex "& { $(irm https://raw.githubusercontent.com/CommunityToolkit/Aspire/main/eng/scripts/dogfood-pr.ps1) } 1329" |
There was a problem hiding this comment.
Pull request overview
This PR adds new Aspire Community Toolkit integrations for Bitwarden Secrets Manager: a hosting-side resource that reconciles a Bitwarden project + managed secrets during AppHost startup, and a client-side integration that registers authenticated BitwardenClient instances from structured Aspire configuration.
Changes:
- Introduces
BitwardenSecretManagerResource+ reconciler/state store to provision/adopt projects and create/adopt/update secrets, and to publish manifest + structured configuration viaWithReference(...). - Adds a client integration (
AddBitwardenSecretManagerClient/ keyed variant) with settings binding, validation, and optional health checks. - Adds tests, documentation, CI wiring, and a runnable example AppHost + consumer service.
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.Tests/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.Tests.csproj | Adds hosting integration test project. |
| tests/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.Tests/BitwardenSecretManagerReconcilerTests.cs | Tests reconciler behavior (create/adopt/update project + secrets) using a fake provider. |
| tests/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.Tests/BitwardenSecretManagerBuilderTests.cs | Tests builder/resource model behavior and WithReference environment injection. |
| tests/CommunityToolkit.Aspire.Bitwarden.SecretManager.Tests/CommunityToolkit.Aspire.Bitwarden.SecretManager.Tests.csproj | Adds client integration test project. |
| tests/CommunityToolkit.Aspire.Bitwarden.SecretManager.Tests/BitwardenSecretManagerClientPublicApiTests.cs | Verifies client extension methods validate inputs. |
| tests/CommunityToolkit.Aspire.Bitwarden.SecretManager.Tests/AspireBitwardenSecretManagerExtensionsTests.cs | Tests client settings binding + health check registration behavior. |
| src/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/README.md | Documents hosting integration usage and configuration. |
| src/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.csproj | Adds hosting package project + dependencies. |
| src/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/BitwardenSecretResource.cs | Adds managed secret resource type and value/expression behavior. |
| src/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/BitwardenSecretReference.cs | Adds public secret reference interface + internal reference implementation. |
| src/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/BitwardenSecretManagerResource.cs | Implements the Bitwarden project resource model and reference/env var support. |
| src/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/BitwardenSecretManagerReconciler.cs | Implements startup reconciliation + state persistence + provider abstraction. |
| src/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/BitwardenSecretManagerExtensions.cs | Adds AppHost builder extensions, manifest publishing, and initialization wiring. |
| src/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/AssemblyInfo.cs | Exposes internals to hosting tests. |
| src/CommunityToolkit.Aspire.Bitwarden.SecretManager/README.md | Documents client integration configuration and usage. |
| src/CommunityToolkit.Aspire.Bitwarden.SecretManager/CommunityToolkit.Aspire.Bitwarden.SecretManager.csproj | Adds client package project + dependencies. |
| src/CommunityToolkit.Aspire.Bitwarden.SecretManager/BitwardenSecretManagerHealthCheck.cs | Adds health check implementation for the client integration. |
| src/CommunityToolkit.Aspire.Bitwarden.SecretManager/BitwardenSecretManagerClientSettings.cs | Adds settings POCO for client configuration. |
| src/CommunityToolkit.Aspire.Bitwarden.SecretManager/AspireBitwardenSecretManagerExtensions.cs | Adds client registration extensions (keyed + unkeyed), binding, validation, and health checks. |
| README.md | Adds integration entries + NuGet/doc links for Bitwarden Secrets Manager. |
| examples/bitwarden-secret-manager/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.AppHost/Properties/launchSettings.json | Adds example AppHost launch profiles. |
| examples/bitwarden-secret-manager/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.AppHost/Program.cs | Adds example AppHost using the hosting integration + passing config to a consumer. |
| examples/bitwarden-secret-manager/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.AppHost/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.AppHost.csproj | Adds example AppHost project. |
| examples/bitwarden-secret-manager/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.AppHost/aspire.config.json | Adds aspire config pointing to the AppHost project. |
| examples/bitwarden-secret-manager/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.ApiService/Properties/launchSettings.json | Adds example consumer service launch profiles. |
| examples/bitwarden-secret-manager/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.ApiService/Program.cs | Adds example consumer service using the client integration. |
| examples/bitwarden-secret-manager/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.ApiService/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.ApiService.csproj | Adds example consumer service project. |
| Directory.Packages.props | Adds Bitwarden.Secrets.Sdk package version. |
| CommunityToolkit.Aspire.slnx | Adds new src/tests projects and example projects to the solution. |
| .github/workflows/tests.yaml | Wires new hosting + client test projects into CI. |
Comments suppressed due to low confidence (2)
src/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/BitwardenSecretManagerExtensions.cs:127
- This overload also dereferences
projectName.Resourcewithout validatingprojectNamefor null, which can lead toNullReferenceExceptionfor invalid caller input. AddArgumentNullException.ThrowIfNull(projectName);to match the validation used for otherIResourceBuilder<>parameters.
public static IResourceBuilder<BitwardenSecretManagerResource> AddBitwardenSecretManager(
this IDistributedApplicationBuilder builder,
[ResourceName] string name,
IResourceBuilder<ParameterResource> projectName,
IResourceBuilder<ParameterResource> organizationId,
IResourceBuilder<ParameterResource> accessToken)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentException.ThrowIfNullOrWhiteSpace(name);
ArgumentNullException.ThrowIfNull(organizationId);
ArgumentNullException.ThrowIfNull(accessToken);
return AddBitwardenSecretManagerCore(
builder,
name,
ConfiguredStringValue.FromParameter(projectName.Resource),
ConfiguredGuidValue.FromParameter(organizationId.Resource),
accessToken);
src/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/BitwardenSecretManagerExtensions.cs:275
- This
AddSecretoverload (ReferenceExpression) also doesn't validatebuilderfor null before using it, which can produceNullReferenceExceptionfor invalid caller input. AddArgumentNullException.ThrowIfNull(builder);to align with the rest of the public API surface.
/// <summary>
/// Adds a managed Bitwarden secret whose local and remote names are the same.
/// </summary>
/// <param name="builder">The parent Bitwarden resource builder.</param>
/// <param name="name">The Aspire resource name and Bitwarden secret name.</param>
/// <param name="value">The secret value expression.</param>
/// <returns>The managed secret resource builder.</returns>
public static IResourceBuilder<BitwardenSecretResource> AddSecret(
this IResourceBuilder<BitwardenSecretManagerResource> builder,
[ResourceName] string name,
ReferenceExpression value)
{
ArgumentNullException.ThrowIfNull(value);
return builder.AddSecret(name, name, value);
}
|
Back in draft, I'm not happy with the (lack of a real) deployment model. |
b687e42 to
9db24c3
Compare
Because I can't design BitwardenSecretResource as a ParameterResource while also preventing use of WithEnvironment, it makes more sense to design for WithEnvironment only. Old: WithBitwardenSecretValue(name, secret) New: WithEnvironment(name, secret) Old: WithBitwardenSecretId(name, secret) New: WithEnvironment(name, secret.AsSecretId())
b2732d7 to
69a2d18
Compare
|
I did a few more API simplifications, added coverage for edge cases (and fixed the failing ones), fixed an issue where config reloading in deploy mode caused weird prompting behavior, added deploy debug logging API changes
The last two changes forces usage into one of two modes of operation:
It allows a mix of scenarios
Preview packages were updated for Aspire 13.4, see #1329 (comment) |
…ing.Bitwarden.SecretManager.AppHost/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.AppHost.csproj
…mmunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.csproj
There was a problem hiding this comment.
can i get a bit of an explainer on this file as it looks to be doing some pretty hacky stuff with the TCS that it relies on static extern members.
There was a problem hiding this comment.
ok, this gives me a bit of an insight into the way the extension is working.
I am worried about the maintainability of the integration given the coupling to the internals of Aspire that have to have "hackily" accessed.
Summary
Add Bitwarden Secrets Manager hosting and client integrations for Aspire.
This targets Bitwarden Secrets Manager, the application and machine secret management product, rather than the Bitwarden Password Manager end-user vault. The AppHost can now provision and manage a Bitwarden project as a first-class resource, with managed secret definitions attached to it. It can also pass structured configuration to consuming services and adopt existing Bitwarden projects or secrets when needed.
Why
This removes the need for ad hoc Bitwarden bootstrap scripts and manual client wiring by letting the AppHost provision a Bitwarden project and its managed secrets and pass structured configuration to consuming services. It also supports adopting existing Bitwarden projects or secrets when a greenfield setup is not appropriate.
What changed
Design note
This follows a similar shape to Azure Key Vault in Aspire: the AppHost models Bitwarden Secrets Manager as an external managed secret store rather than as a local containerized dependency. Bitwarden Secrets Manager can be self-hosted, but doing so requires an enterprise server license, so a local self-hosted setup is not a practical default for many development teams.
Validation
dotnet builddotnet test tests/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.Tests/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.Tests.csproj --no-builddotnet test tests/CommunityToolkit.Aspire.Bitwarden.SecretManager.Tests/CommunityToolkit.Aspire.Bitwarden.SecretManager.Tests.csproj --no-buildHosting-side tests currently use a fake provider rather than a live Bitwarden tenant.
I manually tested this against a real Bitwarden instance using the example project included in this pull request.
Planning Docs
The original planning documents I gave to my agent:
You might notice there were some iterations on the original plan, so the PLAN document doesn't accurately reflect the current state of this pull request.
The final architecture is documented in ARCHITECTURE.md and has been expanded and kept up-to-date while iterating on the original design.