-
Notifications
You must be signed in to change notification settings - Fork 35
Description
Problem
Three separate entry points for server setup share zero code:
- CLI Installer (
Installer/Program.cs— 2,122 lines) — standalone console app - GUI Installer (
InstallerGui/Services/InstallationService.cs— 1,552 lines) — completely duplicates CLI logic - 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.Coreclass library - Move community dependency installation (sp_WhoIsActive, DarlingData, First Responder Kit — download with retry/backoff, batch split, execute)
- Shared types:
ServerInfo,InstallationProgress,InstallationResult - Expose
CancellationTokensupport 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 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:
<!-- Installer.Core.csproj -->
<ItemGroup>
<EmbeddedResource Include="..\install\**\*.sql" LinkBase="Scripts\install" />
<EmbeddedResource Include="..\upgrades\**\*" LinkBase="Scripts\upgrades" />
</ItemGroup>A ScriptProvider abstraction lets consumers choose the source:
public class ScriptProvider
{
// Dashboard uses this — scripts baked into the assembly
public static ScriptProvider FromEmbeddedResources();
// CLI uses this — preserves current filesystem search behavior
public static ScriptProvider FromDirectory(string path);
}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.Coreand becomes a thin console wrapper - Uses
ScriptProvider.FromDirectory()to preserve filesystem discovery - Maps
InstallationResultto 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=falseon 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_historyon 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.Coreclass library project, add to solution - Implement
ScriptProviderwith embedded resources and filesystem override - Embed all
install/**/*.sqlandupgrades/**/*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)
- Extract shared models:
ServerInfo,InstallationProgress,InstallationResult - Define result types mapping to CLI's 9 exit codes (Success, ConnectionFailed, CriticalScriptFailed, etc.)
- Expose
CancellationTokenon all async install/upgrade methods - Add warning log when upgrade folder exists but
upgrade.txtis missing - Extract installation report generation as opt-in utility
- Refactor CLI Installer to reference
Installer.Core(thin console wrapper usingScriptProvider.FromDirectory()) - Retarget
Installer.TestsagainstInstaller.Corepublic API (VersionDetection, UpgradeOrdering, FileFiltering, Idempotency, Adversarial tests) - Verify CLI Installer works identically after refactor (all flags, auth modes, exit codes)
Phase 2: Dashboard integration
- Add
Installer.Corereference to Dashboard - Modify
AddServerDialogto detect missing PerformanceMonitor database on Test Connection - Detect existing database at older version and offer Upgrade
- Add inline installation UI (progress bar, log output, cancel button, advanced options)
- Force
ReadOnlyIntent=falseon installation connection - Separate installer credentials (transient, elevated) from monitoring credentials (stored, least-privilege)
- Wire up
CredentialServicefor monitoring credentials only - Write to
config.installation_historyafter install/upgrade from Dashboard - Generate installation report on completion
- Test full flow: new server -> detect no DB -> install with sa -> add with monitoring creds
- Test upgrade flow: existing server with older DB -> upgrade -> add/update in Dashboard
- Test cancellation mid-install
Phase 3: Retire InstallerGui
- Remove
InstallerGuiproject from solution and build pipeline - Update release artifact list (remove InstallerGui executable)
- Update SignPath signing config (remove InstallerGui artifact)
- Update README / docs
Files 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
- Lite (collects its own data, no server-side install needed)
- Changes to SQL install scripts themselves
- CLI Installer UX changes beyond refactoring to shared library
- Dashboard uninstall capability (CLI-only for now — can be added later if needed)