├── README.md ├── ip_scan.ps1 ├── ip_scan_JSON.ps1 └── ip_scan_JSON_CSV_linux.ps1 /README.md: -------------------------------------------------------------------------------- 1 | # Powershell - Scan IP Range Quickly with Multithreading 2 | ![ip_scan.ps1 demo recording](http://virasawmi.com/gordon/powershell/ip_scan/ip_scan-demo.gif) 3 | 4 | This script scans an IP range very quickly. I use this to scan big networks. The graphic is in realtime. It takes maybe 3 minutes to find 900 computers on bigger networks. 5 | 6 | The script returns IP, DNS, and MAC entries. 7 | 8 | Although slower, I've found this script to be more dependable than AD's computer list and Powershell's Get-NetNeighbor scan. 9 | 10 | Please remember to edit/input your subnet range at line 38. This script doesn't autodetect your subnet mask and what IP you are using. . 11 | 12 | -------------------------------------------------------------------------------- /ip_scan.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Scan an IP range and report how many IPs are alive. 4 | 5 | .DESCRIPTION 6 | Uses Multi Threading and only 1 ping to accomplish the task faster. (Inspired by Angry IP SCanner) 7 | 8 | .PARAMETER 9 | In the top of the script, set your IP ranges. You can scan multiple ranges. Handy for corporate nets. 10 | 11 | .INPUTS 12 | None 13 | 14 | .OUTPUTS 15 | Text to Powershell Console 16 | 17 | .NOTES 18 | 19 | Version: 1.01 20 | Author: Gordon Virasawmi 21 | GitHub: https://github.com/GordonVi 22 | Creation Date: 10/25/2019 @ 6:56pm 23 | Purpose/Change: Initial script development 24 | License: Free for all. Too simple to charge for. Too important to not publish. 25 | Works Cited: Runspaces Simplified - https://blog.netnerds.net/2016/12/runspaces-simplified/ 26 | 27 | 28 | .EXAMPLE 29 | .\ip_scan.ps1 30 | 31 | #> 32 | 33 | # -------------------------------------------------- 34 | 35 | $threads = 1000 # how many simultanious threads. I've tested up to 1000 ok against ~3600 local IPs, ~900 active. 36 | 37 | $list = for ($a=1; $a -le 255; $a++) # set the last octlet range 38 | { 39 | "10.0.0.$a" # set the first 3 octlets. 40 | } 41 | 42 | # -------------------------------------------------- 43 | 44 | clear 45 | 46 | "" 47 | write-host " Threads: " -nonewline -foregroundcolor yellow 48 | $threads 49 | " Build Pool: " 50 | " Drain Pool: " 51 | " ---------------------" 52 | write-host " Total Hosts: $($list.count)" 53 | write-host " Alive Hosts: " 54 | write-host " Dead Hosts: " 55 | 56 | 57 | # BLOCK 1: Create and open runspace pool, setup runspaces array with min and max threads 58 | $pool = [RunspaceFactory]::CreateRunspacePool(1, $threads) 59 | $pool.ApartmentState = "MTA" 60 | $pool.Open() 61 | $runspaces = $results = @() 62 | 63 | # -------------------------------------------------- 64 | 65 | # BLOCK 2: Create reusable scriptblock. This is the workhorse of the runspace. Think of it as a function. 66 | $scriptblock = { 67 | Param ( 68 | [string]$ip 69 | ) 70 | 71 | $ping=$(Test-Connection -ComputerName $ip -Count 1).scope.isconnected 72 | if ($ping -eq "true") { 73 | 74 | $DNS=([System.Net.Dns]::GetHostByAddress($ip)).Hostname 75 | #$mac=$($(arp -a $ip)[3]).Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1] 76 | $mac=$(get-netneighbor -ipaddress $ip).LinkLayerAddress 77 | 78 | } else { 79 | 80 | $DNS="" 81 | $mac="" 82 | } 83 | 84 | # return whatever you want, or don't. 85 | return [pscustomobject][ordered]@{ 86 | ip = $ip 87 | ping = $ping 88 | DNS = $DNS 89 | MAC = $mac 90 | } 91 | } 92 | 93 | # -------------------------------------------------- 94 | 95 | # BLOCK 3: Create runspace and add to runspace pool 96 | $counter=0 97 | foreach ($ip in $list) { 98 | 99 | $runspace = [PowerShell]::Create() 100 | $null = $runspace.AddScript($scriptblock) 101 | $null = $runspace.AddArgument($ip) 102 | 103 | $runspace.RunspacePool = $pool 104 | 105 | # BLOCK 4: Add runspace to runspaces collection and "start" it 106 | # Asynchronously runs the commands of the PowerShell object pipeline 107 | $runspaces += [PSCustomObject]@{ Pipe = $runspace; Status = $runspace.BeginInvoke() } 108 | 109 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 16 , 2 110 | $counter++ 111 | write-host "$counter " -nonewline 112 | } 113 | 114 | # -------------------------------------------------- 115 | 116 | # BLOCK 5: Wait for runspaces to finish 117 | 118 | <# 119 | 120 | do { 121 | 122 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 5 , 9 123 | $cnt = ($runspaces | Where {$_.Result.IsCompleted -ne $true}).Count 124 | write-host "$cnt " 125 | 126 | } while ($cnt -gt 0) 127 | 128 | #> 129 | 130 | # -------------------------------------------------- 131 | 132 | $total=$counter 133 | $counter=0 134 | 135 | # BLOCK 6: Clean up 136 | foreach ($runspace in $runspaces ) { 137 | # EndInvoke method retrieves the results of the asynchronous call 138 | $results += $runspace.Pipe.EndInvoke($runspace.Status) 139 | $runspace.Pipe.Dispose() 140 | 141 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 16 , 3 142 | $counter++ 143 | write-host "$($total-$counter) " -nonewline 144 | 145 | } 146 | 147 | $pool.Close() 148 | $pool.Dispose() 149 | 150 | # -------------------------------------------------- 151 | 152 | # Bonus block 7 153 | # Look at $results to see any errors or whatever was returned from the runspaces 154 | 155 | # Use this to output to JSON. CSV works too since it's simple data. 156 | # $results | convertto-json -depth 10 > ip_scan.json 157 | 158 | $total=$results.count 159 | $alive = $($results | ? {$_.ping -eq "true"}).count 160 | $dead = $($results | ? {$_.ping -ne "true"}).count 161 | 162 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 0 , 5 163 | 164 | write-host " Total Hosts: " -nonewline -foregroundcolor cyan 165 | $total 166 | 167 | write-host " Alive Hosts: " -nonewline -foregroundcolor green 168 | $alive 169 | 170 | write-host " Dead Hosts: " -nonewline -foregroundcolor red 171 | $dead 172 | 173 | "" 174 | 175 | $results | ? {$_.ping -eq "true"} | select ip,DNS,MAC 176 | -------------------------------------------------------------------------------- /ip_scan_JSON.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Scan an IP range and report how many IPs are alive. 4 | 5 | .DESCRIPTION 6 | Uses Multi Threading and only 1 ping to accomplish the task faster. (Inspired by Angry IP SCanner) 7 | 8 | .PARAMETER 9 | In the top of the script, set your IP ranges. You can scan multiple ranges. Handy for corporate nets. 10 | 11 | .INPUTS 12 | None 13 | 14 | .OUTPUTS 15 | Text to CLI console out 16 | Text to a TimeStamp.JSON file 17 | 18 | .NOTES 19 | Version: 1.02 20 | Author: Gordon Virasawmi 21 | GitHub: https://github.com/GordonVi/ip_scan/ 22 | Creation Date: 9/23/2022 @ 10:08am 23 | Purpose/Change: Added JSON Logging 24 | License: Free for all. Too simple to charge for. Too important to not publish. 25 | Works Cited: Runspaces Simplified - https://blog.netnerds.net/2016/12/runspaces-simplified/ 26 | 27 | .EXAMPLE 28 | .\ip_scan_JSON.ps1 29 | (Type this in the Powershell CLI or Right Click and Run from Windows GUI) 30 | 31 | #> 32 | 33 | # -------------------------------------------------- 34 | 35 | $TimeStart = $(Get-Date -UFormat "%H:%M:%S") 36 | 37 | $threads = 1000 # how many simultanious threads. I've tested up to 1000 ok against ~3600 local IPs, ~900 active. 38 | 39 | $list = for ($a=1; $a -le 254; $a++) # set the last octlet range 40 | { 41 | "192.168.1.$a" # First 3 octlets, then $a for range from the FOR loop 42 | "192.168.2.$a" # Manually add subnets, or create a $b to scan more. 43 | } 44 | 45 | # -------------------------------------------------- 46 | 47 | clear 48 | 49 | "" 50 | write-host " Threads: " -nonewline -foregroundcolor yellow 51 | $threads 52 | " Build Pool: " 53 | " Drain Pool: " 54 | " ---------------------" 55 | write-host " Total Hosts: $($list.count)" 56 | write-host " Alive Hosts: " 57 | write-host " Dead Hosts: " 58 | 59 | 60 | # BLOCK 1: Create and open runspace pool, setup runspaces array with min and max threads 61 | $pool = [RunspaceFactory]::CreateRunspacePool(1, $threads) 62 | $pool.ApartmentState = "MTA" 63 | $pool.Open() 64 | $runspaces = $results = @() 65 | 66 | # -------------------------------------------------- 67 | 68 | # BLOCK 2: Create reusable scriptblock. This is the workhorse of the runspace. Think of it as a function. 69 | $scriptblock = { 70 | Param ( 71 | [string]$ip 72 | ) 73 | 74 | $ping=$(Test-Connection -ComputerName $ip -Count 1).scope.isconnected 75 | if ($ping -eq "true") { 76 | 77 | $DNS=([System.Net.Dns]::GetHostByAddress($ip)).Hostname 78 | #$mac=$($(arp -a $ip)[3]).Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1] 79 | $mac=$(get-netneighbor -ipaddress $ip).LinkLayerAddress 80 | 81 | } else { 82 | 83 | $DNS="" 84 | $mac="" 85 | } 86 | 87 | # return whatever you want, or don't. 88 | return [pscustomobject][ordered]@{ 89 | ip = $ip 90 | ping = $ping 91 | DNS = $DNS 92 | MAC = $mac 93 | } 94 | } 95 | 96 | # -------------------------------------------------- 97 | 98 | # BLOCK 3: Create runspace and add to runspace pool 99 | $counter=0 100 | foreach ($ip in $list) { 101 | 102 | $runspace = [PowerShell]::Create() 103 | $null = $runspace.AddScript($scriptblock) 104 | $null = $runspace.AddArgument($ip) 105 | 106 | $runspace.RunspacePool = $pool 107 | 108 | # BLOCK 4: Add runspace to runspaces collection and "start" it 109 | # Asynchronously runs the commands of the PowerShell object pipeline 110 | $runspaces += [PSCustomObject]@{ Pipe = $runspace; Status = $runspace.BeginInvoke() } 111 | 112 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 16 , 2 113 | $counter++ 114 | write-host "$counter " -nonewline 115 | } 116 | 117 | # -------------------------------------------------- 118 | 119 | # BLOCK 5: Wait for runspaces to finish 120 | 121 | <# 122 | 123 | do { 124 | 125 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 5 , 9 126 | $cnt = ($runspaces | Where {$_.Result.IsCompleted -ne $true}).Count 127 | write-host "$cnt " 128 | 129 | } while ($cnt -gt 0) 130 | 131 | #> 132 | 133 | # -------------------------------------------------- 134 | 135 | $total=$counter 136 | $counter=0 137 | 138 | # BLOCK 6: Clean up 139 | foreach ($runspace in $runspaces ) { 140 | # EndInvoke method retrieves the results of the asynchronous call 141 | $results += $runspace.Pipe.EndInvoke($runspace.Status) 142 | $runspace.Pipe.Dispose() 143 | 144 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 16 , 3 145 | $counter++ 146 | write-host "$($total-$counter) " -nonewline 147 | 148 | } 149 | 150 | $pool.Close() 151 | $pool.Dispose() 152 | 153 | # -------------------------------------------------- 154 | 155 | # Bonus block 7 156 | # Look at $results to see any errors or whatever was returned from the runspaces 157 | 158 | # Use this to output to JSON. CSV works too since it's simple data. 159 | # $results | convertto-json -depth 10 > ip_scan.json 160 | 161 | $total=$results.count 162 | $alive = $($results | ? {$_.ping -eq "true"}).count 163 | $dead = $($results | ? {$_.ping -ne "true"}).count 164 | 165 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 0 , 5 166 | 167 | write-host " Total Hosts: " -nonewline -foregroundcolor cyan 168 | $total 169 | 170 | write-host " Alive Hosts: " -nonewline -foregroundcolor green 171 | $alive 172 | 173 | write-host " Dead Hosts: " -nonewline -foregroundcolor red 174 | $dead 175 | 176 | " ---------------------" 177 | 178 | # Block 8 179 | # Write the dated JSON 180 | 181 | #$results | ? {$_.ping -eq "true"} | select ip,DNS,MAC | sort ip | out-host 182 | 183 | $filename = $(Get-Date -UFormat "%Y-%m-%d_%H-%M-%S") 184 | $filename = "$filename.JSON" 185 | $nodes=@() 186 | 187 | foreach ($node in $($results | ? {$_.ping -eq "true"} | select ip,DNS,MAC | sort -Property mac, DNS, ip -Descending)) { 188 | 189 | $nodes += [PsCustomObject]@{ 190 | IP = $node.IP 191 | MAC = $node.mac 192 | DNS = $node.dns 193 | } 194 | } 195 | 196 | 197 | $jsonobject = [pscustomobject]@{ 198 | 199 | Name = "IP Scan" 200 | Date = $(Get-Date -UFormat "%Y-%m-%d") 201 | TimeStart = $TimeStart 202 | TimeEnd = $(Get-Date -UFormat "%H:%M:%S") 203 | Threads = $threads 204 | Total = $total 205 | Alive = $alive 206 | Dead = $dead 207 | Nodes = $nodes 208 | } 209 | 210 | $jsonobject | ConvertTo-Json | Out-File -FilePath ".\$filename" 211 | 212 | 213 | #pause 214 | $null = [Console]::ReadKey() 215 | 216 | -------------------------------------------------------------------------------- /ip_scan_JSON_CSV_linux.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Scan an IP range and report how many IPs are alive. 4 | 5 | .DESCRIPTION 6 | Uses Multi Threading and only 1 ping to accomplish the task faster. (Inspired by Angry IP SCanner) 7 | 8 | .PARAMETER 9 | In the top of the script, set your IP ranges. You can scan multiple ranges. Handy for corporate nets. 10 | 11 | .INPUTS 12 | None 13 | 14 | .OUTPUTS 15 | Text to CLI console out 16 | Text to a TimeStamp.JSON file 17 | 18 | .NOTES 19 | Version: 1.02 20 | Author: Gordon Virasawmi 21 | GitHub: https://github.com/GordonVi/ip_scan/ 22 | Creation Date: 9/23/2022 @ 10:08am 23 | Purpose/Change: Added JSON Logging 24 | License: Free for all. Too simple to charge for. Too important to not publish. 25 | Works Cited: Runspaces Simplified - https://blog.netnerds.net/2016/12/runspaces-simplified/ 26 | 27 | .EXAMPLE 28 | .\ip_scan_JSON.ps1 29 | (Type this in the Powershell CLI or Right Click and Run from Windows GUI) 30 | 31 | #> 32 | 33 | # -------------------------------------------------- 34 | 35 | $TimeStart = $(Get-Date -UFormat "%H:%M:%S") 36 | 37 | $threads = 1000 # how many simultanious threads. I've tested up to 1000 ok against ~3600 local IPs, ~900 active. 38 | 39 | $list = for ($a=0; $a -le 255; $a++) # set the last octlet range 40 | { 41 | "10.134.225.$a" # Manually add subnets, or create a $b to scan more. 42 | "10.134.226.$a" # Manually add subnets, or create a $b to scan more. 43 | "10.134.227.$a" # Manually add subnets, or create a $b to scan more. 44 | } 45 | 46 | # -------------------------------------------------- 47 | 48 | clear 49 | 50 | "" 51 | write-host " Threads: " -nonewline -foregroundcolor yellow 52 | $threads 53 | " Build Pool: " 54 | " Drain Pool: " 55 | " ---------------------" 56 | write-host " Total Hosts: $($list.count)" 57 | write-host " Alive Hosts: " 58 | write-host " Dead Hosts: " 59 | 60 | 61 | # BLOCK 1: Create and open runspace pool, setup runspaces array with min and max threads 62 | $pool = [RunspaceFactory]::CreateRunspacePool(1, $threads) 63 | $pool.ApartmentState = "MTA" 64 | $pool.Open() 65 | $runspaces = $results = @() 66 | 67 | # -------------------------------------------------- 68 | 69 | # BLOCK 2: Create reusable scriptblock. This is the workhorse of the runspace. Think of it as a function. 70 | $scriptblock = { 71 | Param ( 72 | [string]$ip 73 | ) 74 | 75 | $ping=Test-Connection -ComputerName $ip -Count 1 76 | if ($ping.status -eq "Success") { 77 | 78 | $DNS=([System.Net.Dns]::GetHostByAddress($ip)).Hostname 79 | $mac=$($(arp -a $ip).split(" "))[3] 80 | #$mac=$(get-netneighbor -ipaddress $ip).LinkLayerAddress 81 | 82 | } else { 83 | 84 | $DNS="" 85 | $mac="" 86 | } 87 | 88 | # return whatever you want, or don't. 89 | return [pscustomobject][ordered]@{ 90 | ip = $ip 91 | ping = $ping.status 92 | DNS = $DNS 93 | MAC = $mac 94 | } 95 | } 96 | 97 | # -------------------------------------------------- 98 | 99 | # BLOCK 3: Create runspace and add to runspace pool 100 | $counter=0 101 | foreach ($ip in $list) { 102 | 103 | $runspace = [PowerShell]::Create() 104 | $null = $runspace.AddScript($scriptblock) 105 | $null = $runspace.AddArgument($ip) 106 | 107 | $runspace.RunspacePool = $pool 108 | 109 | # BLOCK 4: Add runspace to runspaces collection and "start" it 110 | # Asynchronously runs the commands of the PowerShell object pipeline 111 | $runspaces += [PSCustomObject]@{ Pipe = $runspace; Status = $runspace.BeginInvoke() } 112 | 113 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 16 , 2 114 | $counter++ 115 | write-host "$counter " -nonewline 116 | } 117 | 118 | # -------------------------------------------------- 119 | 120 | # BLOCK 5: Wait for runspaces to finish 121 | 122 | <# 123 | 124 | do { 125 | 126 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 5 , 9 127 | $cnt = ($runspaces | Where {$_.Result.IsCompleted -ne $true}).Count 128 | write-host "$cnt " 129 | 130 | } while ($cnt -gt 0) 131 | 132 | #> 133 | 134 | # -------------------------------------------------- 135 | 136 | $total=$counter 137 | $counter=0 138 | 139 | # BLOCK 6: Clean up 140 | foreach ($runspace in $runspaces ) { 141 | # EndInvoke method retrieves the results of the asynchronous call 142 | $results += $runspace.Pipe.EndInvoke($runspace.Status) 143 | $runspace.Pipe.Dispose() 144 | 145 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 16 , 3 146 | $counter++ 147 | write-host "$($total-$counter) " -nonewline 148 | 149 | } 150 | 151 | $pool.Close() 152 | $pool.Dispose() 153 | 154 | # -------------------------------------------------- 155 | 156 | # Bonus block 7 157 | # Look at $results to see any errors or whatever was returned from the runspaces 158 | 159 | # Use this to output to JSON. CSV works too since it's simple data. 160 | # $results | convertto-json -depth 10 > ip_scan.json 161 | 162 | $total=$results.count 163 | $alive = $($results | ? {$_.ping -eq "Success"}).count 164 | $dead = $($results | ? {$_.ping -ne "Success"}).count 165 | 166 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 0 , 5 167 | 168 | write-host " Total Hosts: " -nonewline -foregroundcolor cyan 169 | $total 170 | 171 | write-host " Alive Hosts: " -nonewline -foregroundcolor green 172 | $alive 173 | 174 | write-host " Dead Hosts: " -nonewline -foregroundcolor magenta 175 | $dead 176 | 177 | " ---------------------" 178 | 179 | # Block 8 180 | # Write the dated JSON 181 | 182 | #$results | ? {$_.ping -eq "true"} | select ip,DNS,MAC | sort ip | out-host 183 | 184 | $filename = $(Get-Date -UFormat "%Y-%m-%d_%H-%M-%S") 185 | $filename = "$filename" 186 | $nodes=@() 187 | 188 | foreach ($node in $($results | ? {$_.ping -eq "Success"} | select ip,DNS,MAC | sort-object -Property ip, DNS, MAC -Descending)) { 189 | 190 | $nodes += [PsCustomObject]@{ 191 | IP = $node.IP 192 | MAC = $node.mac 193 | DNS = $node.dns 194 | } 195 | } 196 | 197 | 198 | $jsonobject = [pscustomobject]@{ 199 | 200 | Name = "IP Scan" 201 | Date = $(Get-Date -UFormat "%Y-%m-%d") 202 | TimeStart = $TimeStart 203 | TimeEnd = $(Get-Date -UFormat "%H:%M:%S") 204 | Threads = $threads 205 | Total = $total 206 | Alive = $alive 207 | Dead = $dead 208 | Nodes = $nodes 209 | } 210 | 211 | $jsonobject | ConvertTo-Json | Out-File -FilePath ".\$filename.JSON" 212 | $results | ? {$_.ping -eq "Success"} | select IP,DNS,MAC | ConvertTo-CSV | Out-File -FilePath ".\$filename.CSV" 213 | 214 | 215 | --------------------------------------------------------------------------------