You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Dashboard Add Server (AddServerDialog.xaml.cs) — read-only, no install capability
Users who want to monitor a new server must: run the installer separately, then open Dashboard, then add the server. Two apps for one task. Meanwhile, the GUI Installer duplicates all CLI logic instead of sharing it — any bug fix or new script requires changes in two places.
Proposal
Phase 1: Extract Installer.Core shared library
Primary extraction source:InstallerGui/Services/InstallationService.cs — already factored as a service class with IProgress<InstallationProgress>, CancellationToken on all async methods, and static methods. This is a much cleaner extraction base than Program.cs, which has ~600 lines of console-specific code mixed into the logic.
Core extraction:
Move connection testing, SQL batch splitting, script discovery/ordering, progress reporting, version detection, upgrade orchestration into a new Installer.Core class library
Move community dependency installation (sp_WhoIsActive, DarlingData, First Responder Kit — download with retry/backoff, batch split, execute)
Expose CancellationToken support throughout async methods so all consumers (CLI, Dashboard) can cancel long-running installs
Define result types that map cleanly to the CLI's 9 exit codes (0=Success through 8=UpgradesFailed) — CLI wrapper translates result to exit code, Dashboard translates to user-facing messages
Log a warning when an upgrade folder exists but has no upgrade.txt (currently silently skipped in GetApplicableUpgrades())
SQL script packaging — embedded resources with filesystem override:
Dashboard ships via Velopack and has no access to the install/ folder at runtime. To solve this, Installer.Core embeds all SQL scripts as assembly resources at build time:
A ScriptProvider abstraction lets consumers choose the source:
publicclassScriptProvider{// Dashboard uses this — scripts baked into the assemblypublicstaticScriptProviderFromEmbeddedResources();// CLI uses this — preserves current filesystem search behaviorpublicstaticScriptProviderFromDirectory(stringpath);}
This means:
Dashboard references Installer.Core and scripts just work — no path management, no missing files
Scripts are versioned with the assembly — Dashboard 2.5.0 always carries the 2.5.0 scripts
CLI workflow unchanged (edit script locally, run installer, iterate)
Offline installs work for Dashboard
Total embedded size is ~200-300 KB of SQL text — negligible
Logging abstraction:
CLI writes to console, GUI writes to RichTextBox, Dashboard has its own Logger. Installer.Core needs a logging interface beyond IProgress<InstallationProgress>:
IProgress<InstallationProgress> for structured progress (status, percentage, step counts)
Installation report generation (text file) stays in Installer.Core as an opt-in utility — consumers decide whether to call it
Error logging left to consumers (each has their own mechanism)
CLI Installer refactor:
CLI references Installer.Core and becomes a thin console wrapper
Uses ScriptProvider.FromDirectory() to preserve filesystem discovery
Maps InstallationResult to exit codes
Interactive password input, console coloring, help text stay in CLI
Phase 2: Integrate installation into Dashboard's Add Server
When user clicks "Test Connection" and the PerformanceMonitor database doesn't exist, show an "Install Now" option
When the database exists at an older version, show an "Upgrade" option (not just detection — full execution of applicable upgrade scripts)
Dashboard already collects server name, auth type, credentials, encryption settings — everything the installer needs
Force ReadOnlyIntent=false on the installation connection regardless of the server's Dashboard config (installation requires write access)
Installation runs async with progress bar, log output, and cancel button in the dialog
Write to config.installation_history on completion (same as both current installers)
Generate installation report (text file) on completion — same format as current installers
On success: database is installed/upgraded AND server is added to Dashboard in one step
Options for clean install, reset schedule, validation exposed as checkboxes (collapsed by default, advanced section)
Phase 3: Retire InstallerGui
Remove InstallerGui/ project from solution
Remove from build pipeline and release artifacts
Update SignPath config (remove InstallerGui from signing, Installers artifact slug now covers CLI only)
Update docs to point users at Dashboard for GUI installs, CLI for automation
Credential separation
Installer credentials and monitoring credentials should be treated as distinct:
Installer credentials need elevated permissions (sa / sysadmin) to create the database, tables, stored procedures, Agent jobs, and Extended Events sessions. These are used once during setup and should not be persisted.
Monitoring credentials only need read access to the PerformanceMonitor database for ongoing Dashboard use. These are what get stored in Windows Credential Manager.
The Add Server flow should handle this cleanly: prompt for installer credentials during the install step (used transiently, not saved), then prompt for or default to lower-privilege monitoring credentials that get stored for Dashboard connections. This avoids leaving sa credentials in Credential Manager and follows least-privilege.
What stays
CLI Installer — essential for automation, headless servers, scripting across many servers. Refactored to use Installer.Core.
All SQL scripts (install/) — unchanged.
Task checklist
Phase 1: Installer.Core
Create Installer.Core class library project, add to solution
Implement ScriptProvider with embedded resources and filesystem override
Embed all install/**/*.sql and upgrades/**/* as assembly resources
Extract shared logic from InstallationService.cs: connection testing, batch splitting, script discovery, progress reporting, version detection, upgrade orchestration
Extract community dependency installation (download, retry/backoff, execute)
Problem
Three separate entry points for server setup share zero code:
Installer/Program.cs— 2,122 lines) — standalone console appInstallerGui/Services/InstallationService.cs— 1,552 lines) — completely duplicates CLI logicAddServerDialog.xaml.cs) — read-only, no install capabilityUsers who want to monitor a new server must: run the installer separately, then open Dashboard, then add the server. Two apps for one task. Meanwhile, the GUI Installer duplicates all CLI logic instead of sharing it — any bug fix or new script requires changes in two places.
Proposal
Phase 1: Extract
Installer.Coreshared libraryPrimary extraction source:
InstallerGui/Services/InstallationService.cs— already factored as a service class withIProgress<InstallationProgress>,CancellationTokenon all async methods, and static methods. This is a much cleaner extraction base thanProgram.cs, which has ~600 lines of console-specific code mixed into the logic.Core extraction:
Installer.Coreclass libraryServerInfo,InstallationProgress,InstallationResultCancellationTokensupport throughout async methods so all consumers (CLI, Dashboard) can cancel long-running installsupgrade.txt(currently silently skipped inGetApplicableUpgrades())SQL script packaging — embedded resources with filesystem override:
Dashboard ships via Velopack and has no access to the
install/folder at runtime. To solve this, Installer.Core embeds all SQL scripts as assembly resources at build time:A
ScriptProviderabstraction lets consumers choose the source:This means:
Logging abstraction:
CLI writes to console, GUI writes to RichTextBox, Dashboard has its own Logger. Installer.Core needs a logging interface beyond
IProgress<InstallationProgress>:IProgress<InstallationProgress>for structured progress (status, percentage, step counts)CLI Installer refactor:
Installer.Coreand becomes a thin console wrapperScriptProvider.FromDirectory()to preserve filesystem discoveryInstallationResultto exit codesPhase 2: Integrate installation into Dashboard's Add Server
ReadOnlyIntent=falseon the installation connection regardless of the server's Dashboard config (installation requires write access)config.installation_historyon completion (same as both current installers)Phase 3: Retire
InstallerGuiInstallerGui/project from solutionCredential separation
Installer credentials and monitoring credentials should be treated as distinct:
The Add Server flow should handle this cleanly: prompt for installer credentials during the install step (used transiently, not saved), then prompt for or default to lower-privilege monitoring credentials that get stored for Dashboard connections. This avoids leaving sa credentials in Credential Manager and follows least-privilege.
What stays
Installer.Core.install/) — unchanged.Task checklist
Phase 1: Installer.Core
Installer.Coreclass library project, add to solutionScriptProviderwith embedded resources and filesystem overrideinstall/**/*.sqlandupgrades/**/*as assembly resourcesInstallationService.cs: connection testing, batch splitting, script discovery, progress reporting, version detection, upgrade orchestrationServerInfo,InstallationProgress,InstallationResultCancellationTokenon all async install/upgrade methodsupgrade.txtis missingInstaller.Core(thin console wrapper usingScriptProvider.FromDirectory())Installer.TestsagainstInstaller.Corepublic API (VersionDetection, UpgradeOrdering, FileFiltering, Idempotency, Adversarial tests)Phase 2: Dashboard integration
Installer.Corereference to DashboardAddServerDialogto detect missing PerformanceMonitor database on Test ConnectionReadOnlyIntent=falseon installation connectionCredentialServicefor monitoring credentials onlyconfig.installation_historyafter install/upgrade from DashboardPhase 3: Retire InstallerGui
InstallerGuiproject from solution and build pipelineFiles involved
Installer.Core/— new class library (ScriptProvider, InstallationService, models, result types)Installer.Core/Installer.Core.csproj— embedded SQL resources, Microsoft.Data.SqlClient dependencyInstaller/Program.cs— refactor to thin wrapper over Installer.CoreInstallerGui/Services/InstallationService.cs— primary extraction source, then delete projectInstaller.Tests/— retarget tests against Installer.CoreDashboard/AddServerDialog.xaml.cs— add install/upgrade detection + UIDashboard/Services/CredentialService.cs— reuse for monitoring credentialsinstall/— SQL scripts (unchanged, embedded into Installer.Core)upgrades/— upgrade scripts (unchanged, embedded into Installer.Core)Not in scope