Skip to content
ConsoleMonster

Complete setup wizard

Walk through component choices, dependent server loading, review, installation progress and a result.

Demos Wizards Showcase

Use it when an installer needs editable options, a review step and visible progress.

  • Live
  • 112×44
  • PowerShell 7.4+
  • .NET 8.0+
Complete setup wizard 112×4424-bitFull

How it's built

The whole program in one file, with its sample data at the top. It drew the recording above; both languages draw the same cells.

PowerShellsetup.ps1View on GitHub
# A complete installer: location, components, service, connection, review and progress.
[CmdletBinding()]
param(
    [switch] $StaticSnapshot,
    [ValidateRange(0, 6)] [int] $Step = 0
)
Import-Module ConsoleMonster

# Data: invented installation inputs. The bounded driver only delays and updates widgets.
# No files, services, launch entries, network connections or persistent presets are created.
$installFolder = './toolbox-agent'
$dataFolder = './toolbox-data'
$components = @(
    [pscustomobject] @{ Id = 'Agent'; Name = 'Agent service'; Group = 'Core'; Size = '18 MB'; Description = 'Runs tasks and reports results.'; Selected = $true; Disabled = $null }
    [pscustomobject] @{ Id = 'Cli'; Name = 'Command-line tools'; Group = 'Core'; Size = '6 MB'; Description = 'The toolbox command and PowerShell module.'; Selected = $true; Disabled = $null }
    [pscustomobject] @{ Id = 'Html'; Name = 'HTML reports'; Group = 'Reporting'; Size = '4 MB'; Description = 'Interactive reports with tables and charts.'; Selected = $true; Disabled = $null }
    [pscustomobject] @{ Id = 'Pdf'; Name = 'PDF export'; Group = 'Reporting'; Size = '31 MB'; Description = 'Prints reports to PDF.'; Selected = $false; Disabled = $null }
    [pscustomobject] @{ Id = 'Chat'; Name = 'Chat notifications'; Group = 'Integrations'; Size = '1 MB'; Description = 'Posts run summaries to a channel.'; Selected = $false; Disabled = $null }
    [pscustomobject] @{ Id = 'Mail'; Name = 'Email notifications'; Group = 'Integrations'; Size = '1 MB'; Description = 'Sends reports by email.'; Selected = $false; Disabled = $null }
    [pscustomobject] @{ Id = 'Syslog'; Name = 'Syslog forwarder'; Group = 'Integrations'; Size = '1 MB'; Description = 'Forwards findings to a collector.'; Selected = $false; Disabled = 'needs the Enterprise licence' }
)
$accounts = @(
    [pscustomobject] @{ Id = 'CurrentUser'; Name = 'Current user'; Description = 'Runs with your signed-in identity.' }
    [pscustomobject] @{ Id = 'ServiceUser'; Name = 'Dedicated service identity'; Description = "Uses the platform's managed service identity." }
    [pscustomobject] @{ Id = 'Custom'; Name = 'A specific account'; Description = 'An account you name; password stays out of the command.' }
)
$regions = @(
    [pscustomobject] @{ Id = 'eu'; Name = 'Europe' }
    [pscustomobject] @{ Id = 'us'; Name = 'Americas' }
    [pscustomobject] @{ Id = 'ap'; Name = 'Asia Pacific' }
)
$endpoints = @(
    [pscustomobject] @{ Region = 'eu'; Host = 'eu-west.example.test'; City = 'Dublin'; Latency = '12 ms'; Disabled = $null }
    [pscustomobject] @{ Region = 'eu'; Host = 'eu-central.example.test'; City = 'Frankfurt'; Latency = '9 ms'; Disabled = $null }
    [pscustomobject] @{ Region = 'eu'; Host = 'eu-north.example.test'; City = 'Stockholm'; Latency = '31 ms'; Disabled = $null }
    [pscustomobject] @{ Region = 'us'; Host = 'us-east.example.test'; City = 'East'; Latency = '18 ms'; Disabled = $null }
    [pscustomobject] @{ Region = 'us'; Host = 'us-west.example.test'; City = 'West'; Latency = '74 ms'; Disabled = $null }
    [pscustomobject] @{ Region = 'ap'; Host = 'ap-south.example.test'; City = 'South'; Latency = '121 ms'; Disabled = $null }
    [pscustomobject] @{ Region = 'ap'; Host = 'ap-east.example.test'; City = 'East'; Latency = ''; Disabled = 'maintenance until 18:00' }
)
$pageNames = @('Location', 'Components', 'Service', 'Connection', 'Review', 'Install', 'Result')
$installSteps = @('Copy files', 'Register agent', 'Configure connection', 'Start agent')

# Layout: the native form owns validation, dependent choices, editors and review.
$form = New-ConsoleForm 'Install Toolbox Agent' -Command Install-ToolboxAgent -SubmitText Install -Description 'Sets up the agent that runs toolbox tasks on this machine.' {
    New-ConsoleField Path 'Install folder' -Type Path -Kind Folder -Default $installFolder -Required -Page Location -Parameter Path -Help 'Created during installation. Tab completes folder names.'
    New-ConsoleField Data 'Data folder' -Type Path -Kind Folder -Default $dataFolder -Page Location -Parameter DataPath -Help 'Logs, reports and the local queue.'
    New-ConsoleField Launcher 'Launcher entry' -Type Toggle -Default $true -Page Location -Parameter Launcher
    New-ConsoleField Components Components -Type MultiSelect -Choices $components -Display Name -CommandValue Id -GroupBy Group -Badge Size -Describe Description -Selected Selected -Disabled Disabled -Minimum 1 -Page Components -Parameter Component -Help 'Space toggles, Ctrl+A selects all, Enter closes the list.'
    New-ConsoleField Account 'Service account' -Type Select -Choices $accounts -Display Name -CommandValue Id -Describe Description -Default ServiceUser -Page Service -Parameter Account
    New-ConsoleField UserName 'User name' -VisibleWhen { $Values.Account -eq 'Custom' } -Required -Placeholder toolbox-agent -Page Service -Parameter UserName
    New-ConsoleField Password Password -Type Secret -VisibleWhen { $Values.Account -eq 'Custom' } -Page Service -Parameter Password -CommandPlaceholder '(Read-Host -AsSecureString)'
    New-ConsoleField AutoStart 'Start automatically' -Type Toggle -Default $true -Page Service -Parameter AutoStart
    New-ConsoleField Region Region -Type Select -Choices $regions -Display Name -CommandValue Id -Default eu -Required -Page Connection -Parameter Region -Help 'Choosing a region loads its servers.'
    New-ConsoleField Server Server -Type Select -DependsOn Region -Required -Page Connection -Parameter Server -LoadChoices {
        param($Values, $Endpoints)
        Start-Sleep -Milliseconds 300
        $Endpoints | Where-Object Region -EQ $Values.Region
    } -ArgumentList (,$endpoints) -Display Host -CommandValue Host -Badge Latency -Describe City -Disabled Disabled -Help 'Loaded for the chosen region; Esc cancels loading.'
    New-ConsoleField Port Port -Type Number -Integer -Minimum 1 -Maximum 65535 -Default 443 -Page Connection -Parameter Port
    New-ConsoleField UseProxy 'Use a proxy' -Type Toggle -Page Connection -Parameter UseProxy
    New-ConsoleField Proxy 'Proxy address' -VisibleWhen { $Values.UseProxy } -Required -Placeholder 'http://proxy.example.test:8080' -Page Connection -Parameter Proxy
}

# The snapshot uses native app controls on each wizard page, without asking for input.
function New-SetupSnapshot([int] $Page) {
    New-ConsoleRows -Gap 1 {
        New-ConsoleRule '[bold]Install Toolbox Agent[/]'
        New-ConsolePhases {
            for ($i = 0; $i -lt $pageNames.Count; $i++) {
                $status = if ($Page -eq 6 -or $i -lt $Page) { 'Pass' } elseif ($i -eq $Page) { 'Running' } else { 'Pending' }
                New-ConsoleStatusItem $pageNames[$i] -Status $status
            }
        }
        New-ConsolePanel -Title "$($Page + 1) / 7 · $($pageNames[$Page])" {
            New-ConsoleRows -Gap 1 {
                switch ($Page) {
                    0 {
                        'Where the agent and its data go.'
                        New-ConsoleTextInput -Name Path -Label 'Install folder' -Value $installFolder
                        New-ConsoleTextInput -Name Data -Label 'Data folder' -Value $dataFolder
                        New-ConsoleToggle -Name Launcher -Label 'Launcher entry' -Value $true
                        '[muted]Logs, reports and the local queue use the data folder.[/]'
                    }
                    1 {
                        $choices = $form.Pages[1].Fields[0].Choices
                        New-ConsoleMultiSelect -Name Components -Label Components -Choices $choices -Value @($choices | Where-Object Selected | ForEach-Object Value)
                        $components | ForEach-Object {
                            [pscustomobject] @{ Component = $_.Name; Group = $_.Group; Size = $_.Size; Description = $_.Description }
                        } | New-ConsoleTable -Property Component, Group, Size, Description -Expand
                        '[muted]Syslog forwarder needs the Enterprise licence. At least one component is required.[/]'
                    }
                    2 {
                        $choices = $form.Pages[2].Fields[0].Choices
                        New-ConsoleSelect -Name Account -Label 'Service account' -Choices $choices -Value $choices[1].Value
                        New-ConsoleToggle -Name AutoStart -Label 'Start automatically' -Value $true
                        'Choose a specific account to reveal user name and password fields.'
                        '[muted]Secrets are omitted from the equivalent command.[/]'
                    }
                    3 {
                        $choices = $form.Pages[3].Fields[0].Choices
                        New-ConsoleSelect -Name Region -Label Region -Choices $choices -Value $choices[0].Value
                        $serverField = New-ConsoleField Server -Type Select -Choices @($endpoints | Where-Object Region -EQ eu) -Display Host -CommandValue Host -Badge Latency -Describe City
                        New-ConsoleSelect -Name Server -Label Server -Choices $serverField.Choices -Value $serverField.Choices[1].Value
                        New-ConsoleTextInput -Name Port -Label 'Port (1–65535)' -Value 443
                        New-ConsoleToggle -Name UseProxy -Label 'Use a proxy'
                        '[muted]Changing the region reloads servers. Enabling a proxy reveals its address.[/]'
                    }
                    4 {
                        New-ConsoleKeyValue ([ordered] @{
                            'Install folder' = $installFolder; 'Data folder' = $dataFolder; Components = 'Agent, Cli, Html'
                            'Service account' = 'Dedicated service identity'; Server = 'eu-central.example.test:443'; 'Launcher / auto start' = 'Yes / Yes'
                        })
                        New-ConsolePanel -Title 'Equivalent command' {
                            "Install-ToolboxAgent -Path './toolbox-agent' -DataPath './toolbox-data' -Component Agent,Cli,Html -Account ServiceUser -Region eu -Server eu-central.example.test -Port 443 -Launcher -AutoStart"
                        }
                        New-ConsoleButton -Name Install -Label Install
                        New-ConsoleButton -Name Back -Label Back
                    }
                    default {
                        New-ConsoleStatusList {
                            for ($i = 0; $i -lt $installSteps.Count; $i++) {
                                $status = if ($Page -eq 6 -or $i -lt 2) { 'Pass' } elseif ($i -eq 2) { 'Running' } else { 'Pending' }
                                New-ConsoleStatusItem $installSteps[$i] -Status $status
                            }
                        }
                        New-ConsoleProgress Install -Total 40 -Value $(if ($Page -eq 6) { 40 } else { 24 }) -Status $(if ($Page -eq 6) { 'Pass' } else { 'Running' })
                        if ($Page -eq 6) { '[pass]Toolbox Agent installed[/] · 4 steps done · launch with the toolbox command' }
                        else { 'Configuring eu-central.example.test:443' }
                    }
                }
            }
        }
    }
}

# Behaviour: edit every page, review, then run a bounded in-memory installation.
if ($StaticSnapshot -or [Console]::IsInputRedirected) {
    Show-ConsoleApp { New-SetupSnapshot $Step } -StaticSnapshot
    return
}
$answers = Read-ConsoleForm $form
if (-not $answers) { return }
$dashboard = New-ConsoleDashboard -Title 'Installing Toolbox Agent' -Subtitle $answers.Path {
    New-ConsolePanel -Title Steps { New-ConsoleStatusList -Name steps -Item $installSteps }
    New-ConsolePadding -Padding 1, 0 { New-ConsoleProgress Install -Total 40 }
}
Start-ConsoleDashboard $dashboard -ArgumentList (,$installSteps) {
    param($Steps)
    foreach ($label in $Steps) {
        Update-ConsoleStatus -Name steps -Key $label -Status Running
        Update-ConsoleProgress -Label $label
        1..10 | ForEach-Object { Start-Sleep -Milliseconds 90; Update-ConsoleProgress -Increment 1 }
        Update-ConsoleStatus -Name steps -Key $label -Status Pass
    }
    Update-ConsoleProgress -Status Pass
}
New-ConsolePanel -Title 'Toolbox Agent installed' {
    New-ConsoleKeyValue {
        New-ConsoleKeyValueItem 'Install folder' $answers.Path
        New-ConsoleKeyValueItem Components ($answers.Components -join ', ')
        New-ConsoleKeyValueItem Server $answers.Server
        New-ConsoleKeyValueItem 'Steps done' '4' -Status Pass
        New-ConsoleKeyValueItem Launch toolbox
    }
} | Write-ConsoleWidget
C#Program.csView on GitHub
// A complete installer: location, components, service, connection, review and progress.
using ConsoleMonster;
using ConsoleMonster.Building;
using ConsoleMonster.Interactive;
using ConsoleMonster.Layout;
using ConsoleMonster.Live;
using ConsoleMonster.Styling;
using ConsoleMonster.Text;
using ConsoleMonster.Widgets;

// Data: invented installation inputs. The bounded driver only delays and updates widgets.
// No files, services, launch entries, network connections or persistent presets are created.
string installFolder = "./toolbox-agent";
string dataFolder = "./toolbox-data";
var components = new[] {
    new Part("Agent", "Agent service", "Core", "18 MB", "Runs tasks and reports results.", true),
    new Part("Cli", "Command-line tools", "Core", "6 MB", "The toolbox command and PowerShell module.", true),
    new Part("Html", "HTML reports", "Reporting", "4 MB", "Interactive reports with tables and charts.", true),
    new Part("Pdf", "PDF export", "Reporting", "31 MB", "Prints reports to PDF."),
    new Part("Chat", "Chat notifications", "Integrations", "1 MB", "Posts run summaries to a channel."),
    new Part("Mail", "Email notifications", "Integrations", "1 MB", "Sends reports by email."),
    new Part("Syslog", "Syslog forwarder", "Integrations", "1 MB", "Forwards findings to a collector.", false, "needs the Enterprise licence")
};
var accounts = new[] {
    new Choice("CurrentUser", "Current user") { Description = "Runs with your signed-in identity." },
    new Choice("ServiceUser", "Dedicated service identity") { Description = "Uses the platform's managed service identity." },
    new Choice("Custom", "A specific account") { Description = "An account you name; password stays out of the command." }
};
string[] pageNames = { "Location", "Components", "Service", "Connection", "Review", "Install", "Result" };
string[] installSteps = { "Copy files", "Register agent", "Configure connection", "Start agent" };
var endpoints = new[] {
    new Endpoint("eu", "eu-west.example.test", "Dublin", "12 ms"),
    new Endpoint("eu", "eu-central.example.test", "Frankfurt", "9 ms"),
    new Endpoint("eu", "eu-north.example.test", "Stockholm", "31 ms"),
    new Endpoint("us", "us-east.example.test", "East", "18 ms"),
    new Endpoint("us", "us-west.example.test", "West", "74 ms"),
    new Endpoint("ap", "ap-south.example.test", "South", "121 ms"),
    new Endpoint("ap", "ap-east.example.test", "East", "", "maintenance until 18:00")
};
bool snapshot = args.Contains("--snapshot") || (!args.Contains("--interactive") && Console.IsInputRedirected);
int stepAt = Array.IndexOf(args, "--step");
int step = stepAt < 0 ? 0 : int.Parse(args[stepAt + 1], System.Globalization.CultureInfo.InvariantCulture);
if (step < 0 || step > 6) throw new ArgumentOutOfRangeException(nameof(step));
Choice[] Parts() => components.Select(p => new Choice(p.Id, p.Name) {
    Group = p.Group, Description = p.Description, Badge = RichText.Plain(p.Size, new RichStyle(ThemeToken.Muted)),
    Selected = p.Selected, DisabledReason = p.Disabled
}).ToArray();
Choice[] Servers(string? region) => endpoints.Where(e => e.Region == region).Select(e => new Choice(e.Host) {
    Description = e.City, Badge = RichText.Plain(e.Latency, new RichStyle(ThemeToken.Muted)), DisabledReason = e.Disabled
}).ToArray();

// Layout: the native form owns validation, dependent choices, editors and review.
Form BuildForm() {
    Form form = new Form("Install Toolbox Agent") {
        Command = "Install-ToolboxAgent", SubmitText = "Install",
        Description = "Sets up the agent that runs toolbox tasks on this machine."
    };
    FormPage location = form.Page("Location");
    PathField path = location.Path("Path", "Install folder");
    path.PathKind = PathKind.Folder; path.Default = installFolder; path.Required = true; path.Parameter = "Path";
    path.Help = "Created during installation. Tab completes folder names.";
    PathField data = location.Path("Data", "Data folder");
    data.PathKind = PathKind.Folder; data.Default = dataFolder; data.Parameter = "DataPath";
    data.Help = "Logs, reports and the local queue.";
    ToggleField launcher = location.Toggle("Launcher", "Launcher entry");
    launcher.Default = true; launcher.Parameter = "Launcher";
    FormPage parts = form.Page("Components");
    ChoiceField selection = parts.MultiSelect("Components", "Components", Parts());
    selection.Minimum = 1; selection.Parameter = "Component";
    selection.Help = "Space toggles, Ctrl+A selects all, Enter closes the list.";
    FormPage service = form.Page("Service");
    ChoiceField account = service.Select("Account", "Service account", accounts);
    account.Default = "ServiceUser"; account.Parameter = "Account";
    TextField user = service.Text("UserName", "User name");
    user.VisibleWhen = v => Equals(v["Account"], "Custom"); user.Required = true;
    user.Placeholder = "toolbox-agent"; user.Parameter = "UserName";
    SecretField password = service.Secret("Password", "Password");
    password.VisibleWhen = v => Equals(v["Account"], "Custom"); password.Parameter = "Password";
    password.CommandPlaceholder = "(Read-Host -AsSecureString)";
    ToggleField automatic = service.Toggle("AutoStart", "Start automatically");
    automatic.Default = true; automatic.Parameter = "AutoStart";
    FormPage connection = form.Page("Connection");
    ChoiceField region = connection.Select("Region", "Region", new[] {
        new Choice("eu", "Europe"), new Choice("us", "Americas"), new Choice("ap", "Asia Pacific")
    });
    region.Default = "eu"; region.Required = true; region.Parameter = "Region";
    region.Help = "Choosing a region loads its servers.";
    ChoiceField server = connection.Select("Server", "Server");
    server.DependsOn.Add("Region"); server.Required = true; server.Parameter = "Server";
    server.Loader = async (values, token) => { await Task.Delay(300, token); return Servers(values.Get<string>("Region")); };
    server.Help = "Loaded for the chosen region; Esc cancels loading.";
    NumberField port = connection.Number("Port", "Port");
    port.WholeNumber = true; port.Minimum = 1; port.Maximum = 65535; port.Default = 443m; port.Parameter = "Port";
    ToggleField proxy = connection.Toggle("UseProxy", "Use a proxy"); proxy.Parameter = "UseProxy";
    TextField address = connection.Text("Proxy", "Proxy address");
    address.VisibleWhen = v => v.Get<bool>("UseProxy"); address.Required = true;
    address.Placeholder = "http://proxy.example.test:8080"; address.Parameter = "Proxy";
    return form;
}

// The snapshot uses native app controls on each wizard page, without asking for input.
IWidget SnapshotPage(int page) {
    IWidget content;
    switch (page) {
        case 0:
            content = new Rows(new TextBlock("Where the agent and its data go."),
                new TextInput("Path", installFolder) { Label = "Install folder" },
                new TextInput("Data", dataFolder) { Label = "Data folder" },
                new Toggle("Launcher", true) { Label = "Launcher entry" },
                new TextBlock("[muted]Logs, reports and the local queue use the data folder.[/]")) { Gap = 1 };
            break;
        case 1:
            Table parts = Table.For(components.Select(p => new { Component = p.Name, Group = p.Group, Size = p.Size, Description = p.Description }))
                .WithExpand().Column(p => p.Component).Column(p => p.Group).Column(p => p.Size).Column(p => p.Description, c => c.WithFlexible());
            content = new Rows(new MultiSelect("Components", Parts()) { Label = "Components" }, parts,
                new TextBlock("[muted]Syslog forwarder needs the Enterprise licence. At least one component is required.[/]")) { Gap = 1 };
            break;
        case 2:
            content = new Rows(new Select("Account", accounts) { Label = "Service account", Value = "ServiceUser" },
                new Toggle("AutoStart", true) { Label = "Start automatically" },
                new TextBlock("Choose a specific account to reveal user name and password fields."),
                new TextBlock("[muted]Secrets are omitted from the equivalent command.[/]")) { Gap = 1 };
            break;
        case 3:
            content = new Rows(new Select("Region", new[] { new Choice("eu", "Europe"), new Choice("us", "Americas"), new Choice("ap", "Asia Pacific") }) { Label = "Region", Value = "eu" },
                new Select("Server", Servers("eu")) { Label = "Server", Value = "eu-central.example.test" },
                new TextInput("Port", "443") { Label = "Port (1–65535)" },
                new Toggle("UseProxy") { Label = "Use a proxy" },
                new TextBlock("[muted]Changing the region reloads servers. Enabling a proxy reveals its address.[/]")) { Gap = 1 };
            break;
        case 4:
            content = new Rows(new KeyValueList().Add("Install folder", installFolder).Add("Data folder", dataFolder)
                .Add("Components", "Agent, Cli, Html").Add("Service account", "Dedicated service identity")
                .Add("Server", "eu-central.example.test:443").Add("Launcher / auto start", "Yes / Yes"),
                new Panel(new TextBlock("Install-ToolboxAgent -Path './toolbox-agent' -DataPath './toolbox-data' -Component Agent,Cli,Html -Account ServiceUser -Region eu -Server eu-central.example.test -Port 443 -Launcher -AutoStart"), "Equivalent command"),
                new Button("Install", "Install"), new Button("Back", "Back")) { Gap = 1 };
            break;
        default:
            StatusList tasks = new StatusList();
            for (int i = 0; i < installSteps.Length; i++) tasks.Add(installSteps[i], page == 6 || i < 2 ? StatusKind.Pass : i == 2 ? StatusKind.Running : StatusKind.Pending);
            ProgressBar progress = new ProgressBar("Install") {
                Total = 40, Value = page == 6 ? 40 : 24,
                Status = page == 6 ? StatusKind.Pass : StatusKind.Running
            };
            content = new Rows(tasks, progress, new TextBlock(page == 6
                ? "[pass]Toolbox Agent installed[/] · 4 steps done · launch with the toolbox command"
                : "Configuring eu-central.example.test:443")) { Gap = 1 };
            break;
    }
    Phases strip = new Phases();
    for (int i = 0; i < pageNames.Length; i++) strip.Set(pageNames[i], page == 6 || i < page ? StatusKind.Pass : i == page ? StatusKind.Running : StatusKind.Pending);
    return new Rows(new Rule("[bold]Install Toolbox Agent[/]"), strip,
        new Panel(content, $"{page + 1} / 7 · {pageNames[page]}")) { Gap = 1 };
}

// Behaviour: edit every page, review, then run a bounded in-memory installation.
if (snapshot) {
    ConsoleApp.Run(SnapshotPage(step), options: new ConsoleAppOptions { StaticSnapshot = true });
    return;
}
FormResult answers = BuildForm().Show(new FormOptions());
if (!answers.Submitted) return;
StatusList install = new StatusList("steps");
foreach (string label in installSteps) install.Add(label, StatusKind.Pending);
ProgressBar bar = new ProgressBar("Install") { Total = 40 };
Dashboard dashboard = new DashboardBuilder("Installing Toolbox Agent")
    .Subtitle(Markup.Escape(answers.Values.Get<string>("Path")))
    .Panel("Steps", install).Add(new Padding(bar, 1, 0, 1, 0)).BuildDashboard();
using (LiveSession session = new LiveSession(dashboard)) {
    session.Run(s => {
        StatusListHandle status = install.Handle(s);
        ProgressHandle progress = bar.Handle(s);
        foreach (string label in installSteps) {
            status.Set(label, StatusKind.Running);
            progress.Label(label);
            for (int i = 0; i < 10; i++) {
                s.Cancellation.ThrowIfCancellationRequested();
                Thread.Sleep(90); progress.Advance();
            }
            status.Set(label, StatusKind.Pass);
        }
        progress.Complete();
    });
}
Terminal.Write(new Panel(new KeyValueList().Add("Install folder", Markup.Escape(answers.Values.Get<string>("Path")))
    .Add("Components", Markup.Escape(string.Join(", ", answers.Values.GetList<object>("Components"))))
    .Add("Server", Markup.Escape(answers.Values.Get<string>("Server")))
    .Add("Steps done", "4", StatusKind.Pass).Add("Launch", "toolbox"), "Toolbox Agent installed"));

record Part(string Id, string Name, string Group, string Size, string Description, bool Selected = false, string? Disabled = null);
record Endpoint(string Region, string Host, string City, string Latency, string? Disabled = null);

Run it yourself

Three steps in PowerShell, four in C#. Every command copies with one click.

  1. Install the module

    Once. Windows PowerShell 5.1 or PowerShell 7 on Windows, macOS or Linux.

    Install-Module ConsoleMonster -Scope CurrentUser
  2. Save the program

    Download it, or copy it from above into setup.ps1.

  3. Run it

    From the folder you saved it in.

    ./setup.ps1
  1. Create a project

    A plain console app; .NET 8 or later.

    dotnet new console -n Setup; cd Setup
  2. Add ConsoleMonster

    The only package the program needs.

    dotnet add package ConsoleMonster
  3. Replace Program.cs

    Download it, or copy the program from above.

  4. Run it

    dotnet run

Or skip the copying. Every demo ships with the module and the project template.

Show-ConsoleDemo setup
Get-ConsoleDemo setup -Path ./setup.ps1
dotnet new install ConsoleMonster.Templates
dotnet new consolemonster --demo setup -n Setup

Make it yours

Start with these, then read the rest of the file.

  1. Replace the component catalog and regional endpoints.

  2. Set your application folders and account policy.

  3. Connect the validated answers to your explicit installation action.

More wizards

All demos