microsoft / microsoft/aspire

Deploy-time provisioning: no compute-agnostic way for values produced by a running resource to flow into dependent resources' configuration

Open
#18,097 1 comment 0 reactions 0 assignees View on GitHub
area-deployment triage:bot-seen
Dominant language
C#
Stars
6.3k
Forks
991
Avg merge
2d 15h
Merged PRs (30d)
196

Description

### Is there an existing issue for this?

- [x] I have searched the existing issues

### Is your feature request related to a problem? Please describe the problem.

Many self-hostable services require **post-start bootstrap that produces values their consumers need**: the value does not exist until the service's container is running and an API call has been made against it. Examples:

- **GlitchTip / self-hosted Sentry** — create org/team/project, obtain the **DSN** that client apps need in their `Sentry:Dsn` config (my concrete use case, in a Community Toolkit integration).
- **Keycloak** — create a realm and clients, obtain **client secrets**.
- **Grafana** — create a service account, obtain an **API token**.
- **MinIO / RabbitMQ / Vault** — create users/buckets/vhosts, obtain **access keys / tokens**.

In **run mode** this composes beautifully with existing primitives: subscribe to `ResourceReadyEvent`, call the service's HTTP API in-process, set properties consumed by a lazily-evaluated `ConnectionStringExpression`, and have consumers use `WithReference(...)` + `WaitFor(...)`. The DSN/secret flows to consumers with no extra concepts.

In **publish/deploy mode** there appears to be no equivalent, regardless of compute environment:

1. Every publisher resolves all values at **artifact-generation time** — Docker Compose writes parameter values into `.env.{env}` during `prepare-{env}`; the Kubernetes publisher resolves parameters into Helm `values.yaml` during its prepare step. But the value I need only exists _after_ the compute deploy step has run and the service is up.
2. The pipeline never runs the app model, so `ResourceReadyEvent` never fires and connection string expressions are not lazily awaited per consumer as in run mode.
3. `WaitFor` translates to startup _ordering_ only (e.g. compose `depends_on`); it cannot carry a value.
4. There is no feedback path from "deployed and provisioned" back into dependent resources' configuration **within the same pipeline run**.

The closest workaround is compute-agnostic but takes two deploys: build the connection string from parameters, run a custom pipeline step after deploy that provisions the service over HTTP and saves the values to `IDeploymentStateManager` under `Parameters:{name}`; deployment state is loaded into configuration at startup, so the values materialize on the **next** `aspire deploy`. Single-deploy convergence is only achievable with publisher-specific hacks (patching the generated `.env` file and invoking `ComposeUpAsync` a second time so changed services are recreated), which do not generalize to Kubernetes or other compute environments and depend on internals like step naming and artifact layout.

### Describe the solution you'd like

A first-class, compute-agnostic concept for **deploy-time provisioned values**: values that are declared at model-build time, produced by integration code at a defined point during deployment (after the producing resource's compute is up), and materialized into dependent resources' configuration by each publisher using its native late-binding mechanism.

Sketch of the shape this could take (names illustrative):

```csharp
// Declared at model-build time; carries no value yet.
var dsn = builder.AddDeferredValue("glitchtip-dsn", secret: true);

var glitchtip = builder.AddGlitchTip("glitchtip")
.WithProvisioner(async (ctx, ct) =>
{
// Runs during deploy, after this resource's compute environment reports it deployed.
// ctx exposes a resolved, reachable endpoint for the resource.
var result = await ProvisionAsync(ctx.Endpoint, ct);
dsn.SetValue(result.Dsn);
});

// Consumers reference the deferred value like a parameter; each publisher
// materializes it with its native late-binding mechanism.
builder.AddProject("api")
.WithEnvironment("SENTRY__DSN", dsn)
.WaitFor(glitchtip);
```

(In the real integration, `AddGlitchTip` would fold the deferred value into its `ConnectionStringExpression` so consumers just call `WithReference(glitchtip)`; the explicit `WithEnvironment` above shows the producer→consumer flow end to end.)

Key properties:

1. **Publisher-native materialization.** Docker Compose emits an env-file placeholder; Kubernetes emits a Secret; Azure targets emit their secret/config primitive. Integration authors never touch publisher artifacts.
2. **A defined pipeline phase** ("provision-after-compute", between the compute deploy steps and `Deploy`) where producer callbacks run, with a documented contract for how dependents receive the value in the same deploy — whether that's ordering dependent compute after producers, or a targeted reconcile/restart of dependents whose deferred inputs changed.
3. **Persistence semantics**: produced values are saved to deployment state automatically so subsequent deploys resolve them up front (today's parameter/state behavior, but as a supported contract rather than an emergent one).
4. **Run-mode parity**: in run mode the same provisioner callback could be driven by the existing eventing (`ResourceReadyEvent`), so one code path serves both modes.

Alternatives considered:

- **Two-deploy state pattern** (described above): works on every publisher today, but first-deploy UX is "deploy, then deploy again", and it relies on the undocumented `Parameters:{name}` state→configuration round-trip.
- **Artifact patching + re-running the deploy step**: single-deploy, but inherently publisher-specific and fragile (internal step names, artifact formats).
- **Provisioner job/sidecar containers** in the target network: solves _where the code runs_, but there is still no portable channel for getting the job's output into sibling resources' env, and it adds an image-publishing burden on integration authors.

### Additional context

Concrete motivating implementation: a GlitchTip hosting integration for the Aspire Community Toolkit (`CommunityToolkit.Aspire.Hosting.GlitchTip`). Run-mode provisioning (org/team/project + DSN via `ResourceReadyEvent`) works exactly as desired with `WaitFor`; deploy mode currently has no path that doesn't either bake in a specific compute environment or require a second deploy. Happy to share the full design analysis and to validate an API proposal against this use case.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.