Add Bitwarden Secrets Manager hosting and client integrations#1329
Add Bitwarden Secrets Manager hosting and client integrations#1329sliekens wants to merge 92 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. |
|
This is ready for review , I'm happy with the current state of this branch. Dogfooding However, I did publish a preview version to my own nuget feed, which you can use for testing.
Version 13.3.1-preview.2 packages were built from commit 0758df7. |
There was a problem hiding this comment.
Leaving this having only looked at 36/39 files. I still have a few test classes I'm too tired to complete right now, but wanted to submit the feedback.
I want to note that I've not personally used BitWarden, so I am mostly looking at it from a meta perspective.
In general, nothing looked extremely out of place. I commented where I thought maybe Aaron nudged me on another PR.
| - Using eventing subscribers as the deployment integration point for publishing. | ||
| - Making runtime reconciliation the primary architectural concept. | ||
|
|
||
| The intended design is pipeline-step-first, declared-resource-first. |
There was a problem hiding this comment.
I wonder if Aaron's suggestion to me would apply to this and the ASPIRE-INTERNALS.md
These seem like detailed documentation rather than a quick start guide. I think the repo standard is a quick start guide in README.md and then any more detailed documentation would be PR'd to aspire.dev after merging to Main and the nuget package gets published.
| 1. Check whether `ASPIREATS001`, `ASPIREPIPELINES001`, `ASPIREPIPELINES002`, `ASPIREPIPELINES004`, `ASPIREINTERACTION001` have moved from `[Experimental]` to stable — if so, remove the corresponding `#pragma` suppressions. | ||
| 2. Run the AppHost and check for `MissingMethodException` / `MissingFieldException` from the `UnsafeAccessor` targets above. | ||
| 3. Run `aspire deploy` end-to-end and verify that (a) managed secrets are not prompted when they exist in Bitwarden, (b) reference secrets are not prompted, and (c) the "Parameters need values" banner disappears automatically in run mode. | ||
| 4. Check whether `DistributedApplicationBuilder.cs` still adds the deployment state file as a JSON configuration source (`AddJsonFile`), and whether the state file format produced by `FileDeploymentStateManager` is still compatible with the JSON configuration provider key format. |
There was a problem hiding this comment.
I said what I said on ARCHITECTURE.md, but also, this looks almost like agent guidance for future changes. Who is this documentation for? The developer in the chair using the hosting integration or the developer making updates to the hosting integration?
There was a problem hiding this comment.
This one is basically an apology to future me and any other maintainers for what you're about to see, along with some upgrade hints for when the Aspire internals eventually do change, so I would put it squarely in the "for contributors" box.
There was a problem hiding this comment.
I assumed so. My follow up here is:
Maybe we should think about standardizing a way to add LLM guidance in a given integration.
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
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.