Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 109 additions & 46 deletions PITCHME.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
# PSGraph

PSGraph is a PowerShell module that allows you to script the generation of graphs using the GraphViz engine. It makes it easy to produce data driven visualizations.
PSGraph is a PowerShell module that lets you script the generation of graphs using the GraphViz engine. It makes it easy to produce data-driven visualizations straight from PowerShell objects.

![basic graph](https://kevinmarquette.github.io/img/basic.png)
![basic graph](images/firstGraph.png)

---
### Install GraphViz from the Chocolatey repo

Register-PackageSource -Name Chocolatey -ProviderName Chocolatey -Location http://chocolatey.org/api/v2/
Find-Package graphviz | Install-Package -ForceBootstrap

### Install PSGraph from the Powershell Gallery
### Install PSGraph from the PowerShell Gallery

Find-Module PSGraph | Install-Module
Import-Module PSGraph

### Import Module
### Install GraphViz

Import-Module PSGraph
# Chocolatey on Windows (nuget.org fallback for non-admin installs),
# Homebrew on macOS, your distro's package manager on Linux
Install-GraphViz

---

Expand All @@ -40,79 +38,144 @@ Then we can render the graph as an image.
Edge -From middle -To end
} | Export-PSGraph -ShowGraph


![firstGraph](http://psgraph.readthedocs.io/en/latest/images/firstGraph.png)
![firstGraph](images/firstGraph.png)

---

### Data driven graphs

The real fun starts when they are data driven
The real fun starts when they are data driven — every example below pulls its shape from real PowerShell objects, not hand-typed node names.

---

### Example: Server farm data
### Example: Server farm topology

Imagine you wanted to diagram a server farm.
Describe how tiers of servers relate to each other.

$WebServer = 1..2 | ForEach-Object {"Web_$_"}
$APIServer = 1..2 | ForEach-Object {"API_$_"}
$DatabaseServer = 1..2 | ForEach-Object {"DB_$_"}

graph servers {
node @{shape='box'}
edge LoadBalancer -To $WebServer
edge $WebServer -To $APIServer
edge $APIServer -To AvailabilityGroup
edge AvailabilityGroup -To $DatabaseServer
} | Export-PSGraph -ShowGraph

I'm generating example servers here:
![servers](images/pitchme-serverfarm.png)

# Server counts
$WebServerCount = 2
$APIServerCount = 2
$DatabaseServerCount = 2
---

# Server lists
$WebServer = 1..$WebServerCount | % {"Web_$_"}
$APIServer = 1..$APIServerCount | % {"API_$_"}
$DatabaseServer = 1..$DatabaseServerCount | % {"DB_$_"}
### Example: Database schema

`Record`/`Row`/`Cells` build GraphViz's HTML-like table nodes — a natural fit for entity-relationship diagrams. `Cells -PortProperty` names a row so `Edge` can point straight at it.

$customers = @(
[pscustomobject]@{ Column='Id'; Type='int PK' }
[pscustomobject]@{ Column='Name'; Type='nvarchar' }
[pscustomobject]@{ Column='Email'; Type='nvarchar' }
)
$orders = @(
[pscustomobject]@{ Column='Id'; Type='int PK' }
[pscustomobject]@{ Column='CustomerId'; Type='int FK' }
[pscustomobject]@{ Column='Total'; Type='money' }
)

graph schema {
Record Customers -Rows ($customers | Cells -PortProperty Column)
Record Orders -Rows ($orders | Cells -PortProperty Column)
Edge 'Orders:CustomerId' -To 'Customers:Id'
} | Export-PSGraph -ShowGraph

But you could source these from AD or your CMDB
![schema](images/pitchme-schema.png)

---

### Example: Server farm graph
### Example: Live process tree

Then describe how those lists of servers are related
Graph what's actually running right now, color-coded by memory use via `New-NodeAttributeSet`.

graph servers {
node -Default @{shape='box'}
edge LoadBalancer -To $WebServer
edge $WebServer -To $APIServer
edge $APIServer -To AvailabilityGroup
edge AvailabilityGroup -To $DatabaseServer
$all = Get-Process
$procs = $all | Where-Object {
$_.Id -ne 0 -and $_.Parent -and ($all.Id -contains $_.Parent.Id)
}

graph processTree @{rankdir='LR'} {
$procs | ForEach-Object {
$color = if ($_.WorkingSet64 -gt 200MB) {'orangered'}
elseif ($_.WorkingSet64 -gt 50MB) {'gold'}
else {'palegreen'}
$attrs = New-NodeAttributeSet -Style filled -FillColor $color
$attrs.label = $_.ProcessName
node $_.Id $attrs
}
edge $procs -FromScript {$_.Parent.Id} -ToScript {$_.Id}
} | Export-PSGraph -ShowGraph

![process tree](images/pitchme-processtree.png)

---

### Example: Server farm graph image
### Example: Windows service dependencies

`Get-Service` already exposes each service's dependency graph — PSGraph just draws it.

$services = Get-Service | Where-Object RequiredServices

graph serviceDeps @{rankdir='LR'} {
node @{shape='box'}
$services | ForEach-Object {
edge $_.Name -To $_.RequiredServices.Name
}
} | Export-PSGraph -ShowGraph

![servers](https://kevinmarquette.github.io/img/servers.png)
![service dependencies](images/pitchme-servicedeps.png)

---

### Example: Project structures
### Example: PowerShell module dependencies

![files structure](http://psgraph.readthedocs.io/en/latest/images/filesSmall.png)
Dogfooding: walk installed modules' own `RequiredModules` and graph them.

$modules = Get-Module -ListAvailable | Where-Object RequiredModules

graph moduleDeps @{rankdir='LR'} {
node @{shape='box'}
$modules | ForEach-Object {
edge $_.Name -To $_.RequiredModules.Name
}
} | Export-PSGraph -ShowGraph

![module dependencies](images/pitchme-moduledeps.png)

---

### Example: Parent and child processes
### Example: Export to any format in one line

![related processes](http://psgraph.readthedocs.io/en/latest/images/processSmall.png)
`Export-PSGraph` ships a format-specific alias for every supported output — `svgGraph`, `pngGraph`, `pdfGraph`, `dotGraph`, and more.

$dot = graph g { edge hello world }

$dot | svgGraph -Destination out.svg
$dot | pngGraph -Destination out.png
$dot | pdfGraph -Destination out.pdf

![formats](images/pitchme-formats.png)

---

### Example: Network connections
### More examples

![network connections](http://psgraph.readthedocs.io/en/latest/images/networkConnection.png)
* [Project structure](images/filesSmall.png) — a folder tree walked with `Get-ChildItem`
* [GraphViz gallery recreations](https://github.com/themodulecollective/PSGraph/blob/main/docs/Example-Gallery.md) — clusters, entity-relation diagrams, finite automata
* Full command reference and more scripted examples: [psgraph.readthedocs.io](http://psgraph.readthedocs.io)

---

### What will you graph?

For more information

* [psgraph.readthedocs.io](http://psgraph.readthedocs.io)
* [github.com/kevinmarquette/psgraph](https://github.com/kevinmarquette/psgraph)
* [kevinmarquette.github.io](https://kevinmarquette.github.io)
* [psgraph.readthedocs.io](http://psgraph.readthedocs.io) — full documentation
* [github.com/themodulecollective/PSGraph](https://github.com/themodulecollective/PSGraph) — source, issues, and this fork's changelog
* `Get-Help about_PSGraph` — conceptual overview, right from your PowerShell prompt
1 change: 0 additions & 1 deletion PITCHME.yaml

This file was deleted.

133 changes: 133 additions & 0 deletions PSGraph/en-US/about_PSGraph.help.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
TOPIC
about_PSGraph

SHORT DESCRIPTION
PSGraph is a small DSL (Domain Specific Language) for generating
GraphViz graphs from PowerShell. It turns PowerShell objects and
collections into GraphViz's DOT text, then optionally renders that
text into an image.

LONG DESCRIPTION
PSGraph does not parse anything of its own. `Graph { ... }` is
ordinary PowerShell: the `{ ... }` is a scriptblock literal, and
`Graph` runs it directly with `& $ScriptBlock`. Every command called
inside that block - `Node`, `Edge`, `Rank`, `SubGraph`, and the rest -
is just an ordinary function call that happens to emit a line of DOT
text as its pipeline output. `Graph` collects that output, wraps it
in `digraph { ... }`, and returns the whole thing as a string array.

Because indentation and cluster nesting have to be shared between
calls that have no direct reference to one another, PSGraph tracks
them with a small amount of module-scoped state (current indent
depth, the active subgraph list) that gets set when a `Graph` block
opens and cleared when it closes. You do not need to manage this
yourself; it only matters if you are reading the source.

The result of a `Graph` block is plain text in the DOT language, so
it can be inspected, saved, or piped straight into `Export-PSGraph`
to render an image with GraphViz.

COMMANDS

Graph (alias DiGraph)
The top-level container. Opens a graph, runs the scriptblock
that defines its contents, and closes it.

Node
Declares one or more nodes and their attributes.

Edge
Declares an edge (or a chain, or a cross-product of edges)
between nodes.

SubGraph
A graph nested inside another graph, for clustering related
nodes together.

Rank
Places the given nodes at the same level in the layout.

Inline
Passes raw DOT text through untouched, for GraphViz syntax
PSGraph's DSL does not model directly.

Record, Row, Cells, Entity
Build GraphViz's HTML-like table nodes. `Record` is the table;
`Row` is one hand-built row; `Cells` converts a whole collection
of pipeline objects into rows at once; `Entity` converts a
single object into a `Record` automatically.

New-NodeAttributeSet (alias NodeAttributes)
New-EdgeAttributeSet (alias EdgeAttributes)
Build a case-correct GraphViz attribute hashtable from
parameters, with tab completion for shape, color, and font
values - GraphViz attribute names and values are case-sensitive,
and these catch mistakes before they reach GraphViz.

Export-PSGraph (aliases pngGraph, svgGraph, pdfGraph, dotGraph, ...)
Shells out to GraphViz's `dot` executable to render DOT source
into an image. Each supported output format also has its own
alias, e.g. `$dot | svgGraph -Destination out.svg`.

Show-PSGraph
Shorthand for `Export-PSGraph -ShowGraph`.

Set-NodeFormatScript
Sets a scriptblock used to reformat every node/edge ID PSGraph
emits, for cases where your source data's names need cleanup
before they become DOT identifiers.

Install-GraphViz
Installs the native GraphViz binaries PSGraph shells out to
(Chocolatey on Windows, with a nuget.org fallback for non-admin
installs via -Scope CurrentUser; Homebrew on macOS).

Run `Get-Help <command> -Full` for any of these, e.g.
`Get-Help Record -Full`, for parameters and examples.

A NOTE ON THE 'Node' COMMAND NAME

The bare `Node` command name can collide with Node.js-related
tooling or autoload behavior present on some systems. This has been
discussed upstream for years with no consensus reached, and is an
intentionally deferred design question, not an oversight: renaming
or aliasing `Node` would be a breaking change to the DSL and will
only happen as part of a deliberate major-version change with its
own migration notes.

EXAMPLES
# A minimal graph, captured to a variable
$dot = Graph {
Edge hello world
}

# The same graph, rendered and shown immediately
Graph {
Edge hello world
} | Export-PSGraph -ShowGraph

# Data-driven: build nodes and edges from real objects
$processes = Get-Process | Select-Object -First 10
Graph @{rankdir = 'LR'} {
node @{shape = 'box'}
node $processes -NodeScript {$_.Id} -Attributes @{label = {$_.ProcessName}}
edge $processes -FromScript {$_.Parent.Id} -ToScript {$_.Id}
} | Show-PSGraph

SEE ALSO
Full command reference and more examples:
http://psgraph.readthedocs.io

Source, issues, and this fork's changelog:
https://github.com/themodulecollective/PSGraph

GraphViz's own documentation, for DOT-language and attribute details
PSGraph does not wrap in a dedicated parameter:
https://graphviz.org/documentation/

KEYWORDS
PSGraph
GraphViz
DOT
Graph
DSL
38 changes: 38 additions & 0 deletions docs/Command-Cells.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Cells

`Cells` converts pipeline objects into GraphViz HTML-like table rows (`<TR>...</TR>`), one row per object. It exists to pair with `Record`: pipe any collection straight into a table node without hand-building `Row` calls for each property.

Get-Process | Select-Object -First 3 Name, Id |
Cells | Record Processes | Show-PSGraph

By default the first object's property names become a bold header row, and every property after that becomes one `<TD>` per row.

## Cells [-Properties [string[]]] [-ExcludeProperty [string[]]]

Filter which properties become columns, and in what order. Both accept wildcards.

Get-Process | Cells -Properties Name, Id, CPU

Get-Process | Cells -ExcludeProperty Handle*, WS

## Cells [-PortProperty [string]]

Names one column's `<TD>` with a `PORT` attribute, so `Edge` can target that specific cell instead of the whole record.

Get-Process | Select-Object -First 3 Name, Id |
Cells -PortProperty Id | Record Processes -Name Procs

Graph {
Record Procs -Rows (Get-Process | Select-Object -First 3 Name, Id | Cells -PortProperty Id)
Node Other
Edge Other -To Procs:1234
}

## Cells [-Align [LEFT|CENTER|RIGHT]] [-HtmlEncode] [-NoHeader]

* `-Align` — text alignment applied to every `<TD>`. Defaults to `LEFT`.
* `-HtmlEncode` — HTML-encodes each cell's value, for data that may contain `<>&`.
* `-NoHeader` — skips the header row that's otherwise built from the first object's property names.

Get-Process | Select-Object -First 5 Name, Id, CPU |
Cells -Align CENTER -NoHeader | Record ProcessList
Loading
Loading