Scripts/Get-AssetInventory4.ps1

<#
    .NOTES
        Original Author: G.A.F.F. Jakobs
        Originally Created: August 30, 2014
    .LINK
        https://gallery.technet.microsoft.com/scriptcenter/Fast-asynchronous-ping-IP-d0a5cf0e
#>


Param(
    [parameter(Mandatory = $true, Position = 0)]
    [System.Net.IPAddress]$StartAddress,
    [parameter(Mandatory = $true, Position = 1)]
    [System.Net.IPAddress]$EndAddress
)

function New-Range ($start, $end) {
    [byte[]]$BySt = $start.GetAddressBytes()
    [Array]::Reverse($BySt)
    [byte[]]$ByEn = $end.GetAddressBytes()
    [Array]::Reverse($ByEn)
    $i1 = [System.BitConverter]::ToUInt32($BySt,0)
    $i2 = [System.BitConverter]::ToUInt32($ByEn,0)
    for ($x = $i1;$x -le $i2;$x++) {
        $ip = ([System.Net.IPAddress]$x).GetAddressBytes()
        [Array]::Reverse($ip)
        [System.Net.IPAddress]::Parse($($ip -join '.'))
    }
}

$IpRange = New-Range $StartAddress $EndAddress
$IpTotal = $IpRange.Count
Get-Event -SourceIdentifier "ID-Ping*" | Remove-Event
Get-EventSubscriber -SourceIdentifier "ID-Ping*" | Unregister-Event

$IpRange | 
ForEach {
    [string]$VarName = 'Ping_' + $_.Address
    New-Variable -Name $VarName -Value (New-Object System.Net.NetworkInformation.Ping)
    Register-ObjectEvent -InputObject (Get-Variable $VarName -ValueOnly) -EventName PingCompleted -SourceIdentifier "ID-$VarName"
    (Get-Variable $VarName -ValueOnly).SendAsync($_,2000,$VarName)
    Remove-Variable $VarName
}

while ($Pending -lt $IpTotal) {
    Wait-Event -SourceIdentifier "ID-Ping*" | Out-Null
    Start-Sleep -Milliseconds 10
    $Pending = (Get-Event -SourceIdentifier "ID-Ping*").Count
}

$Assets = Get-Event -SourceIdentifier "ID-Ping*" | 
    ForEach { 
        if ($_.SourceEventArgs.Reply.Status -eq 'Success') {
            $Asset = New-Object -TypeName psobject

            $IpAddress = ($_.SourceEventArgs.Reply).Address.IpAddressToString
            Unregister-Event $_.SourceIdentifier
            Remove-Event $_.SourceIdentifier
            Add-Member -InputObject $Asset -MemberType NoteProperty -Name Address -Value $IpAddress

            $Client = New-Object Net.Sockets.TcpClient
            $Client.Connect($IpAddress,135)
            if ($Client.Connected) {
                Add-Member -InputObject $Asset -MemberType NoteProperty -Name OperatingSystem -Value 'Windows'
            } else {
                Add-Member -InputObject $Asset -MemberType NoteProperty -Name OperatingSystem -Value '-'
            }
            $Client.Close()

            $Asset
        }
    } | Sort-Object { $_.Address -as [Version] } 
    
return $Assets

<# REFERENCES
https://stackoverflow.com/questions/9566052/how-to-check-network-port-access-and-display-useful-message
https://adamtheautomator.com/building-asynchronous-powershell-functions/
https://stackoverflow.com/questions/11888342/how-do-i-add-an-event-handler-to-a-net-object-in-powershell
https://powershell.org/forums/topic/event-handler-in-powershell/
https://devblogs.microsoft.com/scripting/use-asynchronous-event-handling-in-powershell/
http://www.powershellcookbook.com/recipe/WguS/create-and-respond-to-custom-events
#>