PSGraphPlus.psm1
Write-Verbose 'Importing from [C:\projects\psgraphplus\PSGraphPlus\private]' Write-Verbose 'Importing from [C:\projects\psgraphplus\PSGraphPlus\public]' # .\PSGraphPlus\public\Show-AstCommandGraph.ps1 Function Show-AstCommandGraph { <# .SYNOPSIS Generates a graph of the commands called in a script .DESCRIPTION Generates a graph of the commands called in a script .PARAMETER ScriptBlock a scriptblock to process .PARAMETER ScriptText The raw text of a script to process .PARAMETER Path The path to a script to process .PARAMETER AllCommands Show commands called that are not part of the script or module .PARAMETER AllCalls Will show a line for each time a function is called .PARAMETER Raw Produces a raw dot file. .EXAMPLE $script = { function test-function () { Write-Output 'test' } function other-function () { test-function } } $script | Show-AstCommandGraph .NOTES The core powershell cmdlets are filtered out of the graph. commands like foreach-object, write-verbose, ect just add noise #> [cmdletbinding(DefaultParameterSetName = 'ScriptBlock')] param( [parameter( ParameterSetName = 'ScriptBlock', ValueFromPipeline )] [ValidateNotNullOrEmpty()] [scriptblock] $ScriptBlock, [parameter( ParameterSetName = 'ScriptText' )] [ValidateNotNullOrEmpty()] [string] $ScriptText, [parameter( ParameterSetName = 'Path', ValueFromPipeline, Position = 0 )] [ValidateNotNullOrEmpty()] [string] $Path, [switch] $AllCommands, [switch] $AllCalls, [switch] $Raw ) process { if (![string]::IsNullOrWhiteSpace($Path)) { $ScriptText = Get-Content $Path -Raw -ErrorAction Stop } if (![string]::IsNullOrWhiteSpace($ScriptText)) { $ScriptBlock = [scriptblock]::Create($ScriptText) } $functions = $ScriptBlock.Ast | Select-AST -Type FunctionDefinitionAst $names = $functions.Name $commands = $names if ($AllCommands) { $commands = (Get-Command | Where-Object source -notlike Microsoft.PowerShell.* | Select-Object -ExpandProperty Name) + $names $commands = $commands | Select-Object -Unique } if ($null -ne $names) { $graph = Graph { node @{shape = 'box'} node $names foreach ($function in $functions) { $calls = $function.Body | Select-Ast -Type CommandAst $uniquecalls = $calls.commandelements | Where-Object StringConstantType -eq 'BareWord' | Select-Object -ExpandProperty Value -Unique:(-Not $AllCalls) | ForEach-Object {$commands -eq $_ } if ($uniquecalls) { edge $function.Name -To $uniquecalls } } } } if ($Raw) { $graph } else { $graph | Export-PSGraph -ShowGraph } } } # .\PSGraphPlus\public\Show-GitGraph.ps1 enum Direction { BottomToTop TopToBottom RightToLeft LeftToRight } function Show-GitGraph { <# .SYNOPSIS Gets a graph of the git history .DESCRIPTION This will generate a graph showing the recent histroy of a project with the branches. .PARAMETER Path Local location of the Git repository .PARAMETER HistoryDepth How far back into history to show .PARAMETER Uri Allows the injection of a base URL for github projects .PARAMETER ShowCommitMessage This will show the git commit instead of the hash .PARAMETER Raw Output the raw graph without generating the image or showing it. Useful for testing. .PARAMETER Direction This sets the direction of the chart. .EXAMPLE Show-GitGraph .EXAMPLE Show-GitGraph -HistoryDepth 30 .EXAMPLE Show-GitGraph -Path c:\workspace\project -ShowCommitMessage .NOTES #> [CmdletBinding()] param( $Path = $PWD, [alias('Depth')] $HistoryDepth = 15, $Uri = 'https://github.com/KevinMarquette/PSGraph', [switch] $ShowCommitMessage, [switch] $Raw, [Direction] $Direction = [Direction]::LeftToRight ) begin { $directionMap = @{ [Direction]::TopToBottom = 'TB' [Direction]::BottomToTop = 'BT' [Direction]::LeftToRight = 'LR' [Direction]::RightToLeft = 'RL' } } process { Push-Location $Path # Git history with branch details $git = git log --format="%h|%p|%s" -n $HistoryDepth --branches=* | Select-Object -SkipLast 1 $HASH = 0 $PARENT = 1 $SUBJECT = 2 $branches = git branch -a -v $tagList = git show-ref --abbrev=7 --tags $current = git log -1 --pretty=format:"%h" $tagLookup = @{} foreach ($tag in $tagList) { $tagHash, $tagName = $tag -split ' ' if (-not $tagLookup.ContainsKey($tagHash)) { $tagLookup[$tagHash] = @() } $tagLookup[$tagHash] += $tagName.replace('refs/tags/', '') } $commits = @() $graph = graph git @{ rankdir = $directionMap[$Direction]; label = [regex]::Escape( $PWD); pack = 'true' } { Node @{shape = 'box'} foreach ($line in $git) { $data = $line.split('|') $label = $data[$HASH] if ($ShowCommitMessage) { $label = '{0}\n{1}' -f $data[$SUBJECT], $data[$HASH] $commitID = 'commit' + $data[$HASH] Node $commitID @{label = $data[$SUBJECT]; shape = 'plaintext'} Rank $commitID, $data[$HASH] Edge -From $commitID -To $data[$HASH] @{style = 'dotted'; arrowhead = 'none'} $commits = @($commitID) + @($commits) } Node -Name $data[$HASH] @{ URL = "{0}/commit/{1}" -f $Uri, $data[$HASH] } Edge -From $data[$PARENT].split(' ') -To $data[$HASH] #add tags if ($tagLookup.ContainsKey($data[$HASH])) { Node $tagLookup[$data[$HASH]] @{fillcolor = 'yellow'; style = 'filled'} Edge -From $tagLookup[$data[$HASH]] -To $data[$HASH] } } if ($commits.Count) { Edge $commits @{style = 'invis'} } # branches Node @{shape = 'box'; fillcolor = 'green'; style = 'filled'} foreach ($line in $branches) { if ($line -match '(?<branch>[\w/-]+)\s+(?<hash>\w+) (.+)') { Node $Matches.branch Edge $Matches.branch -To $Matches.hash } } # current commit Node $current @{fillcolor = 'gray'; style = 'filled'} } if ($Raw) { $graph } else { $graph | Export-PSGraph -ShowGraph } Pop-Location } } # .\PSGraphPlus\public\Show-NetworkConnectionGraph.ps1 function Show-NetworkConnectionGraph { <# .SYNOPSIS Generates a map of network connections .Description This graph will show the source and target IP addresses with each edge showing the ports .EXAMPLE Show-NetworkConnectionGraph .Example Show-NetworkConnectionGraph -ComputerName $server -Credential $Credential .NOTES #> [CmdletBinding( DefaultParameterSetName = 'Default' )] param( # Remote computer name [Parameter( ParameterSetName = 'Default' )] [string[]] $ComputerName, # Credential for authorization [Parameter( ParameterSetName = 'Default' )] [pscredential] $Credential, # Outputs the raw dot graph (for testing [switch] $Raw ) process { $session = @{} if ( $null -ne $ComputerName ) { $PSBoundParameters.Remove('Raw') $session = @{ CimSession = New-CimSession @PSBoundParameters } } elseif ( $CimSession ) { $session = @{ CimSession = $CimSession } } $netstat = Get-NetTCPConnection -State Established, TimeWait -ErrorAction SilentlyContinue @session $netstat = $netstat | Where-Object LocalAddress -NotMatch ':' $dns = Get-DnsClientCache @session | Where-Object data -in $netstat.RemoteAddress $graph = graph network @{rankdir = 'LR'; label = 'Network Connections'} { Node @{shape = 'rect'} $EdgeParam = @{ Node = $netstat FromScript = {$_.LocalAddress} ToScript = {$_.RemoteAddress} Attributes = @{label = {'{0}:{1}' -f $_.LocalPort, $_.RemotePort}} } Edge @EdgeParam Node $dns -NodeScript {$_.data} @{label = {'{0}\n{1}' -f $_.entry, $_.data}} } if ($Raw) { $graph } else { $graph | Export-PSGraph -ShowGraph } } } # .\PSGraphPlus\public\Show-ProcessConnectionGraph.ps1 function Show-ProcessConnectionGraph { <# .SYNOPSIS Generates a map of network connections .Description This graph will show the source and target IP addresses with each edge showing the ports .EXAMPLE Show-NetworkConnectionGraph .Example Show-NetworkConnectionGraph -ComputerName $server -Credential $Credential .NOTES #> [CmdletBinding( DefaultParameterSetName = 'Default' )] param( # Remote computer name [Parameter( ParameterSetName = 'Default' )] [string[]] $ComputerName, # Credential for authorization [Parameter( ParameterSetName = 'Default' )] [pscredential] $Credential, # Outputs the raw dot graph (for testing) [switch] $Raw ) process { $session = @{} if ( $null -ne $ComputerName ) { $PSBoundParameters.Remove('Raw') $session = @{ CimSession = New-CimSession @PSBoundParameters } } $netstat = Get-NetTCPConnection -State Established, TimeWait -ErrorAction SilentlyContinue @session $netstat = $netstat | Where-Object LocalAddress -NotMatch ':' $dns = Get-DnsClientCache @session | Where-Object data -in $netstat.RemoteAddress $process = Get-CIMInstance -ClassName CIM_Process @session | Where-Object ProcessId -in $netstat.OwningProcess $graph = graph network @{rankdir = 'LR'; label = 'Process Network Connections'} { Node @{shape = 'rect'} Node $process -NodeScript {$_.ProcessID} @{label = {'{0}\n{1}' -f $_.ProcessName, $_.ProcessID}} $EdgeParam = @{ Node = $netstat FromScript = {$_.OwningProcess} ToScript = {$_.RemoteAddress} Attributes = @{label = {'{0}:{1}' -f $_.LocalPort, $_.RemotePort}} } Edge @EdgeParam Node $dns -NodeScript {$_.data} @{label = {'{0}\n{1}' -f $_.entry, $_.data}} } if ($Raw) { $graph } else { $graph | Export-PSGraph -ShowGraph } } } # .\PSGraphPlus\public\Show-ServiceDependencyGraph.ps1 function Show-ServiceDependencyGraph { <# .SYNOPSIS Show the process dependency graph .DESCRIPTION Loads all processes and maps out the dependencies .EXAMPLE Show-ProcessDependencyGraph .NOTES General notes #> [CmdletBinding()] param( # Service Name [Parameter()] [string[]] $Name, # Remote computer name [Parameter()] [string[]] $ComputerName, # Credential for authorization [Parameter()] [pscredential] $Credential, # Outputs the raw dot graph (for testing) [switch] $Raw ) process { if ( $null -ne $ComputerName ) { Write-Verbose 'Connecting to remote system' $PSBoundParameters.Remove('Raw') $services = Invoke-Command @PSBoundParameters -ScriptBlock {Get-Service -Include *} } else { $services = Get-Service -Include * } if ($null -ne $Name) { Write-Verbose ( 'Filtering on name [{0}]' -f ( $Name -join ',' ) ) $services = foreach ($node in $services) { if ($node.Name -in $Name) { $node continue } foreach ($dependency in $node.ServicesDependedOn.Name) { if ( $dependency -in $Name) { $node continue } } } } if ( $null -eq $services ) { return } Set-NodeFormatScript {$_.tolower()} $graph = graph services @{rankdir = 'LR'; pack = 'true'} { Node @{shape = 'box'} Node $services -NodeScript {$_.name} @{ label = {'{0}\n{1}' -f $_.DisplayName, $_.Name} color = {If ($_.Status -eq 'Running') {'blue'}else {'red'}} } $linkedServices = $services | Where-Object {$_.ServicesDependedOn} Edge $linkedServices -FromScript {$_.Name} -ToScript {$_.ServicesDependedOn.Name} } Set-NodeFormatScript if ($Raw) { $graph } else { $graph | Export-PSGraph -ShowGraph } } } Write-Verbose 'Importing from [C:\projects\psgraphplus\PSGraphPlus\classes]' |