PSGraphPlus.psm1

Write-Verbose 'Importing from [C:\projects\psgraphplus\PSGraphPlus\private]'
Write-Verbose 'Importing from [C:\projects\psgraphplus\PSGraphPlus\public]'
# .\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) } {
            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'} {
            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]'