# host-agent.ps1 — Windows host metrics → one JSON payload per run. # Create one capture probe for this machine, paste its URL below, and # schedule this script (e.g. every 5 min). The probe page builds a # dashboard from the payload automatically. $Url = "YOUR_URL" # bare capture URL for THIS host $Services = @("Spooler","MSSQLSERVER") # services to report (edit me) $ErrorActionPreference = "SilentlyContinue" function Round2($n){ [math]::Round([double]$n, 2) } # ---- host / uptime ------------------------------------------------------- $os = Get-CimInstance Win32_OperatingSystem $boot = $os.LastBootUpTime $uptimeSec = [int]((Get-Date) - $boot).TotalSeconds # ---- cpu / memory -------------------------------------------------------- $cpuLoad = (Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average $cores = (Get-CimInstance Win32_Processor | Measure-Object -Property NumberOfLogicalProcessors -Sum).Sum $memTotalGb = Round2 ($os.TotalVisibleMemorySize / 1MB) $memFreeGb = Round2 ($os.FreePhysicalMemory / 1MB) $memUsedGb = Round2 ($memTotalGb - $memFreeGb) $memUsedPct = if ($memTotalGb -gt 0) { Round2 (100 * $memUsedGb / $memTotalGb) } else { 0 } # ---- disks --------------------------------------------------------------- $disks = @(); $volumes = @{}; $anyUnhealthy = $false; $minFreePct = 100 foreach ($v in (Get-Volume | Where-Object { $_.DriveLetter -and $_.Size -gt 0 })) { $sizeGb = Round2 ($v.Size / 1GB) $freeGb = Round2 ($v.SizeRemaining / 1GB) $freePct = if ($v.Size -gt 0) { Round2 (100 * $v.SizeRemaining / $v.Size) } else { 0 } $usedPct = Round2 (100 - $freePct) # Map the volume back to its physical disk for media/bus type + SMART health. $media = "Unknown"; $bus = $null; $health = $null $part = Get-Partition -DriveLetter $v.DriveLetter if ($part) { $d = Get-Disk -Number $part.DiskNumber if ($d) { $bus = "$($d.BusType)"; $health = "$($d.HealthStatus)" } $pd = Get-PhysicalDisk | Where-Object { "$($_.DeviceId)" -eq "$($part.DiskNumber)" } | Select-Object -First 1 if ($pd) { $media = "$($pd.MediaType)"; if ($pd.HealthStatus) { $health = "$($pd.HealthStatus)" } } } if ($health -and $health -ne "Healthy") { $anyUnhealthy = $true } if ($freePct -lt $minFreePct) { $minFreePct = $freePct } $disks += [ordered]@{ letter = "$($v.DriveLetter)"; label = "$($v.FileSystemLabel)"; size_gb = $sizeGb; free_gb = $freeGb; used_pct = $usedPct; media_type = $media; bus_type = $bus; health = $health } $volumes["$($v.DriveLetter)"] = [ordered]@{ free_gb = $freeGb; free_pct = $freePct; used_pct = $usedPct } } # ---- services ------------------------------------------------------------ $svc = @() foreach ($name in $Services) { $s = Get-Service -Name $name if ($s) { $svc += [ordered]@{ name = "$($s.Name)"; display = "$($s.DisplayName)"; status = "$($s.Status)"; start_type = "$($s.StartType)" } } else { $svc += [ordered]@{ name = $name; display = $name; status = "NotFound"; start_type = $null } } } # ---- pending reboot ------------------------------------------------------ $pendingReboot = $false if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending") { $pendingReboot = $true } if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired") { $pendingReboot = $true } if (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations) { $pendingReboot = $true } # ---- pending Windows Updates -------------------------------------------- $updPending = $null; $updSecurity = $null try { $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $result = $searcher.Search("IsInstalled=0 and IsHidden=0") $updPending = $result.Updates.Count $updSecurity = (@($result.Updates | Where-Object { $_.Categories | Where-Object { $_.Name -eq "Security Updates" } })).Count } catch { } # ---- event-log errors (last 24h) ---------------------------------------- $since = (Get-Date).AddHours(-24) $sysErr = @(Get-WinEvent -FilterHashtable @{ LogName="System"; Level=2; StartTime=$since }).Count $appErr = @(Get-WinEvent -FilterHashtable @{ LogName="Application"; Level=2; StartTime=$since }).Count # ---- assemble + post ----------------------------------------------------- $payload = [ordered]@{ agent = "win-host"; v = 1; ts = (Get-Date).ToString("o") host = [ordered]@{ name = $env:COMPUTERNAME; os = "$($os.Caption)"; uptime_sec = $uptimeSec; boot_time = $boot.ToString("o") } cpu = [ordered]@{ load_pct = [double]$cpuLoad; cores = [int]$cores } memory = [ordered]@{ total_gb = $memTotalGb; used_gb = $memUsedGb; used_pct = $memUsedPct } disks = $disks volumes = $volumes min_disk_free_pct = $minFreePct any_disk_unhealthy = $anyUnhealthy services = $svc pending_reboot = $pendingReboot updates = [ordered]@{ pending = $updPending; security_pending = $updSecurity } event_errors = [ordered]@{ system_24h = $sysErr; application_24h = $appErr } } $json = $payload | ConvertTo-Json -Depth 6 -Compress Invoke-RestMethod -Uri $Url -Method Post -ContentType "application/json" -Body $json