This is where you start to see the creative side of agentic AI. Instead of one fixed script for everyone, this tool looks at your Windows network, shows you what it finds, and then builds a brand-new health-check script custom to you and your devices. A tool that builds a tool.
It's a three-step process that adapts to each person who runs it:
| Step | What happens | Why it's clever |
|---|---|---|
| 1. Discover | Scans your Windows network and lists the devices it can see, identifying each one by combining its hostname, manufacturer (MAC address), and the services it responds to. | It's looking at your environment — and tells you what each device is, not just its address. |
| 2. Ask | For each device, it asks you: "Include this in your health check? (y/n)". | You stay in control — you decide what matters to watch. |
| 3. Build | It writes a brand-new PowerShell script that checks each device you chose, in a way that suits its type. | The output is custom-built for your specific network. |
There are two scripts in this tool, and it helps to know the difference before you start:
PowerShell, and click it.a for Auto (include every device it found — quick) or m for Manual (review each device and answer y/n for each).y and it runs the check for you automatically. When it finishes, you'll see the finished report on screen, it saves a copy to your Desktop (a file named Windmill_NetworkHealthReport_…), and it opens the report automatically in Notepad. Everything lands on your Desktop — the one place everyone knows how to find.Produced by an agentic AI on request, then reviewed and tested. Here it is in full:
<#
=====================================================================
The Windmill Club — Windows Network Health Audit Builder
"Agentic AI for the rest of us — a project of PCSNJ"
WHAT THIS DOES
--------------
This script looks at the Windows network your computer is connected
to and finds the devices that are visible on it. For each device it
tries to figure out WHAT it is (router, printer, computer, etc.) by
combining several honest clues:
- the device's hostname, if it offers one
- the manufacturer behind its network address (MAC prefix)
- what services it responds to (web, print, etc.)
Then it asks YOU which devices you'd like to keep an eye on. When
you're done choosing, it writes a brand-new health-check script
built specifically for YOUR network, checking each device in a way
that makes sense for its type.
IS IT SAFE TO RUN?
------------------
Yes. This script only READS information. It does not change, install,
or send anything anywhere. It looks at your device list, asks a few
friendly questions, and writes a text/script file. That's all.
HOW TO RUN IT
-------------
1. Open PowerShell (Start menu -> type "PowerShell" -> click it).
2. Paste this whole script in and press Enter.
3. Wait while it reads your network (a minute or two).
4. A list of found devices appears. For each one, type y or n to say
whether you want it in your health check.
5. It writes a custom health-check script for YOUR devices and opens
it so you can read it before running it.
NOTES
-----
- Phones, tablets, and many smart-home devices that are asleep do not
answer, so they may not be identified beyond their manufacturer.
That's normal and honest — we label those "unidentified device"
rather than guess.
- You can include or skip ANY device. Your choice, every time.
- This is an introductory example, by design. The checks are simple
and real. More advanced checks can come later.
=====================================================================
#>
$ErrorActionPreference = "Continue"
function Write-Header {
Write-Host ""
Write-Host "========================================================" -ForegroundColor DarkYellow
Write-Host " The Windmill Club — Windows Network Health Audit Builder" -ForegroundColor DarkYellow
Write-Host " Builds a custom health-check tool for YOUR network" -ForegroundColor DarkYellow
Write-Host "========================================================" -ForegroundColor DarkYellow
Write-Host ""
}
function Get-NetworkRange {
# Find the REAL LAN interface — the one that has a default gateway.
# This avoids WSL / VPN / virtual adapters (which have no default route).
try {
$route = Get-NetRoute -DestinationPrefix "0.0.0.0/0" -ErrorAction SilentlyContinue |
Where-Object { $_.NextHop -notlike "0.0.0.0" } |
Sort-Object RouteMetric | Select-Object -First 1
if ($route -and $route.InterfaceIndex) {
$ipObj = Get-NetIPAddress -AddressFamily IPv4 -InterfaceIndex $route.InterfaceIndex -ErrorAction SilentlyContinue |
Where-Object { $_.IPAddress -notlike "169.254.*" } | Select-Object -First 1
if ($ipObj) {
$octets = $ipObj.IPAddress -split "\."
$range = ($octets[0] + "." + $octets[1] + "." + $octets[2] + ".")
return @{ Range = $range; Gateway = $route.NextHop; OwnIP = $ipObj.IPAddress }
}
}
} catch {}
# Fallback: pick the first private-range (RFC1918) IPv4 that isn't a virtual adapter
try {
$ip = (Get-NetIPAddress -AddressFamily IPv4 | Where-Object {
$_.IPAddress -match "^10\.|^192\.168\.|^172\.(1[6-9]|2[0-9]|3[01])\."
} | Select-Object -First 1).IPAddress
if ($ip) {
$octets = $ip -split "\."
return @{ Range = ($octets[0] + "." + $octets[1] + "." + $octets[2] + "."); Gateway = ""; OwnIP = $ip }
}
} catch {}
return $null
}
function Get-VendorHint {
param([string]$oui)
# A helpful lookup of common vendor prefixes. Not exhaustive — the point is
# to give a useful hint for the most common home-network devices.
$map = @{
# Printers
"3C2AF4" = "Brother"; "00E02C" = "Lexmark"; "006087" = "HP"
"3C52A1" = "HP"; "9C8EDC" = "HP"; "000425" = "Ricoh"; "000747" = "Epson"
"003048" = "Epson"; "08002F" = "Xerox"; "001999" = "Canon"
# Computers
"B82A72" = "Dell"; "001150" = "Dell"; "005056" = "VMware"
"185680" = "Intel"; "000C29" = "VMware"; "080027" = "VMware"
"C4A3AB" = "Lenovo"; "001C25" = "Lenovo"; "0050B6" = "Cisco"
"002219" = "Lenovo"; "F88A5E" = "Acer"; "00E0B8" = "Acer"
# Apple
"001B78" = "Apple"; "3C22FB" = "Apple"; "0C4BC6" = "Apple"
"A4C3F0" = "Apple"; "F0D5BF" = "Apple"; "78D752" = "Apple"
# Samsung / phones / tablets
"40D3AE" = "Samsung"; "00A0C9" = "Samsung"; "AC5F3E" = "Samsung"
"38B54A" = "Samsung"; "BCE59D" = "Samsung"
# Amazon / Echo / Fire
"007147" = "Amazon"; "6C5697" = "Amazon"; "AC63BE" = "Amazon"
"4405C7" = "Amazon"; "34D2C5" = "Amazon"; "D4F9A1" = "Amazon"
# Routers / networking
"04A222" = "Arcadyan"; "F8E4E3" = "Netgear"; "00C0B7" = "D-Link"
"00E04C" = "Cisco"; "001A2B" = "Asus"; "DCD24A" = "Linksys"
"0012EF" = "TP-Link"; "50C7BF" = "TP-Link"; "FCFC48" = "TP-Link"
"58C1CB" = "TP-Link"; "00E0FC" = "Asus"; "10BF48" = "Netgear"
# Cameras / smart home
"C4E984" = "Hikvision"; "00C1B4" = "Hikvision"; "0014A9" = "Hikvision"
"1C8E5C" = "TP-Link(IoT)"; "CC40D0" = "Wyze"; "2CF4C5" = "Wyze"
# Google / Nest
"F4F5D8" = "Google/Nest"; "3C5AB4" = "Google/Nest"; "B4F1DA" = "Google/Nest"
# LG / misc computers
"001CBF" = "LG"; "A4EE57" = "LG"; "0007B7" = "LG"
}
if ($map.ContainsKey($oui)) { return $map[$oui] }
return ""
}
function Test-PortAsync {
param([string]$ip, [int]$port)
# Start a connection attempt and return an object we can wait on later.
$client = New-Object System.Net.Sockets.TcpClient
$async = $client.BeginConnect($ip, $port, $null, $null)
return @{ Client = $client; Async = $async; Port = $port }
}
function Wait-PortAsync {
param($pending, [int]$timeoutMs)
# Wait for each started connection to complete or time out, returning named flags.
$result = @{ HTTP=$false; HTTPS=$false; Print=$false; IPP=$false; Cam=$false }
foreach ($p in $pending) {
$open = $false
try {
$open = $p.Async.AsyncWaitHandle.WaitOne($timeoutMs, $false) -and $p.Client.Connected
} catch {}
$p.Client.Close()
switch ($p.Port) {
80 { $result["HTTP"] = $open }
443 { $result["HTTPS"] = $open }
9100 { $result["Print"] = $open }
631 { $result["IPP"] = $open }
554 { $result["Cam"] = $open }
}
}
return $result
}
function Get-DeviceType {
param([string]$vendor, [string]$hostname, [hashtable]$ports, [string]$ip, [string]$gateway)
# Build an honest identification from the clues we have.
# Priority: gateway -> hostname -> ports -> vendor.
# 1. The router is usually the gateway address.
if ($gateway -and $ip -eq $gateway) { return "Router (gateway)" }
# 2. A revealing hostname often names the device.
if ($hostname) {
$h = $hostname.ToLower()
if ($h -match "brw" -and $h -match "[0-9a-f]{6}") { return "Printer (Brother)" }
if ($h -match "hp|laserjet|deskjet|officejet|envy") { return "Printer (HP)" }
if ($h -match "epson") { return "Printer (Epson)" }
if ($h -match "canon|mg[0-9]|pixma") { return "Printer (Canon)" }
if ($h -match "notebook|laptop|desktop|pc|workstation|server|ubuntu|centos") { return "Computer" }
}
# 3. What services it answers to.
if ($ports["Print"] -or $ports["IPP"]) { return "Printer" }
if ($ports["Cam"] -or $ports["ONVIF"]) { return "Camera" }
# 4. Vendor hint.
if ($vendor) {
switch -Regex ($vendor) {
"Brother|Lexmark|HP|Epson|Ricoh|Xerox|Canon" { return "Printer" }
"Dell|Lenovo|Acer|Intel|VMware|LG" { return "Computer" }
"Samsung" { return "Samsung device" }
"Amazon" { return "Amazon device (Echo/Fire)" }
"Arcadyan|Netgear|D-Link|Cisco|Asus|Linksys|TP-Link" { return "Router / network" }
"Hikvision|Wyze" { return "Camera" }
"Google/Nest" { return "Google/Nest device" }
"Apple" { return "Apple device" }
}
}
# 5. If it responds to web traffic but nothing else, it's likely a device
# with a web interface (camera, printer, or smart device).
if ($ports["HTTP"] -or $ports["HTTPS"]) { return "Device with web interface" }
# 6. Nothing reliable to say — be honest rather than guess.
return "Unidentified device"
}
function Get-DiscoveredDevices {
param([hashtable]$net)
$range = $net.Range
$gateway = $net.Gateway
$devices = @()
try {
$arp = arp -a
foreach ($line in $arp) {
if ($line -match '(\d+\.\d+\.\d+\.\d+)\s+([0-9a-fA-F-]{17})') {
$ip = $Matches[1]
$mac = $Matches[2].ToUpper()
# Skip broadcast / multicast / non-LAN noise
if ($ip -eq "255.255.255.255") { continue }
if ($mac -eq "FF-FF-FF-FF-FF-FF") { continue }
if ($ip -like "224.*" -or $ip -like "239.*") { continue }
$firstOctet = [int]($ip -split "\.")[0]
if ($firstOctet -lt 1 -or $firstOctet -gt 223) { continue }
if ($ip -like "169.254.*") { continue }
if ($range -and $ip -notlike "$range*") { continue }
$devices += [PSCustomObject]@{
IP = $ip
MAC = $mac
}
}
}
} catch {}
$devices = $devices | Sort-Object IP -Unique
# For each device, gather the identification clues.
# Port probes run in parallel per device so the scan stays reasonably quick.
$result = @($devices | ForEach-Object {
$ip = $_.IP
$mac = $_.MAC
$hn = ""
try {
$r = [System.Net.Dns]::GetHostEntry($ip)
if ($r -and $r.HostName -and $r.HostName -notlike "*in-addr*") { $hn = $r.HostName }
} catch {}
# Probe the most telling services — all connection attempts start at once,
# then we wait, so the 5 probes take about the time of a single one.
$pending = @(
(Test-PortAsync $ip 80),
(Test-PortAsync $ip 443),
(Test-PortAsync $ip 9100),
(Test-PortAsync $ip 631),
(Test-PortAsync $ip 554)
)
$portTasks = Wait-PortAsync $pending 200
$oui = ($mac -replace "-", "").Substring(0, 6)
$vendor = Get-VendorHint $oui
$type = Get-DeviceType $vendor $hn $portTasks $ip $gateway
[PSCustomObject]@{
IP = $ip
MAC = $mac
Vendor = if ($vendor) { $vendor } else { "unknown" }
Type = $type
Hostname = if ($hn) { $hn } else { "" }
}
})
return $result
}
function Prompt-ForDevices {
param($devices)
$selected = @()
$total = $devices.Count
if ($total -eq 0) {
Write-Host "No devices were found on this scan." -ForegroundColor Yellow
Write-Host "This can happen if the network is quiet or the scan is limited."
Write-Host "Try again after using the network a bit, or check that you are on your home network."
return $selected
}
Write-Host "Found $total device(s) on your network." -ForegroundColor Cyan
Write-Host ""
# Frank-proofing: let the user choose auto (accept all) or manual (review each).
$mode = ""
while ($mode -notmatch "^[am]$") {
Write-Host "How would you like to choose which devices to include?"
Write-Host " a = Auto: include all discovered devices"
Write-Host " m = Manual: review each device and pick (y/n)"
Write-Host "Choice (a/m): " -NoNewline
$mode = (Read-Host).Trim().ToLower()
}
if ($mode -eq "a") {
# Auto: accept everything the scan found.
$selected = @($devices)
Write-Host ""
Write-Host "Auto mode: including all $total device(s)." -ForegroundColor Green
return $selected
}
# Manual mode: review each device.
$included = 0
Write-Host ""
Write-Host "Manual mode: review each device." -ForegroundColor Cyan
Write-Host ""
foreach ($d in $devices) {
$name = $d.Type
if ($d.Hostname) { $name = $name + " [" + $d.Hostname + "]" }
$answer = ""
while ($answer -notmatch "^[yn]$") {
Write-Host (" {0} {1}" -f $d.IP, $name) -NoNewline
Write-Host " Include? (y/n): " -NoNewline -ForegroundColor Gray
$answer = (Read-Host).Trim().ToLower()
}
if ($answer -eq "y") {
$selected += $d
$included++
}
}
Write-Host ""
Write-Host "Included $included of $total device(s)." -ForegroundColor Green
return $selected
}
function Write-CustomHealthScript {
param($devices, [string]$scriptPath)
$sb = New-Object System.Text.StringBuilder
[void]$sb.AppendLine("<#")
[void]$sb.AppendLine(" Custom Network Health Audit")
[void]$sb.AppendLine(" Generated by The Windmill Club Windows Network Health Audit Builder")
[void]$sb.AppendLine(" Generated on: " + (Get-Date -Format "yyyy-MM-dd HH:mm"))
[void]$sb.AppendLine(" Devices checked: " + $devices.Count)
[void]$sb.AppendLine("#>")
[void]$sb.AppendLine("")
# Save the report to the DESKTOP — the one place every user knows how to find.
[void]$sb.AppendLine('$outFolder = [Environment]::GetFolderPath("Desktop")')
[void]$sb.AppendLine('if (-not (Test-Path $outFolder)) { $outFolder = (Get-Location).Path }')
[void]$sb.AppendLine('$reportFile = Join-Path $outFolder ("Windmill_NetworkHealthReport_" + (Get-Date -Format "yyyyMMdd_HHmm") + ".txt")')
[void]$sb.AppendLine('$report = New-Object System.Collections.Generic.List[string]')
[void]$sb.AppendLine('Add-Type -AssemblyName System.Collections')
[void]$sb.AppendLine("")
[void]$sb.AppendLine('Write-Host ""')
[void]$sb.AppendLine('Write-Host "========================================" -ForegroundColor DarkYellow')
[void]$sb.AppendLine('Write-Host " Custom Network Health Audit" -ForegroundColor DarkYellow')
[void]$sb.AppendLine('Write-Host "========================================" -ForegroundColor DarkYellow')
[void]$sb.AppendLine('$report.Add("Custom Network Health Audit")')
[void]$sb.AppendLine('$report.Add("Generated: " + (Get-Date -Format "yyyy-MM-dd HH:mm"))')
[void]$sb.AppendLine('$report.Add("========================================")')
[void]$sb.AppendLine('$report.Add("")')
[void]$sb.AppendLine('Write-Host ("Checking " + $(' + $devices.Count + ') + " device(s)...")')
[void]$sb.AppendLine('$script:okCount = 0')
[void]$sb.AppendLine("")
foreach ($d in $devices) {
[void]$sb.AppendLine('$ip = "' + $d.IP + '"')
[void]$sb.AppendLine('$name = "' + $d.Type + '"')
[void]$sb.AppendLine('$up = Test-Connection -ComputerName $ip -Count 1 -Quiet -ErrorAction SilentlyContinue')
[void]$sb.AppendLine('if ($up) {')
[void]$sb.AppendLine(' Write-Host (" {0,-15} {1,-28} [OK] reachable" -f $ip, $name) -ForegroundColor Green')
[void]$sb.AppendLine(' $script:okCount++')
[void]$sb.AppendLine(' $report.Add(("{0,-15} {1,-28} [OK] reachable" -f $ip, $name))')
[void]$sb.AppendLine('} else {')
[void]$sb.AppendLine(' Write-Host (" {0,-15} {1,-28} [DOWN] no response" -f $ip, $name) -ForegroundColor Red')
[void]$sb.AppendLine(' $report.Add(("{0,-15} {1,-28} [DOWN] no response" -f $ip, $name))')
[void]$sb.AppendLine('}')
[void]$sb.AppendLine("")
}
[void]$sb.AppendLine('$summary = "Checked {0} device(s): {1} reachable, {2} not responding." -f $(' + $devices.Count + '), $script:okCount, ($(' + $devices.Count + ') - $script:okCount)')
[void]$sb.AppendLine('Write-Host ""')
[void]$sb.AppendLine('Write-Host "----- FINISHED REPORT -----" -ForegroundColor DarkYellow')
[void]$sb.AppendLine('$report.Add("")')
[void]$sb.AppendLine('$report.Add("Summary: " + $summary)')
[void]$sb.AppendLine('$report.Add("A DOWN device may be asleep, off, or unreachable. Not every silent device is broken.")')
[void]$sb.AppendLine('$report | Set-Content -Path $reportFile -Encoding UTF8')
# Show the FULL finished report at the end, all in one block.
[void]$sb.AppendLine('Get-Content $reportFile | ForEach-Object { Write-Host $_ }')
[void]$sb.AppendLine('Write-Host ""')
[void]$sb.AppendLine('Write-Host ("Your report has been saved to your Desktop as: " + (Split-Path $reportFile -Leaf)) -ForegroundColor Green')
[void]$sb.AppendLine('Write-Host ("Full path: " + $reportFile) -ForegroundColor Gray')
[void]$sb.AppendLine('Write-Host "Opening it now so you can read it..." -ForegroundColor Cyan')
[void]$sb.AppendLine('Start-Process notepad -ArgumentList $reportFile')
[void]$sb.AppendLine('Write-Host ""')
[void]$sb.AppendLine('Write-Host "Press Enter to close this window..." -NoNewline')
# Read-Host without a visible prompt so the user just sees the message above.
[void]$sb.AppendLine('[void](Read-Host -Prompt "")')
Set-Content -Path $scriptPath -Value $sb.ToString() -Encoding UTF8
return $scriptPath
}
# ---------- Main ----------
Write-Header
$net = Get-NetworkRange
if (-not $net) {
Write-Host "Could not determine your network. Are you connected to a network?" -ForegroundColor Red
Read-Host "Press Enter to exit"
exit 1
}
Write-Host "Scanning your network ($($net.Range)*) for devices..." -ForegroundColor Cyan
Write-Host "(This reads your device list and probes a few services. It changes nothing.)"
Write-Host ""
$devices = Get-DiscoveredDevices $net
$chosen = Prompt-ForDevices $devices
if ($chosen.Count -eq 0) {
Write-Host "No devices were selected, so no health-check script was created." -ForegroundColor Yellow
Read-Host "Press Enter to exit"
exit 0
}
$scriptPath = Join-Path (Get-Location) ("NetworkHealthAudit_" + (Get-Date -Format "yyyyMMdd_HHmm") + ".ps1")
Write-CustomHealthScript $devices $scriptPath
Write-Host ""
Write-Host "Your custom network health-audit script has been created:" -ForegroundColor Green
Write-Host " $scriptPath"
Write-Host ""
Write-Host "Opening it in Notepad so you can see what it does..." -ForegroundColor Gray
Start-Process notepad -ArgumentList $scriptPath
# Frank-proofing: don't make the user figure out how to run a .ps1 file.
# Ask if they'd like us to run it for them right now.
Write-Host ""
$runIt = ""
while ($runIt -notmatch "^[yn]$") {
Write-Host "Would you like to run it now? (y/n): " -NoNewline
$runIt = (Read-Host).Trim().ToLower()
}
if ($runIt -eq "y") {
Write-Host ""
Write-Host "Running your custom health check now..." -ForegroundColor Cyan
Write-Host ""
Invoke-Expression (Get-Content $scriptPath -Raw)
} else {
Write-Host ""
Write-Host "No problem. To run it later, right-click the file and choose 'Run with PowerShell'." -ForegroundColor Yellow
}
Read-Host "Press Enter to exit"