
Automate a Ping and Port Sweep of a Subnet with PowerShell
Before you run this
This guide builds a small PowerShell script that walks every address in a /24 subnet, sends one ICMP echo (a ping) to each, and — for the hosts that answer — tests a short list of TCP ports and does a reverse-DNS lookup. The output is a CSV inventory: IP, up/down, resolved name, and which ports were open. It is read-only. It does not log into, change, or write anything on the machines it probes.
Privileges: you do not need to run this elevated. Test-Connection and Test-NetConnection work fine in a normal user session. The only thing that varies is whether your local firewall or network path allows outbound ICMP and the TCP ports you test.
Authorization first — this matters more than privileges. A port sweep looks exactly like the reconnaissance stage of an attack, and an IDS, an EDR agent, or a security team will flag it. Only scan subnets you own or have written permission to scan. Do not point this at a network you do not administer.
Test it small first. Read the script before you run it. Then run it against a tiny range you understand — say .1 to .10 on your own lab subnet — and confirm the results match reality before you unleash it on a full /24. A /24 with a slow port test can take several minutes; know that going in.
Assumptions. Windows 10/11 or Windows Server, Windows PowerShell 5.1 (the version in the box), domain-joined so reverse DNS resolves against your internal DNS. Test-NetConnection and Resolve-DnsName are standard on these systems. On PowerShell 7 the ping cmdlet takes slightly different parameters — I note that where it matters.
The plan
Two passes, because they cost very different amounts of time:
- Ping sweep — cheap and fast. Find which addresses are alive.
- Port test + name lookup — only against the hosts that answered, so we don't waste minutes timing out on empty addresses.
Step 1: A plain ping sweep
Start with the simplest useful thing. Replace the first three octets with your own subnet.
# --- edit these two lines for your environment ---
$Prefix = "192.168.1." # first three octets of YOUR subnet, keep the trailing dot
$Range = 1..254 # host portion; 1..254 covers a full /24
$Live = foreach ($i in $Range) {
$ip = "$Prefix$i"
# -Count 1 sends a single echo; -Quiet returns just $true/$false
if (Test-Connection -ComputerName $ip -Count 1 -Quiet -ErrorAction SilentlyContinue) {
$ip
}
}
$Live # show the addresses that answered
-Quiet collapses the result to a boolean so the if reads cleanly. -ErrorAction SilentlyContinue keeps unreachable hosts from spraying red text.
A caveat worth stating plainly: ping only finds hosts that answer ICMP. Plenty of Windows servers and hardened devices drop echo requests by default, so a host can be very much alive and still be absent from this list. Ping is a first pass, not gospel. That's part of why we also test ports.
PowerShell 7 note: on 7, Test-Connection uses -TargetName instead of -ComputerName and -Count still works; -Quiet also still works. If you're on 7 and the script errors on -ComputerName, that's why — check the cmdlet's Microsoft Learn page for the exact 7.x parameters.
Step 2: Add port tests and reverse DNS
Now turn each live IP into a richer record. Test-NetConnection with -InformationLevel Quiet returns a simple $true/$false for whether the TCP port opened.
# --- edit these ---
$Prefix = "192.168.1."
$Range = 1..254
$Ports = 22, 80, 443, 445, 3389 # the ports you actually care about
$Inventory = foreach ($i in $Range) {
$ip = "$Prefix$i"
$alive = Test-Connection -ComputerName $ip -Count 1 -Quiet -ErrorAction SilentlyContinue
if (-not $alive) { continue } # skip silent addresses entirely
# Reverse DNS. Wrapped in try/catch because a lookup with no PTR record throws.
$name = try {
(Resolve-DnsName -Name $ip -ErrorAction Stop |
Where-Object { $_.Type -eq 'PTR' }).NameHost
} catch { $null }
# Test each port; collect the ones that answered.
$open = foreach ($p in $Ports) {
$ok = Test-NetConnection -ComputerName $ip -Port $p `
-InformationLevel Quiet -WarningAction SilentlyContinue
if ($ok) { $p }
}
[pscustomobject]@{
IPAddress = $ip
Name = $name
OpenPorts = ($open -join ',')
}
}
$Inventory | Format-Table -AutoSize
Two things to know about Test-NetConnection:
- It is not fast. Each closed port waits for a TCP timeout. Testing five ports across dozens of live hosts adds up, which is exactly why we gate it behind the ping check. Keep
$Portsshort — the handful you genuinely inventory on, not a full 1–65535 sweep. This cmdlet is not built for large-scale scanning; if that's your goal, a purpose-built tool likenmapis the right instrument. -WarningAction SilentlyContinuesuppresses the yellow "TCP connect failed" warnings that would otherwise print for every closed port.
Step 3: Save the inventory to CSV
Add one line to the end so you have a file you can diff against next month's run:
$OutFile = "C:\path\to\subnet-inventory.csv" # replace with a real path you can write to
$Inventory | Export-Csv -Path $OutFile -NoTypeInformation -Encoding UTF8
-NoTypeInformation drops the #TYPE header line that older PowerShell prepends, so the CSV opens cleanly in Excel or feeds into a later Import-Csv. Pick a path your account can write to — a folder under your profile is a safe default.
A note on speed
The script above is sequential: one host at a time. That's the mainstream, easy-to-read approach, and for a single /24 it's fine. If you find yourself waiting too long and you're on PowerShell 7, ForEach-Object -Parallel exists and can run the probes concurrently — but it changes how you collect results and how variables scope, so treat it as a separate exercise and read its documentation before you reach for it. I'd get the sequential version working and trusted first.
Verify it worked
This script changes nothing, so verification is about trusting the output, not checking for side effects:
- Cross-check a known host. Pick a device you know is up with a known name — a domain controller, your own workstation — and confirm it appears in
$Inventorywith the right name and expected open ports. - Compare against the ARP cache. After the sweep, run
arp -ain the same session. Hosts you actually talked to should show up there with a MAC address. If a live IP from your inventory is missing fromarp -a, that's worth a second look. - Spot-check one port by hand. Run
Test-NetConnection -ComputerName <a-live-ip> -Port 443on its own and read the full output; theTcpTestSucceededfield should match what the CSV recorded. - Confirm the CSV. Reopen it with
Import-Csv -Path $OutFileand check the row count and columns look right.
Undo
There is nothing to roll back on the network — no host was modified. The only artifact this creates is the CSV file, so "undo" is simply:
Remove-Item -Path $OutFile
Run that only if you're sure you don't want the record. For the cmdlet details, see Microsoft Learn for Test-Connection, Test-NetConnection, and Resolve-DnsName — confirm the parameters against your exact PowerShell version before you build this into anything scheduled.
Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.
More about the author →Was this article helpful?
Tap a star — no sign-in needed.
Be the first to rate this article.
