Skip to content

Add Bitwarden Secrets Manager hosting and client integrations#1329

Open
sliekens wants to merge 92 commits into
CommunityToolkit:mainfrom
sliekens:bitwarden-secret-manager-integration
Open

Add Bitwarden Secrets Manager hosting and client integrations#1329
sliekens wants to merge 92 commits into
CommunityToolkit:mainfrom
sliekens:bitwarden-secret-manager-integration

Conversation

@sliekens
Copy link
Copy Markdown
Contributor

@sliekens sliekens commented May 19, 2026

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

  • Add a non-container AppHost resource for provisioning and managing a Bitwarden project, with managed secret definitions attached to it.
  • Add a companion client integration that registers authenticated Bitwarden clients for consuming services.
  • Support adopting existing Bitwarden projects or secrets when a greenfield setup is not appropriate.
  • Add sample usage, unit tests, and the necessary solution, documentation, and CI wiring for the new integrations.

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 build
  • dotnet test tests/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.Tests/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.Tests.csproj --no-build
  • dotnet test tests/CommunityToolkit.Aspire.Bitwarden.SecretManager.Tests/CommunityToolkit.Aspire.Bitwarden.SecretManager.Tests.csproj --no-build

Hosting-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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/CommunityToolkit/Aspire/main/eng/scripts/dogfood-pr.sh | bash -s -- 1329

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/CommunityToolkit/Aspire/main/eng/scripts/dogfood-pr.ps1) } 1329"

@sliekens sliekens changed the title Add Bitwarden Secrets Manager integrations Add Bitwarden Secrets Manager hosting and client integrations May 19, 2026
@sliekens sliekens marked this pull request as ready for review May 19, 2026 23:15
Copilot AI review requested due to automatic review settings May 19, 2026 23:15
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 via WithReference(...).
  • 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.Resource without validating projectName for null, which can lead to NullReferenceException for invalid caller input. Add ArgumentNullException.ThrowIfNull(projectName); to match the validation used for other IResourceBuilder<> 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 AddSecret overload (ReferenceExpression) also doesn't validate builder for null before using it, which can produce NullReferenceException for invalid caller input. Add ArgumentNullException.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);
    }

@sliekens sliekens marked this pull request as draft May 20, 2026 18:21
@sliekens sliekens marked this pull request as ready for review May 20, 2026 19:39
@sliekens sliekens marked this pull request as draft May 21, 2026 21:46
@sliekens sliekens marked this pull request as ready for review May 21, 2026 22:50
@sliekens sliekens marked this pull request as draft May 22, 2026 20:55
@sliekens
Copy link
Copy Markdown
Contributor Author

Back in draft, I'm not happy with the (lack of a real) deployment model.
New plan: rewrite secret provisioning as pipeline steps instead of in InitializeAsync.

@sliekens sliekens marked this pull request as ready for review May 31, 2026 18:14
@sliekens
Copy link
Copy Markdown
Contributor Author

sliekens commented May 31, 2026

This is ready for review , I'm happy with the current state of this branch.
The deployment story is pretty good now, centered around pipelines instead of the manifest.

Dogfooding
As an external contributor, I don't have a way to run the dogfooding workflow.

However, I did publish a preview version to my own nuget feed, which you can use for testing.

Project type Install command Usage instructions
AppHost dotnet add package CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager --version 13.3.1-preview.2 --source https://nuget.sliekens.dev/v3/index.json README.md
Client dotnet add package CommunityToolkit.Aspire.Bitwarden.SecretManager --version 13.3.1-preview.2 --source https://nuget.sliekens.dev/v3/index.json README.md

Version 13.3.1-preview.2 packages were built from commit 0758df7.

Copy link
Copy Markdown
Contributor

@Omnideth Omnideth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if Aaron's suggestion to me would apply to this and the ASPIRE-INTERNALS.md

#1155 (comment)

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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed so. My follow up here is:

Maybe we should think about standardizing a way to add LLM guidance in a given integration.

@sliekens sliekens force-pushed the bitwarden-secret-manager-integration branch from b687e42 to 9db24c3 Compare June 4, 2026 16:52
sliekens added 9 commits June 4, 2026 18:13
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())
@sliekens sliekens force-pushed the bitwarden-secret-manager-integration branch from b2732d7 to 69a2d18 Compare June 5, 2026 22:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants