A diagram should take a minute to type. Each step below adds one idea to the last, and each frame is what the program beside it prints, in C#, with the fluent builder or in PowerShell. The core package draws every diagram as a tree and needs nothing else; with ConsoleMonster.Graphics registered, as here, you get boxes, routed connections and groups, laid out by ChartForgeX.
The PowerShell commands follow PSWriteHTML's diagrams: New-ConsoleDiagramNode -Label 'A' -To 'B' is the whole first lesson, and New-ConsoleDiagramLink and New-ConsoleDiagramGroup come when you need them.
1. Three boxes, two arrows
A node can point at other nodes with To.
Install-Module ConsoleMonster -Scope CurrentUser# A node can point at other nodes with -To.
New-ConsoleDiagram {
New-ConsoleDiagramNode 'Laptop' -To 'Router'
New-ConsoleDiagramNode 'Router' -To 'Internet'
New-ConsoleDiagramNode 'Internet'
} | Show-ConsoleFull program
Import-Module ConsoleMonster
# A node can point at other nodes with -To.
New-ConsoleDiagram {
New-ConsoleDiagramNode 'Laptop' -To 'Router'
New-ConsoleDiagramNode 'Router' -To 'Internet'
New-ConsoleDiagramNode 'Internet'
} | Show-ConsoleRun: ./diagram-1.ps1
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// A node can point at other nodes with To.
var diagram = new Diagram {
Nodes = {
new DiagramNode("Laptop") { To = { "Router" } },
new DiagramNode("Router") { To = { "Internet" } },
new DiagramNode("Internet")
}
};
Terminal.Write(diagram);Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// A node can point at other nodes with To.
var diagram = new Diagram {
Nodes = {
new DiagramNode("Laptop") { To = { "Router" } },
new DiagramNode("Router") { To = { "Internet" } },
new DiagramNode("Internet")
}
};
Terminal.Write(diagram);Run: dotnet run
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// A node can point at other nodes with To.
var diagram = Ui.Diagram()
.AddNode(new DiagramNode("Laptop") { To = { "Router" } })
.AddNode(new DiagramNode("Router") { To = { "Internet" } })
.AddNode(new DiagramNode("Internet"));
diagram.Show();
Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// A node can point at other nodes with To.
var diagram = Ui.Diagram()
.AddNode(new DiagramNode("Laptop") { To = { "Router" } })
.AddNode(new DiagramNode("Router") { To = { "Internet" } })
.AddNode(new DiagramNode("Internet"));
diagram.Show();
Run: dotnet run
┌────────────┐ ┌────────────┐ ┌────────────┐ │ Laptop │ │ Router │ │ Internet │ │ │───▶│ │───▶│ │ └────────────┘ └────────────┘ └────────────┘
┌────────────┐ ┌────────────┐ ┌────────────┐ │ Laptop │ │ Router │ │ Internet │ │ │───▶│ │───▶│ │ └────────────┘ └────────────┘ └────────────┘
2. Kinds and labels
Kind says what a node is (and picks its icon in images and HTML). A link of its own, New-ConsoleDiagramLink or DiagramLink, can carry a label.
Install-Module ConsoleMonster -Scope CurrentUser# -Kind says what a node is; a link of its own can carry a label.
New-ConsoleDiagram -Title 'Home network' {
New-ConsoleDiagramNode 'Laptop' -Kind Workstation -Subtitle 'Wi-Fi'
New-ConsoleDiagramNode 'TV' -Kind Workstation -Subtitle 'Ethernet'
New-ConsoleDiagramNode 'NAS' -Kind Storage -Subtitle 'Photos, backups'
New-ConsoleDiagramNode 'Router' -Kind Gateway -Subtitle '192.168.1.1'
New-ConsoleDiagramNode 'Internet' -Kind Cloud -Subtitle 'Fibre 1 Gb'
New-ConsoleDiagramLink 'Laptop' 'Router' -Label 'Wi-Fi'
New-ConsoleDiagramLink 'TV' 'Router' -Label '1 Gb'
New-ConsoleDiagramLink 'NAS' 'Router' -Label '1 Gb'
New-ConsoleDiagramLink 'Router' 'Internet' -Label 'WAN'
} | Show-ConsoleFull program
Import-Module ConsoleMonster
# -Kind says what a node is; a link of its own can carry a label.
New-ConsoleDiagram -Title 'Home network' {
New-ConsoleDiagramNode 'Laptop' -Kind Workstation -Subtitle 'Wi-Fi'
New-ConsoleDiagramNode 'TV' -Kind Workstation -Subtitle 'Ethernet'
New-ConsoleDiagramNode 'NAS' -Kind Storage -Subtitle 'Photos, backups'
New-ConsoleDiagramNode 'Router' -Kind Gateway -Subtitle '192.168.1.1'
New-ConsoleDiagramNode 'Internet' -Kind Cloud -Subtitle 'Fibre 1 Gb'
New-ConsoleDiagramLink 'Laptop' 'Router' -Label 'Wi-Fi'
New-ConsoleDiagramLink 'TV' 'Router' -Label '1 Gb'
New-ConsoleDiagramLink 'NAS' 'Router' -Label '1 Gb'
New-ConsoleDiagramLink 'Router' 'Internet' -Label 'WAN'
} | Show-ConsoleRun: ./diagram-2.ps1
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// Kind says what a node is; a link of its own can carry a label.
var diagram = new Diagram("Home network") {
Nodes = {
new DiagramNode("Laptop") { Kind = DiagramNodeKind.Workstation, Subtitle = "Wi-Fi" },
new DiagramNode("TV") { Kind = DiagramNodeKind.Workstation, Subtitle = "Ethernet" },
new DiagramNode("NAS") { Kind = DiagramNodeKind.Storage, Subtitle = "Photos, backups" },
new DiagramNode("Router") { Kind = DiagramNodeKind.Gateway, Subtitle = "192.168.1.1" },
new DiagramNode("Internet") { Kind = DiagramNodeKind.Cloud, Subtitle = "Fibre 1 Gb" }
},
Links = {
new DiagramLink("Laptop", "Router") { Label = "Wi-Fi" },
new DiagramLink("TV", "Router") { Label = "1 Gb" },
new DiagramLink("NAS", "Router") { Label = "1 Gb" },
new DiagramLink("Router", "Internet") { Label = "WAN" }
}
};
Terminal.Write(diagram);Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// Kind says what a node is; a link of its own can carry a label.
var diagram = new Diagram("Home network") {
Nodes = {
new DiagramNode("Laptop") { Kind = DiagramNodeKind.Workstation, Subtitle = "Wi-Fi" },
new DiagramNode("TV") { Kind = DiagramNodeKind.Workstation, Subtitle = "Ethernet" },
new DiagramNode("NAS") { Kind = DiagramNodeKind.Storage, Subtitle = "Photos, backups" },
new DiagramNode("Router") { Kind = DiagramNodeKind.Gateway, Subtitle = "192.168.1.1" },
new DiagramNode("Internet") { Kind = DiagramNodeKind.Cloud, Subtitle = "Fibre 1 Gb" }
},
Links = {
new DiagramLink("Laptop", "Router") { Label = "Wi-Fi" },
new DiagramLink("TV", "Router") { Label = "1 Gb" },
new DiagramLink("NAS", "Router") { Label = "1 Gb" },
new DiagramLink("Router", "Internet") { Label = "WAN" }
}
};
Terminal.Write(diagram);Run: dotnet run
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// Kind says what a node is; a link of its own can carry a label.
var diagram = Ui.Diagram()
.Title("Home network")
.AddNode(new DiagramNode("Laptop").Kind(DiagramNodeKind.Workstation).Subtitle("Wi-Fi"))
.AddNode(new DiagramNode("TV").Kind(DiagramNodeKind.Workstation).Subtitle("Ethernet"))
.AddNode(new DiagramNode("NAS").Kind(DiagramNodeKind.Storage).Subtitle("Photos, backups"))
.AddNode(new DiagramNode("Router").Kind(DiagramNodeKind.Gateway).Subtitle("192.168.1.1"))
.AddNode(new DiagramNode("Internet").Kind(DiagramNodeKind.Cloud).Subtitle("Fibre 1 Gb"))
.AddLink(new DiagramLink("Laptop", "Router").Label("Wi-Fi"))
.AddLink(new DiagramLink("TV", "Router").Label("1 Gb"))
.AddLink(new DiagramLink("NAS", "Router").Label("1 Gb"))
.AddLink(new DiagramLink("Router", "Internet").Label("WAN"));
diagram.Show();
Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// Kind says what a node is; a link of its own can carry a label.
var diagram = Ui.Diagram()
.Title("Home network")
.AddNode(new DiagramNode("Laptop").Kind(DiagramNodeKind.Workstation).Subtitle("Wi-Fi"))
.AddNode(new DiagramNode("TV").Kind(DiagramNodeKind.Workstation).Subtitle("Ethernet"))
.AddNode(new DiagramNode("NAS").Kind(DiagramNodeKind.Storage).Subtitle("Photos, backups"))
.AddNode(new DiagramNode("Router").Kind(DiagramNodeKind.Gateway).Subtitle("192.168.1.1"))
.AddNode(new DiagramNode("Internet").Kind(DiagramNodeKind.Cloud).Subtitle("Fibre 1 Gb"))
.AddLink(new DiagramLink("Laptop", "Router").Label("Wi-Fi"))
.AddLink(new DiagramLink("TV", "Router").Label("1 Gb"))
.AddLink(new DiagramLink("NAS", "Router").Label("1 Gb"))
.AddLink(new DiagramLink("Router", "Internet").Label("WAN"));
diagram.Show();
Run: dotnet run
Home network ┌─────────────────┐ ┌────────────┐ ┌─────────────┐ ┌────────────┐ │ NAS │ │ Laptop │ Wi-Fi │ Router │ │ Internet │ │ Photos, backups │ │ Wi-Fi │────────▶│ 192.168.1.1 │ │ Fibre 1 Gb │ │ │ │ │ │ │ WAN │ │ │ │───┐└────────────┘ ┌▶│ │──────▶│ │ │ │ └──────── 1 Gb ───────┼▶│ │ │ │ │ │ │ │ │ │ │ └─────────────────┘ ┌────────────┐ │ └─────────────┘ └────────────┘ │ TV │ 1 Gb │ │ Ethernet │───────┘ │ │ └────────────┘
Home network ┌─────────────────┐ ┌────────────┐ ┌─────────────┐ ┌────────────┐ │ NAS │ │ Laptop │ Wi-Fi │ Router │ │ Internet │ │ Photos, backups │ │ Wi-Fi │────────▶│ 192.168.1.1 │ │ Fibre 1 Gb │ │ │ │ │ │ │ WAN │ │ │ │───┐└────────────┘ ┌▶│ │──────▶│ │ │ │ └──────── 1 Gb ───────┼▶│ │ │ │ │ │ │ │ │ │ │ └─────────────────┘ ┌────────────┐ │ └─────────────┘ └────────────┘ │ TV │ 1 Gb │ │ Ethernet │───────┘ │ │ └────────────┘
3. Groups
A group is a block around its nodes, the way a panel wraps content. Groups can nest.
Install-Module ConsoleMonster -Scope CurrentUser# A group is a block around its nodes.
New-ConsoleDiagram -Title 'Small office' {
New-ConsoleDiagramGroup 'Office LAN' {
New-ConsoleDiagramNode '12 PCs' -Kind Workstation -To 'Firewall'
New-ConsoleDiagramNode 'File server' -Kind Storage -Subtitle 'FS01'
New-ConsoleDiagramNode 'Printer' -Kind Device
}
New-ConsoleDiagramGroup 'DMZ' {
New-ConsoleDiagramNode 'Web server' -Kind Server -Subtitle 'WEB01'
}
New-ConsoleDiagramGroup 'Cloud' {
New-ConsoleDiagramNode 'Microsoft 365' -Kind Cloud
New-ConsoleDiagramNode 'Backup' -Kind Storage -Subtitle 'Offsite'
}
New-ConsoleDiagramNode 'Firewall' -Kind Gateway -To 'Web server', 'Microsoft 365'
New-ConsoleDiagramLink 'File server' 'Backup' -Label 'nightly'
} | Show-ConsoleFull program
Import-Module ConsoleMonster
# A group is a block around its nodes.
New-ConsoleDiagram -Title 'Small office' {
New-ConsoleDiagramGroup 'Office LAN' {
New-ConsoleDiagramNode '12 PCs' -Kind Workstation -To 'Firewall'
New-ConsoleDiagramNode 'File server' -Kind Storage -Subtitle 'FS01'
New-ConsoleDiagramNode 'Printer' -Kind Device
}
New-ConsoleDiagramGroup 'DMZ' {
New-ConsoleDiagramNode 'Web server' -Kind Server -Subtitle 'WEB01'
}
New-ConsoleDiagramGroup 'Cloud' {
New-ConsoleDiagramNode 'Microsoft 365' -Kind Cloud
New-ConsoleDiagramNode 'Backup' -Kind Storage -Subtitle 'Offsite'
}
New-ConsoleDiagramNode 'Firewall' -Kind Gateway -To 'Web server', 'Microsoft 365'
New-ConsoleDiagramLink 'File server' 'Backup' -Label 'nightly'
} | Show-ConsoleRun: ./diagram-3.ps1
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// A group is a block around its nodes.
var diagram = new Diagram("Small office") {
Groups = {
new DiagramGroup("Office LAN") {
Nodes = {
new DiagramNode("12 PCs") {
Kind = DiagramNodeKind.Workstation,
To = { "Firewall" }
},
new DiagramNode("File server") {
Kind = DiagramNodeKind.Storage,
Subtitle = "FS01"
},
new DiagramNode("Printer") { Kind = DiagramNodeKind.Device }
}
},
new DiagramGroup("DMZ") {
Nodes = {
new DiagramNode("Web server") {
Kind = DiagramNodeKind.Server,
Subtitle = "WEB01"
}
}
},
new DiagramGroup("Cloud") {
Nodes = {
new DiagramNode("Microsoft 365") { Kind = DiagramNodeKind.Cloud },
new DiagramNode("Backup") {
Kind = DiagramNodeKind.Storage,
Subtitle = "Offsite"
}
}
}
},
Nodes = {
new DiagramNode("Firewall") {
Kind = DiagramNodeKind.Gateway,
To = { "Web server", "Microsoft 365" }
}
},
Links = {
new DiagramLink("File server", "Backup") { Label = "nightly" }
}
};
Terminal.Write(diagram);Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// A group is a block around its nodes.
var diagram = new Diagram("Small office") {
Groups = {
new DiagramGroup("Office LAN") {
Nodes = {
new DiagramNode("12 PCs") {
Kind = DiagramNodeKind.Workstation,
To = { "Firewall" }
},
new DiagramNode("File server") {
Kind = DiagramNodeKind.Storage,
Subtitle = "FS01"
},
new DiagramNode("Printer") { Kind = DiagramNodeKind.Device }
}
},
new DiagramGroup("DMZ") {
Nodes = {
new DiagramNode("Web server") {
Kind = DiagramNodeKind.Server,
Subtitle = "WEB01"
}
}
},
new DiagramGroup("Cloud") {
Nodes = {
new DiagramNode("Microsoft 365") { Kind = DiagramNodeKind.Cloud },
new DiagramNode("Backup") {
Kind = DiagramNodeKind.Storage,
Subtitle = "Offsite"
}
}
}
},
Nodes = {
new DiagramNode("Firewall") {
Kind = DiagramNodeKind.Gateway,
To = { "Web server", "Microsoft 365" }
}
},
Links = {
new DiagramLink("File server", "Backup") { Label = "nightly" }
}
};
Terminal.Write(diagram);Run: dotnet run
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// A group is a block around its nodes.
var diagram = Ui.Diagram()
.Title("Small office")
.AddGroup(new DiagramGroup("Office LAN") { Nodes =
{
new DiagramNode("12 PCs") { Kind = DiagramNodeKind.Workstation, To = { "Firewall" } },
new DiagramNode("File server").Kind(DiagramNodeKind.Storage).Subtitle("FS01"),
new DiagramNode("Printer").Kind(DiagramNodeKind.Device)
} })
.AddGroup(new DiagramGroup("DMZ") { Nodes = { new DiagramNode("Web server")
.Kind(DiagramNodeKind.Server)
.Subtitle("WEB01") } })
.AddGroup(new DiagramGroup("Cloud") { Nodes =
{
new DiagramNode("Microsoft 365").Kind(DiagramNodeKind.Cloud),
new DiagramNode("Backup").Kind(DiagramNodeKind.Storage).Subtitle("Offsite")
} })
.AddNode(new DiagramNode("Firewall")
{
Kind = DiagramNodeKind.Gateway,
To = { "Web server", "Microsoft 365" }
})
.AddLink(new DiagramLink("File server", "Backup").Label("nightly"));
diagram.Show();
Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// A group is a block around its nodes.
var diagram = Ui.Diagram()
.Title("Small office")
.AddGroup(new DiagramGroup("Office LAN") { Nodes =
{
new DiagramNode("12 PCs") { Kind = DiagramNodeKind.Workstation, To = { "Firewall" } },
new DiagramNode("File server").Kind(DiagramNodeKind.Storage).Subtitle("FS01"),
new DiagramNode("Printer").Kind(DiagramNodeKind.Device)
} })
.AddGroup(new DiagramGroup("DMZ") { Nodes = { new DiagramNode("Web server")
.Kind(DiagramNodeKind.Server)
.Subtitle("WEB01") } })
.AddGroup(new DiagramGroup("Cloud") { Nodes =
{
new DiagramNode("Microsoft 365").Kind(DiagramNodeKind.Cloud),
new DiagramNode("Backup").Kind(DiagramNodeKind.Storage).Subtitle("Offsite")
} })
.AddNode(new DiagramNode("Firewall")
{
Kind = DiagramNodeKind.Gateway,
To = { "Web server", "Microsoft 365" }
})
.AddLink(new DiagramLink("File server", "Backup").Label("nightly"));
diagram.Show();
Run: dotnet run
Small office ╭╌ DMZ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮ ╭╌ Office LAN ╌╌╌╌╌╮ ╎ ╎ ╎ ╎ ╎ ┌────────────────┐ ╎ ╎ ┌──────────────┐ ╎ ╎ │ Web server │ ╎ ╎ │ 12 PCs │ ╎ ┌─▶│ WEB01 │ ╎ ╎ │ │───────────┐ │╎ │ │ ╎ ╎ └──────────────┘ ╎ │ │╎ └────────────────┘ ╎ ╎ ╎ │ │╎ ╎ ╎ ╎ │ │╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ ╎ ╎ ▼ │ ╎ ┌──────────────┐ ╎ ┌────────────┐ │ ╎ │ File server │ ╎ │ Firewall │─┘ ╎ │ FS01 │───┐│ │─┐╭╌ Cloud ╌╌╌╌╌╌╌╌╌╌╌╌╮ ╎ │ │ ╎ │└────────────┘ │╎ ╎ ╎ └──────────────┘ ╎ │ │╎ ┌────────────────┐ ╎ ╎ ╎ │ │╎ │ Backup │ ╎ ╎ ╎ └──── nightly ──┼─▶│ Offsite │ ╎ ╎ ┌──────────────┐ ╎ │╎ │ │ ╎ ╎ │ Printer │ ╎ │╎ └────────────────┘ ╎ ╎ │ │ ╎ │╎ ╎ ╎ └──────────────┘ ╎ │╎ ╎ ╎ ╎ │╎ ┌────────────────┐ ╎ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ │╎ │ Microsoft 365 │ ╎ └─▶│ │ ╎ ╎ └────────────────┘ ╎ ╎ ╎ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
Small office ╭╌ DMZ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮ ╭╌ Office LAN ╌╌╌╌╌╮ ╎ ╎ ╎ ╎ ╎ ┌────────────────┐ ╎ ╎ ┌──────────────┐ ╎ ╎ │ Web server │ ╎ ╎ │ 12 PCs │ ╎ ┌─▶│ WEB01 │ ╎ ╎ │ │───────────┐ │╎ │ │ ╎ ╎ └──────────────┘ ╎ │ │╎ └────────────────┘ ╎ ╎ ╎ │ │╎ ╎ ╎ ╎ │ │╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ ╎ ╎ ▼ │ ╎ ┌──────────────┐ ╎ ┌────────────┐ │ ╎ │ File server │ ╎ │ Firewall │─┘ ╎ │ FS01 │───┐│ │─┐╭╌ Cloud ╌╌╌╌╌╌╌╌╌╌╌╌╮ ╎ │ │ ╎ │└────────────┘ │╎ ╎ ╎ └──────────────┘ ╎ │ │╎ ┌────────────────┐ ╎ ╎ ╎ │ │╎ │ Backup │ ╎ ╎ ╎ └──── nightly ──┼─▶│ Offsite │ ╎ ╎ ┌──────────────┐ ╎ │╎ │ │ ╎ ╎ │ Printer │ ╎ │╎ └────────────────┘ ╎ ╎ │ │ ╎ │╎ ╎ ╎ └──────────────┘ ╎ │╎ ╎ ╎ ╎ │╎ ┌────────────────┐ ╎ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ │╎ │ Microsoft 365 │ ╎ └─▶│ │ ╎ ╎ └────────────────┘ ╎ ╎ ╎ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
4. Status
Status uses the same words as every other widget: Pass, Warn, Fail, Info and Pending. Put what is wrong in Value. A node without a status shows no mark, the legend appears only when something isn't fine, and Needs attention under the diagram lists every node and link that isn't, with its full text.
Install-Module ConsoleMonster -Scope CurrentUser# Status uses the same words as every widget; -Value says what is wrong.
New-ConsoleDiagram -Title 'Small office · Monday 08:10' {
New-ConsoleDiagramGroup 'Office LAN' {
New-ConsoleDiagramNode '12 PCs' -Kind Workstation -To 'Firewall'
New-ConsoleDiagramNode 'File server' -Kind Storage -Status Warn -Value 'disk 91%'
New-ConsoleDiagramNode 'Printer' -Kind Device -Status Fail -Value 'offline'
}
New-ConsoleDiagramGroup 'Cloud' {
New-ConsoleDiagramNode 'Microsoft 365' -Kind Cloud
New-ConsoleDiagramNode 'Backup' -Kind Storage -Status Fail -Value 'last success 2 days ago'
}
New-ConsoleDiagramNode 'Firewall' -Kind Gateway -To 'Microsoft 365'
New-ConsoleDiagramLink 'File server' 'Backup' -Label 'failed 2 nights' -Status Fail
} | Show-ConsoleFull program
Import-Module ConsoleMonster
# Status uses the same words as every widget; -Value says what is wrong.
New-ConsoleDiagram -Title 'Small office · Monday 08:10' {
New-ConsoleDiagramGroup 'Office LAN' {
New-ConsoleDiagramNode '12 PCs' -Kind Workstation -To 'Firewall'
New-ConsoleDiagramNode 'File server' -Kind Storage -Status Warn -Value 'disk 91%'
New-ConsoleDiagramNode 'Printer' -Kind Device -Status Fail -Value 'offline'
}
New-ConsoleDiagramGroup 'Cloud' {
New-ConsoleDiagramNode 'Microsoft 365' -Kind Cloud
New-ConsoleDiagramNode 'Backup' -Kind Storage -Status Fail -Value 'last success 2 days ago'
}
New-ConsoleDiagramNode 'Firewall' -Kind Gateway -To 'Microsoft 365'
New-ConsoleDiagramLink 'File server' 'Backup' -Label 'failed 2 nights' -Status Fail
} | Show-ConsoleRun: ./diagram-4.ps1
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// Status uses the same words as every widget; Value says what is wrong.
var diagram = new Diagram("Small office · Monday 08:10") {
Groups = {
new DiagramGroup("Office LAN") {
Nodes = {
new DiagramNode("12 PCs") {
Kind = DiagramNodeKind.Workstation,
To = { "Firewall" }
},
new DiagramNode("File server") {
Kind = DiagramNodeKind.Storage,
Status = StatusKind.Warn,
Value = "disk 91%"
},
new DiagramNode("Printer") {
Kind = DiagramNodeKind.Device,
Status = StatusKind.Fail,
Value = "offline"
}
}
},
new DiagramGroup("Cloud") {
Nodes = {
new DiagramNode("Microsoft 365") { Kind = DiagramNodeKind.Cloud },
new DiagramNode("Backup") {
Kind = DiagramNodeKind.Storage,
Status = StatusKind.Fail,
Value = "last success 2 days ago"
}
}
}
},
Nodes = {
new DiagramNode("Firewall") {
Kind = DiagramNodeKind.Gateway,
To = { "Microsoft 365" }
}
},
Links = {
new DiagramLink("File server", "Backup") {
Label = "failed 2 nights",
Status = StatusKind.Fail
}
}
};
Terminal.Write(diagram);Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Styling;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// Status uses the same words as every widget; Value says what is wrong.
var diagram = new Diagram("Small office · Monday 08:10") {
Groups = {
new DiagramGroup("Office LAN") {
Nodes = {
new DiagramNode("12 PCs") {
Kind = DiagramNodeKind.Workstation,
To = { "Firewall" }
},
new DiagramNode("File server") {
Kind = DiagramNodeKind.Storage,
Status = StatusKind.Warn,
Value = "disk 91%"
},
new DiagramNode("Printer") {
Kind = DiagramNodeKind.Device,
Status = StatusKind.Fail,
Value = "offline"
}
}
},
new DiagramGroup("Cloud") {
Nodes = {
new DiagramNode("Microsoft 365") { Kind = DiagramNodeKind.Cloud },
new DiagramNode("Backup") {
Kind = DiagramNodeKind.Storage,
Status = StatusKind.Fail,
Value = "last success 2 days ago"
}
}
}
},
Nodes = {
new DiagramNode("Firewall") {
Kind = DiagramNodeKind.Gateway,
To = { "Microsoft 365" }
}
},
Links = {
new DiagramLink("File server", "Backup") {
Label = "failed 2 nights",
Status = StatusKind.Fail
}
}
};
Terminal.Write(diagram);Run: dotnet run
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// Status uses the same words as every widget; Value says what is wrong.
var diagram = Ui.Diagram()
.Title("Small office · Monday 08:10")
.AddGroup(new DiagramGroup("Office LAN") { Nodes =
{
new DiagramNode("12 PCs") { Kind = DiagramNodeKind.Workstation, To = { "Firewall" } },
new DiagramNode("File server")
.Kind(DiagramNodeKind.Storage)
.Status(StatusKind.Warn)
.Value("disk 91%"),
new DiagramNode("Printer")
.Kind(DiagramNodeKind.Device)
.Status(StatusKind.Fail)
.Value("offline")
} })
.AddGroup(new DiagramGroup("Cloud") { Nodes =
{
new DiagramNode("Microsoft 365").Kind(DiagramNodeKind.Cloud),
new DiagramNode("Backup")
.Kind(DiagramNodeKind.Storage)
.Status(StatusKind.Fail)
.Value("last success 2 days ago")
} })
.AddNode(new DiagramNode("Firewall")
{
Kind = DiagramNodeKind.Gateway,
To = { "Microsoft 365" }
})
.AddLink(new DiagramLink("File server", "Backup")
.Label("failed 2 nights")
.Status(StatusKind.Fail));
diagram.Show();
Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Styling;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// Status uses the same words as every widget; Value says what is wrong.
var diagram = Ui.Diagram()
.Title("Small office · Monday 08:10")
.AddGroup(new DiagramGroup("Office LAN") { Nodes =
{
new DiagramNode("12 PCs") { Kind = DiagramNodeKind.Workstation, To = { "Firewall" } },
new DiagramNode("File server")
.Kind(DiagramNodeKind.Storage)
.Status(StatusKind.Warn)
.Value("disk 91%"),
new DiagramNode("Printer")
.Kind(DiagramNodeKind.Device)
.Status(StatusKind.Fail)
.Value("offline")
} })
.AddGroup(new DiagramGroup("Cloud") { Nodes =
{
new DiagramNode("Microsoft 365").Kind(DiagramNodeKind.Cloud),
new DiagramNode("Backup")
.Kind(DiagramNodeKind.Storage)
.Status(StatusKind.Fail)
.Value("last success 2 days ago")
} })
.AddNode(new DiagramNode("Firewall")
{
Kind = DiagramNodeKind.Gateway,
To = { "Microsoft 365" }
})
.AddLink(new DiagramLink("File server", "Backup")
.Label("failed 2 nights")
.Status(StatusKind.Fail));
diagram.Show();
Run: dotnet run
Small office · Monday 08:10 ╭╌ Office LAN ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮ ╎ ╎ ╭╌ Cloud ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮ ╎ ┌──────────────────────────┐ ╎ ╎ ╎ ╎ │ 12 PCs │ ╎ ╎ ┌──────────────────────────┐ ╎ ╎ │ │──────────────────┐ ╎ │ Backup ✗│ ╎ ╎ └──────────────────────────┘ ╎ ┌──────────────┼────────────────▶│ last success 2 days ago │ ╎ ╎ ╎ │ │ ╎ └──────────────────────────┘ ╎ ╎ ╎ │ ▼ ╎ ╎ ╎ ┌──────────────────────────┐ ╎ │┌──────────────────────────┐ ╎ ╎ ╎ │ File server ▴│ ╎ ││ Firewall │ ╎ ╎ ╎ │ disk 91% │───┘│ │─┐╎ ╎ ╎ └──────────────────────────┘ ╎ └──────────────────────────┘ │╎ ┌──────────────────────────┐ ╎ ╎ ╎ │╎ │ Microsoft 365 │ ╎ ╎ ╎ └─▶│ │ ╎ ╎ ┌──────────────────────────┐ ╎ ╎ └──────────────────────────┘ ╎ ╎ │ Printer ✗│ ╎ ╎ ╎ ╎ │ offline │ ╎ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ ╎ └──────────────────────────┘ ╎ ╎ ╎ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ ▴ Warn ✗ Fail Needs attention ▴ File server disk 91% ✗ Printer offline ✗ Backup last success 2 days ago ✗ File server → Backup failed 2 nights
Small office · Monday 08:10 ╭╌ Office LAN ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮ ╎ ╎ ╭╌ Cloud ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮ ╎ ┌──────────────────────────┐ ╎ ╎ ╎ ╎ │ 12 PCs │ ╎ ╎ ┌──────────────────────────┐ ╎ ╎ │ │──────────────────┐ ╎ │ Backup ✗│ ╎ ╎ └──────────────────────────┘ ╎ ┌──────────────┼────────────────▶│ last success 2 days ago │ ╎ ╎ ╎ │ │ ╎ └──────────────────────────┘ ╎ ╎ ╎ │ ▼ ╎ ╎ ╎ ┌──────────────────────────┐ ╎ │┌──────────────────────────┐ ╎ ╎ ╎ │ File server ▴│ ╎ ││ Firewall │ ╎ ╎ ╎ │ disk 91% │───┘│ │─┐╎ ╎ ╎ └──────────────────────────┘ ╎ └──────────────────────────┘ │╎ ┌──────────────────────────┐ ╎ ╎ ╎ │╎ │ Microsoft 365 │ ╎ ╎ ╎ └─▶│ │ ╎ ╎ ┌──────────────────────────┐ ╎ ╎ └──────────────────────────┘ ╎ ╎ │ Printer ✗│ ╎ ╎ ╎ ╎ │ offline │ ╎ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ ╎ └──────────────────────────┘ ╎ ╎ ╎ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ ▴ Warn ✗ Fail Needs attention ▴ File server disk 91% ✗ Printer offline ✗ Backup last success 2 days ago ✗ File server → Backup failed 2 nights
5. From a list
You rarely type every link. Nodes you never declared are created from the links, so a list of rows is enough: Diagram.FromLinks in C#, and in PowerShell a hashtable per row splatted into New-ConsoleDiagramLink. Rows with From, To, Label, Status and Group columns can also be piped straight into Show-ConsoleDiagram, for example from Import-Csv.
Install-Module ConsoleMonster -Scope CurrentUser# Rows you already have; nodes are created from the links.
$links = @(
@{ From = 'Browser'; To = 'CDN' }
@{ From = 'CDN'; To = 'Web' }
@{ From = 'Web'; To = 'Orders API' }
@{ From = 'Orders API'; To = 'SQL' }
@{ From = 'Web'; To = 'Redis' }
@{ From = 'Orders API'; To = 'Queue' }
@{ From = 'Queue'; To = 'Worker' }
)
New-ConsoleDiagram -Title 'Shop · production' {
foreach ($link in $links) { New-ConsoleDiagramLink @link }
} | Show-ConsoleFull program
Import-Module ConsoleMonster
# Rows you already have; nodes are created from the links.
$links = @(
@{ From = 'Browser'; To = 'CDN' }
@{ From = 'CDN'; To = 'Web' }
@{ From = 'Web'; To = 'Orders API' }
@{ From = 'Orders API'; To = 'SQL' }
@{ From = 'Web'; To = 'Redis' }
@{ From = 'Orders API'; To = 'Queue' }
@{ From = 'Queue'; To = 'Worker' }
)
New-ConsoleDiagram -Title 'Shop · production' {
foreach ($link in $links) { New-ConsoleDiagramLink @link }
} | Show-ConsoleRun: ./diagram-5.ps1
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// Rows you already have; nodes are created from the links.
Link[] links = {
new("Browser", "CDN"),
new("CDN", "Web"),
new("Web", "Orders API"),
new("Orders API", "SQL"),
new("Web", "Redis"),
new("Orders API", "Queue"),
new("Queue", "Worker")
};
Diagram diagram = Diagram.FromLinks(links, link => link.From, link => link.To);
diagram.Title = "Shop · production";
Terminal.Write(diagram);
record Link(string From, string To);Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// Rows you already have; nodes are created from the links.
Link[] links = {
new("Browser", "CDN"),
new("CDN", "Web"),
new("Web", "Orders API"),
new("Orders API", "SQL"),
new("Web", "Redis"),
new("Orders API", "Queue"),
new("Queue", "Worker")
};
Diagram diagram = Diagram.FromLinks(links, link => link.From, link => link.To);
diagram.Title = "Shop · production";
Terminal.Write(diagram);
record Link(string From, string To);Run: dotnet run
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// Rows you already have; nodes are created from the links.
Link[] links =
{
new("Browser", "CDN"),
new("CDN", "Web"),
new("Web", "Orders API"),
new("Orders API", "SQL"),
new("Web", "Redis"),
new("Orders API", "Queue"),
new("Queue", "Worker")
};
Diagram diagram = Diagram.FromLinks(links, link => link.From, link => link.To)
.Title("Shop · production");
diagram.Show();
record Link(string From, string To);
Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// Rows you already have; nodes are created from the links.
Link[] links =
{
new("Browser", "CDN"),
new("CDN", "Web"),
new("Web", "Orders API"),
new("Orders API", "SQL"),
new("Web", "Redis"),
new("Orders API", "Queue"),
new("Queue", "Worker")
};
Diagram diagram = Diagram.FromLinks(links, link => link.From, link => link.To)
.Title("Shop · production");
diagram.Show();
record Link(string From, string To);
Run: dotnet run
Shop · production ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────────┐ ┌────────────┐ ┌────────────┐ │ Browser │ │ CDN │ │ Web │ │ Orders API │──┐ │ Queue │ │ Worker │ │ │ │ │ │ │───▶│ │──┼▶│ │──┐ │ │ │ │───▶│ │───▶│ │ └─────────────┘ │ └────────────┘ └▶│ │ │ │ │ │ │ │──┐ │ │ │ │ │ │ │ │ │ │ │ │ │ └────────────┘ └────────────┘ └────────────┘ │ ┌─────────────┐ │ ┌────────────┐ └────────────┘ │ │ Redis │ │ │ SQL │ └▶│ │ └▶│ │ └─────────────┘ └────────────┘
Shop · production ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────────┐ ┌────────────┐ ┌────────────┐ │ Browser │ │ CDN │ │ Web │ │ Orders API │──┐ │ Queue │ │ Worker │ │ │ │ │ │ │───▶│ │──┼▶│ │──┐ │ │ │ │───▶│ │───▶│ │ └─────────────┘ │ └────────────┘ └▶│ │ │ │ │ │ │ │──┐ │ │ │ │ │ │ │ │ │ │ │ │ │ └────────────┘ └────────────┘ └────────────┘ │ ┌─────────────┐ │ ┌────────────┐ └────────────┘ │ │ Redis │ │ │ SQL │ └▶│ │ └▶│ │ └─────────────┘ └────────────┘
6. Live values
Give the diagram a widget id and update it while it shows; values and statuses change, the layout stays put. This frame is one moment of a ping monitor. The live version wraps the same diagram in a dashboard:
$diagram = New-ConsoleDiagram -WidgetId ping -Title 'Ping monitor · every 5 s' { <# the nodes above #> }
Invoke-ConsoleDashboard $diagram {
while ($true) {
foreach ($name in 'Router', 'DNS', 'VPN', 'contoso.com') {
$ping = Test-Connection $name -Count 1 -TimeoutSeconds 1 -ErrorAction Ignore
$status = if (-not $ping) { 'Fail' } elseif ($ping.Latency -gt 100) { 'Warn' } else { 'Pass' }
$value = if ($ping) { "$($ping.Latency) ms" } else { 'timeout' }
Update-Console ping -Key $name -Value $value -Status $status
}
Start-Sleep -Seconds 5
}
}
Install-Module ConsoleMonster -Scope CurrentUser# The values a live session would keep updating; the layout never moves.
New-ConsoleDiagram -Title 'Ping monitor · every 5 s' {
New-ConsoleDiagramNode 'This PC' -Kind Workstation -To 'Router'
New-ConsoleDiagramNode 'Router' -Kind Gateway -Value '1 ms' -To 'DNS', 'VPN', 'contoso.com'
New-ConsoleDiagramNode 'DNS' -Kind Server -Subtitle '1.1.1.1' -Value '12 ms'
New-ConsoleDiagramNode 'VPN' -Kind Server -Subtitle '10.8.0.1' -Status Warn -Value '180 ms'
New-ConsoleDiagramNode 'contoso.com' -Kind Cloud -Status Fail -Value 'timeout'
} | Show-ConsoleFull program
Import-Module ConsoleMonster
# The values a live session would keep updating; the layout never moves.
New-ConsoleDiagram -Title 'Ping monitor · every 5 s' {
New-ConsoleDiagramNode 'This PC' -Kind Workstation -To 'Router'
New-ConsoleDiagramNode 'Router' -Kind Gateway -Value '1 ms' -To 'DNS', 'VPN', 'contoso.com'
New-ConsoleDiagramNode 'DNS' -Kind Server -Subtitle '1.1.1.1' -Value '12 ms'
New-ConsoleDiagramNode 'VPN' -Kind Server -Subtitle '10.8.0.1' -Status Warn -Value '180 ms'
New-ConsoleDiagramNode 'contoso.com' -Kind Cloud -Status Fail -Value 'timeout'
} | Show-ConsoleRun: ./diagram-6.ps1
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// The values a live session would keep updating; the layout never moves.
var diagram = new Diagram("Ping monitor · every 5 s") {
Nodes = {
new DiagramNode("This PC") {
Kind = DiagramNodeKind.Workstation,
To = { "Router" }
},
new DiagramNode("Router") {
Kind = DiagramNodeKind.Gateway,
Value = "1 ms",
To = { "DNS", "VPN", "contoso.com" }
},
new DiagramNode("DNS") {
Kind = DiagramNodeKind.Server,
Subtitle = "1.1.1.1",
Value = "12 ms"
},
new DiagramNode("VPN") {
Kind = DiagramNodeKind.Server,
Subtitle = "10.8.0.1",
Status = StatusKind.Warn,
Value = "180 ms"
},
new DiagramNode("contoso.com") {
Kind = DiagramNodeKind.Cloud,
Status = StatusKind.Fail,
Value = "timeout"
}
}
};
Terminal.Write(diagram);Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Styling;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// The values a live session would keep updating; the layout never moves.
var diagram = new Diagram("Ping monitor · every 5 s") {
Nodes = {
new DiagramNode("This PC") {
Kind = DiagramNodeKind.Workstation,
To = { "Router" }
},
new DiagramNode("Router") {
Kind = DiagramNodeKind.Gateway,
Value = "1 ms",
To = { "DNS", "VPN", "contoso.com" }
},
new DiagramNode("DNS") {
Kind = DiagramNodeKind.Server,
Subtitle = "1.1.1.1",
Value = "12 ms"
},
new DiagramNode("VPN") {
Kind = DiagramNodeKind.Server,
Subtitle = "10.8.0.1",
Status = StatusKind.Warn,
Value = "180 ms"
},
new DiagramNode("contoso.com") {
Kind = DiagramNodeKind.Cloud,
Status = StatusKind.Fail,
Value = "timeout"
}
}
};
Terminal.Write(diagram);Run: dotnet run
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// The values a live session would keep updating; the layout never moves.
var diagram = Ui.Diagram()
.Title("Ping monitor · every 5 s")
.AddNode(new DiagramNode("This PC") { Kind = DiagramNodeKind.Workstation, To = { "Router" } })
.AddNode(new DiagramNode("Router")
{
Kind = DiagramNodeKind.Gateway,
Value = "1 ms",
To = { "DNS", "VPN", "contoso.com" }
})
.AddNode(new DiagramNode("DNS").Kind(DiagramNodeKind.Server).Subtitle("1.1.1.1").Value("12 ms"))
.AddNode(new DiagramNode("VPN")
.Kind(DiagramNodeKind.Server)
.Subtitle("10.8.0.1")
.Status(StatusKind.Warn)
.Value("180 ms"))
.AddNode(new DiagramNode("contoso.com")
.Kind(DiagramNodeKind.Cloud)
.Status(StatusKind.Fail)
.Value("timeout"));
diagram.Show();
Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Styling;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// The values a live session would keep updating; the layout never moves.
var diagram = Ui.Diagram()
.Title("Ping monitor · every 5 s")
.AddNode(new DiagramNode("This PC") { Kind = DiagramNodeKind.Workstation, To = { "Router" } })
.AddNode(new DiagramNode("Router")
{
Kind = DiagramNodeKind.Gateway,
Value = "1 ms",
To = { "DNS", "VPN", "contoso.com" }
})
.AddNode(new DiagramNode("DNS").Kind(DiagramNodeKind.Server).Subtitle("1.1.1.1").Value("12 ms"))
.AddNode(new DiagramNode("VPN")
.Kind(DiagramNodeKind.Server)
.Subtitle("10.8.0.1")
.Status(StatusKind.Warn)
.Value("180 ms"))
.AddNode(new DiagramNode("contoso.com")
.Kind(DiagramNodeKind.Cloud)
.Status(StatusKind.Fail)
.Value("timeout"));
diagram.Show();
Run: dotnet run
Ping monitor · every 5 s ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ This PC │ │ Router │ │ DNS │ │ contoso.com ✗│ │ │ │ 1 ms │───▶│ 1.1.1.1 │ │ timeout │ │ │ │ │ │ 12 ms │ │ │ │ │───▶│ │──┐ └────────────┘ ┌▶│ │ │ │ │ │──┼─────────────────┘ │ │ │ │ │ │ │ │ │ └────────────┘ └────────────┘ │ ┌────────────┐ └──────────────┘ │ │ VPN ▴│ └▶│ 10.8.0.1 │ │ 180 ms │ └────────────┘ ▴ Warn ✗ Fail Needs attention ▴ VPN 180 ms ✗ contoso.com timeout
Ping monitor · every 5 s ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ This PC │ │ Router │ │ DNS │ │ contoso.com ✗│ │ │ │ 1 ms │───▶│ 1.1.1.1 │ │ timeout │ │ │ │ │ │ 12 ms │ │ │ │ │───▶│ │──┐ └────────────┘ ┌▶│ │ │ │ │ │──┼─────────────────┘ │ │ │ │ │ │ │ │ │ └────────────┘ └────────────┘ │ ┌────────────┐ └──────────────┘ │ │ VPN ▴│ └▶│ 10.8.0.1 │ │ 180 ms │ └────────────┘ ▴ Warn ✗ Fail Needs attention ▴ VPN 180 ms ✗ contoso.com timeout
7. A request walkthrough
A use case walks a request through the diagram, step by step: requests, replies, messages nobody waits for and calls that fail. The active step lights up and the panel underneath lists the steps. In an interactive console, Show-ConsoleDiagram or diagram.Run() lets Space play it and the arrow keys step through it.
Install-Module ConsoleMonster -Scope CurrentUser# A use case walks a request through the diagram; this shows its third step.
Show-ConsoleDiagram -Title 'Checkout' -Step 3 -StaticSnapshot {
New-ConsoleDiagramNode 'Shopper' -Kind User -To 'Web'
New-ConsoleDiagramNode 'Web' -Kind Service -To 'Orders API'
New-ConsoleDiagramNode 'Orders API' -Kind Service -To 'Payments', 'SQL'
New-ConsoleDiagramNode 'Payments' -Kind External -Subtitle 'Stripe'
New-ConsoleDiagramNode 'SQL' -Kind Database
New-ConsoleDiagramUseCase 'Place an order' {
New-ConsoleDiagramStep 'Shopper' 'Web' 'POST /checkout'
New-ConsoleDiagramStep 'Web' 'Orders API' 'create order'
New-ConsoleDiagramStep 'Orders API' 'Payments' 'charge 24.00 EUR'
New-ConsoleDiagramStep 'Payments' 'Orders API' 'approved' -Reply
New-ConsoleDiagramStep 'Orders API' 'SQL' 'INSERT order'
New-ConsoleDiagramStep 'Orders API' 'Web' '201 order 1042' -Reply
New-ConsoleDiagramStep 'Web' 'Shopper' 'Thank you page' -Reply
}
}Full program
Import-Module ConsoleMonster
# A use case walks a request through the diagram; this shows its third step.
Show-ConsoleDiagram -Title 'Checkout' -Step 3 -StaticSnapshot {
New-ConsoleDiagramNode 'Shopper' -Kind User -To 'Web'
New-ConsoleDiagramNode 'Web' -Kind Service -To 'Orders API'
New-ConsoleDiagramNode 'Orders API' -Kind Service -To 'Payments', 'SQL'
New-ConsoleDiagramNode 'Payments' -Kind External -Subtitle 'Stripe'
New-ConsoleDiagramNode 'SQL' -Kind Database
New-ConsoleDiagramUseCase 'Place an order' {
New-ConsoleDiagramStep 'Shopper' 'Web' 'POST /checkout'
New-ConsoleDiagramStep 'Web' 'Orders API' 'create order'
New-ConsoleDiagramStep 'Orders API' 'Payments' 'charge 24.00 EUR'
New-ConsoleDiagramStep 'Payments' 'Orders API' 'approved' -Reply
New-ConsoleDiagramStep 'Orders API' 'SQL' 'INSERT order'
New-ConsoleDiagramStep 'Orders API' 'Web' '201 order 1042' -Reply
New-ConsoleDiagramStep 'Web' 'Shopper' 'Thank you page' -Reply
}
}Run: ./diagram-7.ps1
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// A use case walks a request through the diagram; this shows its third step.
var diagram = new Diagram("Checkout") {
Nodes = {
new DiagramNode("Shopper") { Kind = DiagramNodeKind.User, To = { "Web" } },
new DiagramNode("Web") { Kind = DiagramNodeKind.Service, To = { "Orders API" } },
new DiagramNode("Orders API") {
Kind = DiagramNodeKind.Service,
To = { "Payments", "SQL" }
},
new DiagramNode("Payments") {
Kind = DiagramNodeKind.External,
Subtitle = "Stripe"
},
new DiagramNode("SQL") { Kind = DiagramNodeKind.Database }
},
UseCases = {
new DiagramUseCase("Place an order") {
Steps = {
new DiagramStep("Shopper", "Web", "POST /checkout"),
new DiagramStep("Web", "Orders API", "create order"),
new DiagramStep("Orders API", "Payments", "charge 24.00 EUR"),
new DiagramStep("Payments", "Orders API", "approved") {
Kind = DiagramStepKind.Reply
},
new DiagramStep("Orders API", "SQL", "INSERT order"),
new DiagramStep("Orders API", "Web", "201 order 1042") {
Kind = DiagramStepKind.Reply
},
new DiagramStep("Web", "Shopper", "Thank you page") {
Kind = DiagramStepKind.Reply
}
}
}
}
};
diagram.Step(2);
Terminal.Write(diagram);Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// A use case walks a request through the diagram; this shows its third step.
var diagram = new Diagram("Checkout") {
Nodes = {
new DiagramNode("Shopper") { Kind = DiagramNodeKind.User, To = { "Web" } },
new DiagramNode("Web") { Kind = DiagramNodeKind.Service, To = { "Orders API" } },
new DiagramNode("Orders API") {
Kind = DiagramNodeKind.Service,
To = { "Payments", "SQL" }
},
new DiagramNode("Payments") {
Kind = DiagramNodeKind.External,
Subtitle = "Stripe"
},
new DiagramNode("SQL") { Kind = DiagramNodeKind.Database }
},
UseCases = {
new DiagramUseCase("Place an order") {
Steps = {
new DiagramStep("Shopper", "Web", "POST /checkout"),
new DiagramStep("Web", "Orders API", "create order"),
new DiagramStep("Orders API", "Payments", "charge 24.00 EUR"),
new DiagramStep("Payments", "Orders API", "approved") {
Kind = DiagramStepKind.Reply
},
new DiagramStep("Orders API", "SQL", "INSERT order"),
new DiagramStep("Orders API", "Web", "201 order 1042") {
Kind = DiagramStepKind.Reply
},
new DiagramStep("Web", "Shopper", "Thank you page") {
Kind = DiagramStepKind.Reply
}
}
}
}
};
diagram.Step(2);
Terminal.Write(diagram);Run: dotnet run
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// A use case walks a request through the diagram; this shows its third step.
var diagram = Ui.Diagram()
.Title("Checkout")
.AddNode(new DiagramNode("Shopper") { Kind = DiagramNodeKind.User, To = { "Web" } })
.AddNode(new DiagramNode("Web") { Kind = DiagramNodeKind.Service, To = { "Orders API" } })
.AddNode(new DiagramNode("Orders API")
{
Kind = DiagramNodeKind.Service,
To = { "Payments", "SQL" }
})
.AddNode(new DiagramNode("Payments").Kind(DiagramNodeKind.External).Subtitle("Stripe"))
.AddNode(new DiagramNode("SQL").Kind(DiagramNodeKind.Database))
.AddUseCase(new DiagramUseCase("Place an order") { Steps =
{
new DiagramStep("Shopper", "Web", "POST /checkout"),
new DiagramStep("Web", "Orders API", "create order"),
new DiagramStep("Orders API", "Payments", "charge 24.00 EUR"),
new DiagramStep("Payments", "Orders API", "approved").Kind(DiagramStepKind.Reply),
new DiagramStep("Orders API", "SQL", "INSERT order"),
new DiagramStep("Orders API", "Web", "201 order 1042").Kind(DiagramStepKind.Reply),
new DiagramStep("Web", "Shopper", "Thank you page").Kind(DiagramStepKind.Reply)
} });
diagram.Step(2);
diagram.Show();
Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// A use case walks a request through the diagram; this shows its third step.
var diagram = Ui.Diagram()
.Title("Checkout")
.AddNode(new DiagramNode("Shopper") { Kind = DiagramNodeKind.User, To = { "Web" } })
.AddNode(new DiagramNode("Web") { Kind = DiagramNodeKind.Service, To = { "Orders API" } })
.AddNode(new DiagramNode("Orders API")
{
Kind = DiagramNodeKind.Service,
To = { "Payments", "SQL" }
})
.AddNode(new DiagramNode("Payments").Kind(DiagramNodeKind.External).Subtitle("Stripe"))
.AddNode(new DiagramNode("SQL").Kind(DiagramNodeKind.Database))
.AddUseCase(new DiagramUseCase("Place an order") { Steps =
{
new DiagramStep("Shopper", "Web", "POST /checkout"),
new DiagramStep("Web", "Orders API", "create order"),
new DiagramStep("Orders API", "Payments", "charge 24.00 EUR"),
new DiagramStep("Payments", "Orders API", "approved").Kind(DiagramStepKind.Reply),
new DiagramStep("Orders API", "SQL", "INSERT order"),
new DiagramStep("Orders API", "Web", "201 order 1042").Kind(DiagramStepKind.Reply),
new DiagramStep("Web", "Shopper", "Thank you page").Kind(DiagramStepKind.Reply)
} });
diagram.Step(2);
diagram.Show();
Run: dotnet run
Checkout ┌────────────┐ ┌────────────┐ ┏━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━┓ │ Shopper │ │ Web │ ┃ Orders API ┃ ┃ Payments ┃ │ │ │ │ ┃ ┃ ┏▶┃ Stripe ┃ │ │ │ │ ┃ ┃━━┛ ┃ ┃ │ │───▶│ │───▶┃ ┃ ┗━━━━━━━━━━━━┛ │ │ │ │ ┃ ┃──┐ │ │ │ │ ┃ ┃ │ └────────────┘ └────────────┘ ┗━━━━━━━━━━━━━┛ │ ┌────────────┐ │ │ SQL │ └▶│ │ └────────────┘ Place an order · step 3 of 7 1. Shopper → Web request POST /checkout 2. Web → Orders API request create order ▶ 3. Orders API → Payments request charge 24.00 EUR 4. Payments → Orders API response approved 5. Orders API → SQL request INSERT order 6. Orders API → Web response 201 order 1042 7. Web → Shopper response Thank you page
Checkout ┌────────────┐ ┌────────────┐ ┏━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━┓ │ Shopper │ │ Web │ ┃ Orders API ┃ ┃ Payments ┃ │ │ │ │ ┃ ┃ ┏▶┃ Stripe ┃ │ │ │ │ ┃ ┃━━┛ ┃ ┃ │ │───▶│ │───▶┃ ┃ ┗━━━━━━━━━━━━┛ │ │ │ │ ┃ ┃──┐ │ │ │ │ ┃ ┃ │ └────────────┘ └────────────┘ ┗━━━━━━━━━━━━━┛ │ ┌────────────┐ │ │ SQL │ └▶│ │ └────────────┘ Place an order · step 3 of 7 1. Shopper → Web request POST /checkout 2. Web → Orders API request create order ▶ 3. Orders API → Payments request charge 24.00 EUR 4. Payments → Orders API response approved 5. Orders API → SQL request INSERT order 6. Orders API → Web response 201 order 1042 7. Web → Shopper response Thank you page
8. Hierarchy
Links that form a tree are a hierarchy: an org chart, a folder tree, a dependency tree. Direction = Down lays it out top to bottom.
Install-Module ConsoleMonster -Scope CurrentUser# Links that form a tree become a hierarchy, laid out top to bottom.
New-ConsoleDiagram -Title 'IT team' -Direction Down {
New-ConsoleDiagramNode 'Anna' -Kind Person -Subtitle 'CIO' -To 'Marek', 'Julia'
New-ConsoleDiagramNode 'Marek' -Kind Person -Subtitle 'Operations' -To 'Tom', 'Ola'
New-ConsoleDiagramNode 'Julia' -Kind Person -Subtitle 'Development' -To 'Ben'
New-ConsoleDiagramNode 'Tom' -Kind Person -Subtitle 'Service desk'
New-ConsoleDiagramNode 'Ola' -Kind Person -Subtitle 'Network'
New-ConsoleDiagramNode 'Ben' -Kind Person -Subtitle 'Apps'
} | Show-ConsoleFull program
Import-Module ConsoleMonster
# Links that form a tree become a hierarchy, laid out top to bottom.
New-ConsoleDiagram -Title 'IT team' -Direction Down {
New-ConsoleDiagramNode 'Anna' -Kind Person -Subtitle 'CIO' -To 'Marek', 'Julia'
New-ConsoleDiagramNode 'Marek' -Kind Person -Subtitle 'Operations' -To 'Tom', 'Ola'
New-ConsoleDiagramNode 'Julia' -Kind Person -Subtitle 'Development' -To 'Ben'
New-ConsoleDiagramNode 'Tom' -Kind Person -Subtitle 'Service desk'
New-ConsoleDiagramNode 'Ola' -Kind Person -Subtitle 'Network'
New-ConsoleDiagramNode 'Ben' -Kind Person -Subtitle 'Apps'
} | Show-ConsoleRun: ./diagram-8.ps1
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// Links that form a tree become a hierarchy, laid out top to bottom.
var diagram = new Diagram("IT team") {
Direction = DiagramDirection.Down,
Nodes = {
new DiagramNode("Anna") {
Kind = DiagramNodeKind.Person,
Subtitle = "CIO",
To = { "Marek", "Julia" }
},
new DiagramNode("Marek") {
Kind = DiagramNodeKind.Person,
Subtitle = "Operations",
To = { "Tom", "Ola" }
},
new DiagramNode("Julia") {
Kind = DiagramNodeKind.Person,
Subtitle = "Development",
To = { "Ben" }
},
new DiagramNode("Tom") { Kind = DiagramNodeKind.Person, Subtitle = "Service desk" },
new DiagramNode("Ola") { Kind = DiagramNodeKind.Person, Subtitle = "Network" },
new DiagramNode("Ben") { Kind = DiagramNodeKind.Person, Subtitle = "Apps" }
}
};
Terminal.Write(diagram);Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// Links that form a tree become a hierarchy, laid out top to bottom.
var diagram = new Diagram("IT team") {
Direction = DiagramDirection.Down,
Nodes = {
new DiagramNode("Anna") {
Kind = DiagramNodeKind.Person,
Subtitle = "CIO",
To = { "Marek", "Julia" }
},
new DiagramNode("Marek") {
Kind = DiagramNodeKind.Person,
Subtitle = "Operations",
To = { "Tom", "Ola" }
},
new DiagramNode("Julia") {
Kind = DiagramNodeKind.Person,
Subtitle = "Development",
To = { "Ben" }
},
new DiagramNode("Tom") { Kind = DiagramNodeKind.Person, Subtitle = "Service desk" },
new DiagramNode("Ola") { Kind = DiagramNodeKind.Person, Subtitle = "Network" },
new DiagramNode("Ben") { Kind = DiagramNodeKind.Person, Subtitle = "Apps" }
}
};
Terminal.Write(diagram);Run: dotnet run
dotnet new console -n Diagrams
cd Diagrams
dotnet add package ConsoleMonster
dotnet add package ConsoleMonster.GraphicsDiagramRendererProvider.Register();
// Links that form a tree become a hierarchy, laid out top to bottom.
var diagram = Ui.Diagram()
.Title("IT team")
.Direction(DiagramDirection.Down)
.AddNode(new DiagramNode("Anna")
{
Kind = DiagramNodeKind.Person,
Subtitle = "CIO",
To = { "Marek", "Julia" }
})
.AddNode(new DiagramNode("Marek")
{
Kind = DiagramNodeKind.Person,
Subtitle = "Operations",
To = { "Tom", "Ola" }
})
.AddNode(new DiagramNode("Julia")
{
Kind = DiagramNodeKind.Person,
Subtitle = "Development",
To = { "Ben" }
})
.AddNode(new DiagramNode("Tom").Kind(DiagramNodeKind.Person).Subtitle("Service desk"))
.AddNode(new DiagramNode("Ola").Kind(DiagramNodeKind.Person).Subtitle("Network"))
.AddNode(new DiagramNode("Ben").Kind(DiagramNodeKind.Person).Subtitle("Apps"));
diagram.Show();
Full program
using ConsoleMonster;
using ConsoleMonster.Graphics;
using ConsoleMonster.Widgets;
DiagramRendererProvider.Register();
// Links that form a tree become a hierarchy, laid out top to bottom.
var diagram = Ui.Diagram()
.Title("IT team")
.Direction(DiagramDirection.Down)
.AddNode(new DiagramNode("Anna")
{
Kind = DiagramNodeKind.Person,
Subtitle = "CIO",
To = { "Marek", "Julia" }
})
.AddNode(new DiagramNode("Marek")
{
Kind = DiagramNodeKind.Person,
Subtitle = "Operations",
To = { "Tom", "Ola" }
})
.AddNode(new DiagramNode("Julia")
{
Kind = DiagramNodeKind.Person,
Subtitle = "Development",
To = { "Ben" }
})
.AddNode(new DiagramNode("Tom").Kind(DiagramNodeKind.Person).Subtitle("Service desk"))
.AddNode(new DiagramNode("Ola").Kind(DiagramNodeKind.Person).Subtitle("Network"))
.AddNode(new DiagramNode("Ben").Kind(DiagramNodeKind.Person).Subtitle("Apps"));
diagram.Show();
Run: dotnet run
IT team ┌────────────┐ │ Anna │ │ CIO │ │ │ └────────────┘ ┌─────────────┴───┴──┐ ▼ ▼ ┌─────────────────┐ ┌────────────┐ │ Julia │ │ Marek │ │ Development │ │ Operations │ │ │ │ │ └─────────────────┘ └────────────┘ ┌─┘ ┌──┴───┴───────────┐ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ Ben │ │ Ola │ │ Tom │ │ Apps │ │ Network │ │ Service desk │ │ │ │ │ │ │ └────────────┘ └────────────┘ └──────────────┘
IT team ┌────────────┐ │ Anna │ │ CIO │ │ │ └────────────┘ ┌─────────────┴───┴──┐ ▼ ▼ ┌─────────────────┐ ┌────────────┐ │ Julia │ │ Marek │ │ Development │ │ Operations │ │ │ │ │ └─────────────────┘ └────────────┘ ┌─┘ ┌──┴───┴───────────┐ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ Ben │ │ Ola │ │ Tom │ │ Apps │ │ Network │ │ Service desk │ │ │ │ │ │ │ └────────────┘ └────────────┘ └──────────────┘
Next
Architecture diagrams loads ChartForgeX architecture files and plays their use cases, and live dashboards shows how a diagram becomes part of a running dashboard.