Skip to content

Warn when persistent containers lack user secrets configuration#14491

Open
sebastienros wants to merge 2 commits intorelease/13.2from
sebros/persistentdb
Open

Warn when persistent containers lack user secrets configuration#14491
sebastienros wants to merge 2 commits intorelease/13.2from
sebros/persistentdb

Conversation

@sebastienros
Copy link
Member

Summary

Fixes #14466

When a container resource has ContainerLifetime.Persistent but the AppHost project doesn't have user secrets configured (UserSecretsId), generated parameter values (such as passwords) cannot be persisted. On each restart, a new password is generated, which changes the container's environment variables, causing DCP to compute a different lifecycle key — triggering a full container recreation instead of reusing the persistent container.

This PR adds a BeforeStartEvent handler that detects this misconfiguration and logs a warning per affected persistent container, guiding users to run dotnet user-secrets init in the AppHost directory.

Root Cause Chain

  1. RunAsEmulator() (e.g., Service Bus) creates sub-resources with CreateDefaultPasswordParameter()
  2. CreateGeneratedParameter() wraps the default in UserSecretsParameterDefault
  3. UserSecretsParameterDefault.GetDefaultValue() calls TrySetSecret() → returns false silently via NoopUserSecretsManager
  4. Password value is generated fresh every restart (not read back from secrets)
  5. Container spec env vars change → DCP lifecycle key changes → container is recreated

Changes

File Change
BuiltInDistributedApplicationEventSubscriptionHandlers.cs New WarnPersistentContainersWithoutUserSecrets handler
DistributedApplicationBuilder.cs Register handler on BeforeStartEvent (run-mode only)
MessageStrings.resx + .Designer.cs Localizable warning message
PersistentContainerWarningTests.cs 3 tests covering warning/no-warning scenarios

Known Limitations

  1. Containers with persistent storage but session lifetime are not covered. Containers using WithDataVolume() or WithBindMount() without ContainerLifetime.Persistent also persist state (e.g., initialized databases retain old passwords), but won't receive this warning since they have ContainerLifetime.Session. This is a different problem scope worth a follow-up issue.

  2. False positives for persistent containers without generated secrets. The warning fires for any persistent container when user secrets aren't configured, even if the container doesn't use generated parameters (e.g., a Redis container with no password). This was a deliberate trade-off for simplicity — having user secrets configured is good practice for any project using persistent containers, and the warning is actionable.

Copilot AI review requested due to automatic review settings February 13, 2026 16:05
@github-actions
Copy link
Contributor

github-actions bot commented Feb 13, 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/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 14491

Or

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

Copy link
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 fixes issue #14466 where persistent containers are incorrectly recreated on every restart. The root cause is that when the AppHost project lacks user secrets configuration (UserSecretsId), generated parameter values (like passwords) cannot be persisted and are regenerated on each restart, causing DCP to compute a different lifecycle key and recreate the container.

Changes:

  • Adds a BeforeStartEvent handler that detects persistent containers when user secrets are not configured and logs a warning
  • Registers the warning handler only in run mode to guide developers to configure user secrets
  • Provides comprehensive test coverage for the warning behavior

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated no comments.

Show a summary per file
File Description
BuiltInDistributedApplicationEventSubscriptionHandlers.cs Adds WarnPersistentContainersWithoutUserSecrets handler that checks for persistent containers when user secrets are unavailable
DistributedApplicationBuilder.cs Registers the new warning handler in run mode
MessageStrings.resx Adds the warning message resource string
MessageStrings.Designer.cs Auto-generated designer code for the new message string
MessageStrings.*.xlf (13 files) Localization files updated with the new warning message (marked as needing translation)
PersistentContainerWarningTests.cs Comprehensive tests covering warning/no-warning scenarios for persistent vs session containers and with/without user secrets
Files not reviewed (1)
  • src/Aspire.Hosting/Resources/MessageStrings.Designer.cs: Language not supported

@github-actions
Copy link
Contributor

github-actions bot commented Feb 13, 2026

🎬 CLI E2E Test Recordings

The following terminal recordings are available for commit 64b7737:

Test Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
CreateAndDeployToDockerCompose ▶️ View Recording
CreateAndDeployToDockerComposeInteractive ▶️ View Recording
CreateAndPublishToKubernetes ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateEmptyAppHostProject ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateStartWaitAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
ResourcesCommandShowsRunningResources ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View Recording

📹 Recordings uploaded automatically from CI run #22002036620

{
if (resource.GetContainerLifetimeType() == ContainerLifetime.Persistent)
{
logger.LogWarning(MessageStrings.PersistentContainerWithoutUserSecrets, resource.Name);
Copy link
Member

Choose a reason for hiding this comment

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

@copilot check to ensure the warning log level is enabled before logging

Copy link
Contributor

Copilot AI commented Feb 13, 2026

@DamianEdwards I've opened a new pull request, #14496, to work on those changes. Once the pull request is ready, I'll request review from you.

* Initial plan

* Check warning log level before logging persistent container warning

Co-authored-by: DamianEdwards <249088+DamianEdwards@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: DamianEdwards <249088+DamianEdwards@users.noreply.github.com>
if (resource.GetContainerLifetimeType() == ContainerLifetime.Persistent)
{
logger.LogWarning(MessageStrings.PersistentContainerWithoutUserSecrets, resource.Name);
if (logger.IsEnabled(LogLevel.Warning))
Copy link
Member Author

Choose a reason for hiding this comment

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

@DamianEdwards FYI, there is an anlyzer in 10.0.103 (an update) that would warn when it's recommended. In this case it would trigger because it's a Warning and would not (in theory) happen in a hot path. It would for anything lower though. And the "expensive" thing in this code is the fact that it's calling params string[] so it's creating an array. Not obvious, right? c.f. dotnet/sdk#53030

Copy link
Member

@DamianEdwards DamianEdwards Feb 13, 2026

Choose a reason for hiding this comment

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

Not quite following. Is the recommended pattern as simple as

always guard LogXXX calls with an appropriate call to IsEnabled(LogLevel.XXX)

That's my mental model today.

Copy link
Member Author

Choose a reason for hiding this comment

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

What you did is totally fine, just saying that even the analyzer doesn't flag some uses because they really don't matter in non-hot paths.... e.g. when using LogWarning like here.

It's safe to just apply it everywhere and not have to think about it.

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.

3 participants