Threadable Pinging Functions

Overview

This is a script that uses a threading function to return ping results on a list of computers. What’s neat about it is you can easily get ping results on hundreds of computers in just seconds. You can either search directly for computers within the script, pass some computers through on the pipeline or import a csv. You can also export results to csv.

Pass a list of computers and export results to csv:


  Get-ADComputer -filter {name -like "*sql*"} | Get-DGMThreadedPingableComputers -csvoutput "C:\Scripts\Get-DGMThreadedPingableComputer\Get-DGMThreadedPingableComputer-output.csv" 

Pass a list of computers (with example output):


  Get-ADComputer -filter {name -like "*DGM06123*"} -Properties Description,OperatingSystem | Get-DGMThreadedPingableComputers 

No Arguments triggers searching for computers within the script (with example output):


  Get-DGMThreadedPingableComputers 

PowerShell Code


<#
.Synopsis
   Threads and threads
.DESCRIPTION
   The tool, Get-DMGThreadedPingableComputers.ps1  was written by David Maiolo.
.EXAMPLE
   Get-DMGThreadedPingableComputers -CSVFile laps_computers_import.csv
#>

function Where-ParallelObject {
    param(
        [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)] $input,
        [ScriptBlock] $Filter,
        [int] $threads,
        [switch] $progressBar,
        [String] $progressBartext
    )

    $inputQueue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
    $results = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )

    $sessionstate = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
    $sessionstate.Variables.Add(
        (New-Object System.Management.Automation.Runspaces.SessionStateVariableEntry('inputQueue', $inputQueue, $null))
    )
    $sessionstate.Variables.Add(
        (New-Object System.Management.Automation.Runspaces.SessionStateVariableEntry('results', $results, $null))
    )

    $runspacepool = [runspacefactory]::CreateRunspacePool(1, $threads, $sessionstate, $Host)
    $runspacepool.Open()

    foreach ($object in $input) {
        $inputQueue.Enqueue($object)
    }

    $jobs = @()

    $sbpre = '
        while($inputQueue.Count -gt 0) {
            $_ = $inputQueue.Dequeue();
            if('
    $sbpost = ') 
            {
                $results.Enqueue($_);    
            }
        }
    '

    $sb = [ScriptBlock]::Create($sbpre + $Filter.toString() + $sbpost)

    1..$threads | % {
        $job = [PowerShell]::Create().AddScript($sb)
        $job.RunspacePool = $runspacepool
        $jobs += New-Object PSObject -Property @{
            Job = $job
            Result = $job.BeginInvoke()
        }
    }

    do {
        if($progressBar.IsPresent) 
        {
            Write-Progress -Activity ($progressBartext+" " +$input.Count+ " Objects") -status ("" + $($results.Count) + " complete.") -percentComplete ( ($results.Count) / $input.Count * 100) 
        }
        Start-Sleep -Seconds 1
    } while ( $jobs.Result.IsCompleted -contains $false)

    foreach ($job in $jobs) {
        $job.Job.EndInvoke($job.Result)
    }
    $runspacepool.Close()
    $runspacepool.Dispose()

    return $results.ToArray()

}

function LogIt
{
  param (
  [Parameter(Mandatory=$true)]
  $message,
  [Parameter(Mandatory=$true)]
  $component,
  [Parameter(Mandatory=$true)]
  $type )

  switch ($type)
  {
    1 { $type = "Info" }
    2 { $type = "Warning" }
    3 { $type = "Error" }
    4 { $type = "Verbose" }
  }

  if (($type -eq "Verbose") -and ($Global:Verbose))
  {
    $toLog = "{0} `$$<{1}><{2} {3}><thread={4}>" -f ($type + ":" + $message), ($Global:ScriptName + ":" + $component), (Get-Date -Format "MM-dd-yyyy"), (Get-Date -Format "HH:mm:ss.ffffff"), $pid
    $toLog | Out-File -Append -Encoding UTF8 -FilePath ("filesystem::{0}" -f $Global:LogFile)
    Write-Host $message
  }
  elseif ($type -ne "Verbose")
  {
    $toLog = "{0} `$$<{1}><{2} {3}><thread={4}>" -f ($type + ":" + $message), ($Global:ScriptName + ":" + $component), (Get-Date -Format "MM-dd-yyyy"), (Get-Date -Format "HH:mm:ss.ffffff"), $pid
    $toLog | Out-File -Append -Encoding UTF8 -FilePath ("filesystem::{0}" -f $Global:LogFile)
    if ($type -eq 'Info') { Write-Host $message }
    if ($type -eq 'Warning') { Write-Host $message -ForegroundColor Yellow}
    if ($type -eq 'Error') { Write-Host $message -ForegroundColor Red}
    

  }
  if (($type -eq 'Warning') -and ($Global:ScriptStatus -ne 'Error')) { $Global:ScriptStatus = $type }
  if ($type -eq 'Error') { $Global:ScriptStatus = $type }

  if ((Get-Item $Global:LogFile).Length/1KB -gt $Global:MaxLogSizeInKB)
  {
    $log = $Global:LogFile
    Remove-Item ($log.Replace(".log", ".lo_"))
    Rename-Item $Global:LogFile ($log.Replace(".log", ".lo_")) -Force
  }
} 

function GetScriptDirectory
{
  $invocation = (Get-Variable MyInvocation -Scope 1).Value
  Split-Path $invocation.MyCommand.Path
} 

function Get-DMGThreadedPingableComputers {

    param(
        [Parameter(Position=0,Mandatory=$false,ValueFromPipeline=$true)] $input,
        [ValidateScript({(Test-Path $_)})]
        [String] $csvinput,
        [ValidateScript({($_ -le 100 -and $_ -gt 0)})]
        [int] $threads=100,
        [String] $csvoutput
    )

        $path = (get-item -Path .).FullName
        $arraytoping=@()

        #Header
        Write-Host "==========================================" -ForegroundColor Cyan
        Write-Host "Ping Computers in Threads " -ForegroundColor Cyan
        Write-Host "v0.1 (2017-12-28) by dmaiolo" -ForegroundColor Cyan
        Write-Host "Threading function by jreal" -ForegroundColor Cyan
        Write-Host "==========================================" -ForegroundColor Cyan
        LogIt -message ("Starting Logging for $Global:ScriptName") -component "Main()" -type 1

        #Check for what arguments were passed
        if([bool]($MyInvocation.BoundParameters.Keys -match 'csvinput')){
            Write-Host "Importing $csvinput..."
            $csvimport = import-csv $csvinput
            $csvimport | foreach-object {$arraytoping += $_.name; Write-Host "Importing $_.name ..."}       
        }
        elseif([bool]($MyInvocation.BoundParameters.Keys -match 'input')){
            Write-Host "Importing from pipeline..."
            foreach ($object in $input) {
                $arraytoping += $object
            }
        }else{
            Write-Host "Manual Input Selected."
            $arraytoping = Get-DMGSearchQueryComputers
        }
        if([bool]($MyInvocation.BoundParameters.Keys -match 'csvoutput')){
            $csvoutputcheck = $true
        }

        #Ping the computers
        $pingablecomputers = Get-DMGOnlineComputers -ComputerList $arraytoping -Threads $threads

        #Create Pingable Table
        if ($pingablecomputers){
            Write-Host ========================================== -ForegroundColor Cyan
            Write-Host Pingable Computers -ForegroundColor Green
            Write-Host ========================================== -ForegroundColor Cyan
            $pingablecomputers | Select Name,Description,OperatingSystem | Format-Table -AutoSize
        }else{
            Write-Host "No Pingable Computers Were Found."
        }

        #Create Non-Pingable Array
        $nonpingablecomputers = $arraytoping | where {$pingablecomputers -notcontains $_}   

        #Create Non-Pingable Table
        if ($nonpingablecomputers){
            Write-Host ========================================== -ForegroundColor Cyan
            Write-Host Non Pingable Computers -ForegroundColor Red
            Write-Host ========================================== -ForegroundColor Cyan
            $nonpingablecomputers | Select Name,Description,OperatingSystem | Format-Table -AutoSize
        }else{
            Write-Host "No Non-Pingable Computers Were Found."
        }

        #Export to CSV if chosen
        if ($csvoutputcheck){
            Write-Host ========================================== -ForegroundColor Cyan
            Write-Host CSV Output Results -ForegroundColor Cyan
            Write-Host ========================================== -ForegroundColor Cyan
            
            #Build the array
            $results = @()
            $pingablecomputers | select Name,Description,OperatingSystem,@{Name='Pingable';Expression={"True"}}  | %{$results += $_ }
            $nonpingablecomputers | select Name,Description,OperatingSystem,@{Name='Pingable';Expression={"False"}} | %{$results += $_ }

            New-DMGCSVOut -csvoutputpath $csvoutput -arrayforoutput $results 
        }


        #Footer
        Write-Host ========================================== -ForegroundColor Cyan
        Write-Host "Log File of Results Generated" -ForegroundColor Cyan
        Write-Host ========================================== -ForegroundColor Cyan
        Write-Host "Log File`: $Global:LogFile VIEW WITH CMTRACE.EXE"
        LogIt -message ("Ending Logging for $Global:ScriptName") -component "Main()" -type 1

}

function Get-DMGSearchQueryComputers
{
    Write-Host "[1] Search by whole or partial computer name"
    Write-Host "[2] Search by description"
    Write-Host "[3] Search by Operating System"
    do {
        try {$numOk = $true; [int]$GetMyANumber = Read-host "Selection"}
        catch {$numOK = $false}}
    until (($GetMyANumber -ge 1 -and $GetMyANumber -le 3) -and $numOK)

    $validcharacters = "^[a-zA-Z0-9\s]+$"
    do {
        try {$stringOk = $true; [string]$query = Read-host "Enter search query (only letters and numbers)"}
        catch {$stringOk = $false}}
    until (($query -match $validcharacters) -and $stringOk)
    $query = "*"+$query
    $query = $query+"*"

    switch ($GetMyANumber) 
        { 
            1 {$computers = Get-ADComputer -Properties Description,OperatingSystem -filter {name -like $query}} 
            2 {$computers = Get-ADComputer -Properties Description,OperatingSystem -filter {description -like $query}} 
            3 {$computers = Get-ADComputer -Properties Description,OperatingSystem -filter {OperatingSystem -like $query}}
        }

    return $computers
}

function Get-DMGOnlineComputers{

    param(
        [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
        [int] $threads,
        [Array] $ComputerList
    )
    
    Write-Host ========================================== -ForegroundColor Cyan
    Write-Host Pinging Computers and Building Table -ForegroundColor Cyan
    Write-Host ========================================== -ForegroundColor Cyan
    
    $computers = @()

    if ($ComputerList.Length -gt 0){
        Write-Host "Pinging"($ComputerList.length)"computers in $threads threads."
        $computers = $ComputerList | Where-ParallelObject -Filter {Test-Connection -ComputerName $_.Name -Quiet -Count 1} -Threads $threads -ProgressBar -progressBartext "Pinging"
        <#$computers | foreach-object {
            LogIt -message ("ICMP Response Succesful`: " +($_.Name).ToString() +" - "+($_.Description).ToString()) -component "Main()" -type 1
            }#>
        return $computers
    }else{
        Write-Host "No Computers were found."
        return $false
    }
}

function New-DMGCSVOut{

    param(
            [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
            [String] $csvoutputpath,
            [array] $arrayforoutput
        )

    try{
        $arrayforoutput | export-csv $csvoutputpath -notypeinformation
        LogIt -message ("CSV Export`: CSV Created at $csvoutputpath") -component "Main()" -type 1
    }catch{
        LogIt -message ("CSV Export`: CSV Could NOT be Created at $csvoutputpath") -component "Main()" -type 3
    }


}

$VerboseLogging = "true"
[bool]$Global:Verbose = [System.Convert]::ToBoolean($VerboseLogging)
$Global:LogFile = Join-Path (GetScriptDirectory) "'Get-DMGThreadedPingableComputer.log"
$Global:MaxLogSizeInKB = 10240
$Global:ScriptName = 'Get-DMGThreadedPingableComputer.ps1' 
$Global:ScriptStatus = 'Success'

Leave a Comment

Your email address will not be published.