Skip to content

Default dashboard endpoints when config is missing - #17371

Closed
Adam Ratzman (adamint) wants to merge 1 commit into
microsoft:mainfrom
adamint:copilot/dashboard-config-defaults
Closed

Default dashboard endpoints when config is missing#17371
Adam Ratzman (adamint) wants to merge 1 commit into
microsoft:mainfrom
adamint:copilot/dashboard-config-defaults

Conversation

@adamint

Copy link
Copy Markdown
Member

Description

Fixes #9999

Aspire dashboard startup should not require aspire.config.json or launch profile environment variables to provide dashboard and OTLP endpoint URLs. Today, custom or incomplete profiles can fail validation before the dashboard has a chance to use runtime endpoint allocation.

This change lets missing dashboard URL, OTLP endpoint, and resource service endpoint configuration fall back to dynamic runtime defaults instead of mutating existing config files. Explicitly configured values are still validated, including malformed URLs and insecure HTTP values unless unsecured transport is allowed.

User-facing usage

Users can run an AppHost even when the profile/config does not include dashboard endpoint environment variables such as:

{
  "ASPNETCORE_URLS": "https://localhost:<port>;http://localhost:<port>",
  "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:<port>",
  "ASPIRE_DASHBOARD_RESOURCE_SERVER_ENDPOINT_URL": "https://localhost:<port>"
}

When those values are absent, Aspire Hosting now creates dashboard and OTLP endpoints dynamically and uses the allocated endpoint values for the dashboard environment at runtime.

Validation:

  • dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-launch-profile -- --filter-class "*.DashboardResourceTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
  • dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-launch-profile -- --filter-class "*.TransportOptionsValidatorTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
  • dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-build --no-launch-profile -- --filter-class "*.DashboardResourceTests" --filter-class "*.TransportOptionsValidatorTests" --filter-class "*.DashboardOptionsTests" --filter-class "*.DashboardEventHandlersTests" --filter-class "*.WithOtlpExporterTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
  • dotnet build tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-restore /p:SkipNativeBuild=true

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

@github-actions

github-actions Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

🚀 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/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17371

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17371"

Copilot AI left a comment

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.

Pull request overview

Restores the Aspire Hosting dashboard’s ability to start when aspire.config.json / launch profile endpoint configuration is missing, by falling back to dynamically allocated runtime endpoints and relaxing the relevant validation. The PR also includes a sizable Aspire CLI change around update-notification banners (including JSON-output suppression) and adds VS Code task dependencies to ensure the extension’s packages are installed before watch tasks run.

Changes:

  • Hosting: Allow missing dashboard/OTLP/resource-service endpoint configuration to succeed validation and rely on runtime endpoint allocation.
  • Hosting tests: Add/adjust tests to cover “missing/empty config uses dynamic defaults” scenarios.
  • CLI/DevEx: Rework update-notification defaults and suppress banners for JSON output; add yarn install prerequisite tasks for extension watch.

Reviewed changes

Copilot reviewed 46 out of 46 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/Aspire.Hosting.Tests/Dashboard/TransportOptionsValidatorTests.cs Updates validation expectations so missing/empty endpoint config succeeds.
tests/Aspire.Hosting.Tests/Dashboard/DashboardResourceTests.cs Adds coverage for dynamic default endpoint allocation when config is absent/partial.
tests/Aspire.Cli.Tests/Commands/BaseCommandTests.cs Adds test coverage for update notification behavior under JSON/non-JSON invocations.
src/Aspire.Hosting/Dashboard/TransportOptionsValidator.cs Relaxes validation so missing dashboard-related endpoint config can fall back to runtime defaults.
src/Aspire.Hosting/Dashboard/DashboardOptions.cs Removes options validation that previously required dashboard/OTLP URLs.
src/Aspire.Hosting/Dashboard/DashboardEventHandlers.cs Creates default endpoints when missing and wires endpoint annotations for runtime allocation.
src/Aspire.Cli/Commands/BaseCommand.cs Changes update-notification gating behavior and adds JSON-based suppression.
src/Aspire.Cli/Commands/AddCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/InitCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/NewCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/LsCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/PsCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/StartCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/StopCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/WaitCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/RestoreCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/ResourceCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/LogsCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/DescribeCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/ExportCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/DashboardRunCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/SetupCommand.cs Opts command into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/PipelineCommandBase.cs Opts pipeline commands into update notifications under new BaseCommand default behavior.
src/Aspire.Cli/Commands/UpdateCommand.cs Relies on new BaseCommand default to avoid update notifications for the update command.
src/Aspire.Cli/Commands/RenderCommand.cs Relies on new BaseCommand default to avoid update notifications.
src/Aspire.Cli/Commands/ParentCommand.cs Relies on new BaseCommand default to avoid update notifications for parent/help-style commands.
src/Aspire.Cli/Commands/ApiGetCommand.cs Relies on new BaseCommand default to avoid update notifications.
src/Aspire.Cli/Commands/ApiListCommand.cs Relies on new BaseCommand default to avoid update notifications.
src/Aspire.Cli/Commands/ApiSearchCommand.cs Relies on new BaseCommand default to avoid update notifications.
src/Aspire.Cli/Commands/DocsGetCommand.cs Relies on new BaseCommand default to avoid update notifications.
src/Aspire.Cli/Commands/DocsListCommand.cs Relies on new BaseCommand default to avoid update notifications.
src/Aspire.Cli/Commands/DocsSearchCommand.cs Relies on new BaseCommand default to avoid update notifications.
src/Aspire.Cli/Commands/IntegrationSearchCommand.cs Relies on new BaseCommand default to avoid update notifications.
src/Aspire.Cli/Commands/ConfigCommand.cs Relies on new BaseCommand default to avoid update notifications for config subcommands.
src/Aspire.Cli/Commands/ExtensionInternalCommand.cs Relies on new BaseCommand default to avoid update notifications for internal/extension commands.
src/Aspire.Cli/Commands/McpStartCommand.cs Relies on new BaseCommand default to avoid update notifications for MCP commands.
src/Aspire.Cli/Commands/McpInitCommand.cs Relies on new BaseCommand default to avoid update notifications for MCP commands.
src/Aspire.Cli/Commands/AgentMcpCommand.cs Relies on new BaseCommand default to avoid update notifications for MCP server command.
src/Aspire.Cli/Commands/AgentInitCommand.cs Relies on new BaseCommand default to avoid update notifications for agent init command.
src/Aspire.Cli/Commands/Sdk/SdkDumpCommand.cs Relies on new BaseCommand default to avoid update notifications for SDK dump.
src/Aspire.Cli/Commands/DoctorCommand.cs Relies on new BaseCommand default to avoid update notifications (per existing rationale).
src/Aspire.Cli/Commands/CertificatesTrustCommand.cs Relies on new BaseCommand default to avoid update notifications.
src/Aspire.Cli/Commands/CertificatesCleanCommand.cs Relies on new BaseCommand default to avoid update notifications.
src/Aspire.Cli/Commands/CacheCommand.cs Relies on new BaseCommand default to avoid update notifications.
extension/.vscode/tasks.json Ensures yarn install runs before extension watch task.
.vscode/tasks.json Ensures yarn install runs before extension watch task when running from repo root.

Comment thread src/Aspire.Hosting/Dashboard/DashboardEventHandlers.cs
Comment thread src/Aspire.Hosting/Dashboard/DashboardEventHandlers.cs
Comment thread src/Aspire.Cli/Commands/BaseCommand.cs Outdated
Comment thread src/Aspire.Cli/Commands/BaseCommand.cs
Comment thread src/Aspire.Cli/Commands/BaseCommand.cs
@adamint

Copy link
Copy Markdown
Member Author

PR Testing Complete

Result: ✅ Verified with the PR dogfood CLI.

  • Head commit tested: 317d5f51e0fbb0f0c73ae4ed23ac3d07b8c6fbe3
  • Installed CLI: 13.4.0-pr.17371.g317d5f51 ✅ matched the PR head commit
  • Mode: local temp-directory testing with the PR package hive
Scenario Status Notes
Basic CLI smoke ✅ Passed Created a fresh C# empty AppHost from the PR hive using aspire new aspire-empty --language csharp --localhost-tld false --suppress-agent-init --non-interactive.
Dashboard missing-config defaults ✅ Passed Removed applicationUrl, dashboard OTLP endpoint vars, and resource-service endpoint vars from both generated config files; aspire start succeeded, dashboard was healthy, and dynamic https, http, and otlp-grpc endpoints were allocated.

Evidence from the dashboard-defaults scenario:

  • aspire start returned dashboard URL https://localhost:57594/login?t=....
  • aspire ps --format Json --resources showed the dashboard resource running and healthy.
  • Dynamic endpoints were present for otlp-grpc, http, and https.
  • The AppHost log included Now listening on: https://localhost:57594 and did not hit the previous missing-config validation failures.

Notes:

  • Dogfood artifacts were not available on the first attempt, so I retried after the PR workflow published them.
  • The first local workspace used a long macOS temp path and hit the platform Unix-domain socket length limit; reinstalling into /tmp/apr17371 resolved that harness issue.
  • The PR diff currently includes CLI/VS Code files in addition to the dashboard hosting changes, so I included the basic CLI smoke scenario.

@JamesNK

Copy link
Copy Markdown
Member

Adam Ratzman (@adamint) Rebase the PR to remove commits from other PRs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@adamint
Adam Ratzman (adamint) force-pushed the copilot/dashboard-config-defaults branch from d666175 to 4ca5ef3 Compare May 22, 2026 01:40

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Design difference from #16046: PR #16046 uses AllowUnsecuredTransport to decide whether dynamic endpoints use http or https. This PR instead infers the OTLP scheme from the configured dashboard URLs via GetDefaultOtlpScheme (https if any https URL present, http otherwise). The two approaches conflict — worth aligning on one strategy if both are under consideration.

Main observations:

  1. ValidateDashboardOptions is now dead code (always returns Success) — consider removing
  2. Only OTLP gRPC is created in the dynamic defaults case (no HTTP) — browser telemetry won't work without explicit config
  3. Minor normalization inconsistency for edge case of empty-string config values

Comment on lines 45 to 50
{
public ValidateOptionsResult Validate(string? name, DashboardOptions options)
{
var builder = new ValidateOptionsResultBuilder();

if (string.IsNullOrEmpty(options.DashboardUrl))
{
builder.AddError($"Failed to configure dashboard resource because {KnownConfigNames.AspNetCoreUrls} environment variable was not set.");
}

if (string.IsNullOrEmpty(options.OtlpGrpcEndpointUrl) && string.IsNullOrEmpty(options.OtlpHttpEndpointUrl))
{
builder.AddError($"Failed to configure dashboard resource because {KnownConfigNames.DashboardOtlpGrpcEndpointUrl} and {KnownConfigNames.DashboardOtlpHttpEndpointUrl} environment variables are not set. At least one OTLP endpoint must be provided.");
}

return builder.Build();
return ValidateOptionsResult.Success;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ValidateDashboardOptions is now dead code — it unconditionally returns ValidateOptionsResult.Success. Consider removing the class (and its DI registration) entirely, or adding a comment explaining why the shell is retained.

Comment on lines +427 to +429
if (otlpGrpcEndpointUrl is null && otlpHttpEndpointUrl is null)
{
dashboardResource.Annotations.Add(CreateEndpoint(KnownEndpointNames.OtlpGrpcEndpointName, uriScheme: GetDefaultOtlpScheme(dashboardUrls), transport: "http2"));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

When both OTLP endpoints are unconfigured, only a gRPC endpoint is created — no HTTP OTLP endpoint. This means browser-based telemetry (which requires HTTP OTLP) won't work with dynamic defaults. Consider adding a comment explaining why only gRPC is defaulted, so future readers don't assume it's an oversight.

Also noting a design difference vs #16046: that PR uses AllowUnsecuredTransport to decide whether dynamic endpoints use http or https. This PR instead infers the OTLP scheme from the configured dashboard URLs (https if any https URL present, http otherwise) via GetDefaultOtlpScheme. The two approaches would conflict if both were merged.

Comment on lines +460 to +461
static string? NormalizeConfiguredUrl(string? url) =>
string.IsNullOrWhiteSpace(url) ? null : url;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor: ConfigureAspireDashboardResource normalizes options.OtlpHttpEndpointUrl to null when empty via NormalizeConfiguredUrl, so no OTLP HTTP endpoint is created. But ConfigureEnvironmentVariables (line ~594) reads options.OtlpHttpEndpointUrl directly — if someone sets ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL="", the empty string passes the != null check and enters the CORS block despite no HTTP OTLP endpoint existing. Previously, validation would have rejected this case. Consider using !string.IsNullOrWhiteSpace(options.OtlpHttpEndpointUrl) in the CORS gate for consistency with the normalization here.

@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 13.4 milestone May 22, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Aspire AppHost/dashboard defaults no longer seem to apply

3 participants