Developer ToolsFree generatorPDF, Excel, Word exportManufacturing

PowerShell Windows Host Security Audit Script Generator

Before you can harden plant and ERP servers you need to know who is a local administrator, what is listening and when each host last patched. This generator builds a read-only script that collects exactly that into CSV files.

Fill in

Hosts reachable with WinRM from your admin workstation.

Generated code
Download:

Setup and inventory

# Read-only audit. Run from an admin workstation with WinRM access.
$servers = 'ERP-APP01,MES-DB01,HMI-SRV02' -split ','
$outDir  = 'C:\SecAudit'
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
$stamp = Get-Date -Format 'yyyyMMdd-HHmm'

Local administrators

$admins = Invoke-Command -ComputerName $servers -ScriptBlock {
  Get-LocalGroupMember -Group 'Administrators' | Select-Object Name, ObjectClass, PrincipalSource
}
$admins | Select-Object PSComputerName, Name, ObjectClass, PrincipalSource |
  Export-Csv "$outDir\local-admins-$stamp.csv" -NoTypeInformation

Listening ports and owning process

$ports = Invoke-Command -ComputerName $servers -ScriptBlock {
  Get-NetTCPConnection -State Listen | ForEach-Object {
    $p = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
    [pscustomobject]@{ Port = $_.LocalPort; Address = $_.LocalAddress; Process = $p.ProcessName }
  }
}
$ports | Select-Object PSComputerName, Port, Address, Process |
  Export-Csv "$outDir\listening-ports-$stamp.csv" -NoTypeInformation

Installed updates and staleness

$hotfixes = Invoke-Command -ComputerName $servers -ScriptBlock {
  Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 1 HotFixID, InstalledOn
}
$hotfixes | ForEach-Object {
  $age = if ($_.InstalledOn) { (New-TimeSpan -Start $_.InstalledOn -End (Get-Date)).Days } else { -1 }
  [pscustomobject]@{ Server = $_.PSComputerName; LastKB = $_.HotFixID; Installed = $_.InstalledOn; AgeDays = $age; Stale = ($age -gt 60) }
} | Export-Csv "$outDir\last-update-$stamp.csv" -NoTypeInformation

Non-Microsoft scheduled tasks

$tasks = Invoke-Command -ComputerName $servers -ScriptBlock {
  Get-ScheduledTask | Where-Object { $_.TaskPath -notlike '\Microsoft\*' } |
    Select-Object TaskName, TaskPath, State, @{n='RunAs';e={$_.Principal.UserId}}
}
$tasks | Select-Object PSComputerName, TaskName, TaskPath, State, RunAs |
  Export-Csv "$outDir\scheduled-tasks-$stamp.csv" -NoTypeInformation
Write-Host "Audit files written to $outDir"

Read-only commands, but test on one non-production host first. Requires PowerShell remoting enabled for the target hosts and an account with local administrator rights. Get-LocalGroupMember can fail when an orphaned domain SID is in the group, so review errors. Check the script with your change process before running it on plant servers.

Host hardening review

Optional review of your audit output and a prioritized fix list.

Optional. The tool and every export work without this. No spam.

How to use it

Enter your server names, an output folder and the age in days at which an update should be treated as stale. Copy the blocks into one script, review it, and run it from an admin workstation that has WinRM access. The script writes four time-stamped CSV files you can open in Excel and attach to an audit record or a risk register entry.

  • Start with three to five hosts
  • Keep CSVs in a restricted folder
  • Re-run monthly and compare to the last file

What to look for

In the administrator list, look for personal accounts, shared service accounts and domain groups that nobody remembers adding. In listening ports, look for remote management services, old FTP or Telnet, and database ports reachable from places they should not be. In the update file, a host months behind is a finding. In scheduled tasks, look for tasks running as high-privilege accounts from user-writable paths.

Fit for OT hosts

HMI and historian servers may run vendor-certified builds where patching follows a vendor schedule. A stale flag there is a prompt to check the vendor bulletin, not an instruction to patch immediately. Document the compensating controls, such as segmentation and application allow lists, next to the finding.

Common mistakes

Running a new script against a production line server without a change window is risky even when it only reads. Another error is leaving output CSVs on a shared drive: they are a map of your attack surface. Finally, do not treat a clean report as proof of security. It shows four facts about each host, nothing more.

Frequently Asked Questions

Is the script safe to run?

It only reads information and writes CSV files to the folder you name. Still test on a non-production host and follow your change process. Remoting and local admin rights are required, which is itself a sensitive permission.

Why use Invoke-Command?

It runs the block on each remote host through WinRM in parallel and returns objects tagged with PSComputerName, which keeps the exported CSV tidy and avoids installing anything on the targets.

What if WinRM is not enabled?

Enable it through group policy on managed servers, or run the script locally on each host with the server list reduced to localhost. Do not open WinRM broadly just to run an audit.

Can I extend it?

Yes. Add blocks for local users, installed software from the registry uninstall keys, or firewall profile status. Keep each block read-only and write its own CSV for easy comparison over time.

If you want the output of audits like this fed into a single risk register or ERP-linked asset record, Netray can help build that pipeline.