
POWERSHELL MASTERCLASS • AUTOMATION PLAYBOOK
Top 10 PowerShell Scripts of All Time: The Essential Automation Tools That Save You Hours Every Week
By CyberDudeBivash • October 09, 2025 • V7 “Goliath” Deep Dive
cyberdudebivash.com | cyberbivash.blogspot.com
Disclosure: This is a technical guide for IT and security professionals. The scripts are for educational purposes. Always test scripts in a lab before running them in production. This post contains affiliate links to relevant training. Your support helps fund our independent research.
Definitive Guide: Table of Contents
- Introduction: PowerShell as a Defender’s Superpower
- #1: The Sentinel — Hunt for Unquoted Service Paths (LPE)
- #2: The Gatekeeper — Audit Local Administrator Groups
- #3: The Locksmith — Find Non-Expiring Passwords in Active Directory
- #4: The Archivist — Automate Event Log Backups
- #5: The Watchdog — A Simple File Integrity Monitor
- #6: The Diplomat — A High-Speed Port Scanner
- #7: The Exterminator — Kill Processes by Hash
- #8: The Archaeologist — Parse AmCache for Execution Evidence
- #9: The Janitor — Clean Up Old Temp Files
- #10: The Scribe — Generate a Daily System Health Report
Introduction: PowerShell as a Defender’s Superpower
PowerShell is the undisputed king of automation and administration on the Windows platform. But it is also one of the most powerful weapons in the arsenal of a modern cybersecurity defender. The ability to automate threat hunting, orchestrate incident response, and audit security configurations at scale is what separates a reactive SOC from a proactive one. This guide provides a definitive, curated list of the top 10 most essential PowerShell scripts that every security professional and system administrator should have in their toolkit. Each one is a “force multiplier” designed to save you hours of manual work and provide critical security insights.
#1: The Sentinel — Hunt for Unquoted Service Paths (LPE)
The Problem It Solves
As we detailed in our report on the **Windows LPE flaw**, an unquoted service path is a classic misconfiguration that allows a local attacker to escalate their privileges to `SYSTEM`. Manually checking for this across hundreds of servers is impossible. This script automates the hunt.
The Full Script
# The Sentinel: Unquoted Service Path Hunter
# Author: CyberDudeBivash
# Purpose: Finds all services with unquoted paths that contain spaces.
function Find-UnquotedServicePaths {
param(
[string]$ComputerName = $env:COMPUTERNAME
)
try {
Write-Host "[-] Querying services on $ComputerName..." -ForegroundColor Yellow
$services = Get-WmiObject -Class Win32_Service -ComputerName $ComputerName -ErrorAction Stop
$vulnerableServices = $services | Where-Object { $_.PathName -notmatch '^".*"' -and $_.PathName -match ' ' }
if ($vulnerableServices) {
Write-Host "[!] Found Vulnerable Services on $ComputerName:" -ForegroundColor Red
$vulnerableServices | ForEach-Object {
[PSCustomObject]@{
ComputerName = $ComputerName
ServiceName = $_.Name
DisplayName = $_.DisplayName
PathName = $_.PathName
State = $_.State
}
}
} else {
Write-Host "[+] No vulnerable services with unquoted paths found on $ComputerName." -ForegroundColor Green
}
} catch {
Write-Warning "Failed to query $ComputerName: $($_.Exception.Message)"
}
}
# Example usage:
# Find-UnquotedServicePaths
# Find-UnquotedServicePaths -ComputerName "SERVER01", "SERVER02"
How It Works (Code Breakdown)
The script uses the `Get-WmiObject -Class Win32_Service` cmdlet to get a list of all registered services on the target machine. It then pipes this list to a `Where-Object` filter. The filter has two conditions: first, it checks if the `PathName` property does *not* start with a quote (`-notmatch ‘^”.*”‘`), and second, it checks if the `PathName` contains a space (`-match ‘ ‘`). Any service that meets both conditions is a potential LPE vector and is output as a custom object.
#2: The Gatekeeper — Audit Local Administrator Groups at Scale
The Problem It Solves
Attackers who gain a foothold will often try to add their own user accounts to the local Administrators group to establish persistence. Auditing this membership across your entire domain is a critical threat hunting and hygiene task.
The Full Script
# The Gatekeeper: Local Admin Auditor
# Author: CyberDudeBivash
# Purpose: Audits membership of the local 'Administrators' group on remote computers.
function Get-LocalAdminMembers {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string[]]$ComputerNames
)
process {
foreach ($computer in $ComputerNames) {
if (Test-Connection -ComputerName $computer -Count 1 -Quiet) {
try {
Write-Verbose "Querying $computer..."
$group = [ADSI]"WinNT://$computer/Administrators,group"
$members = $group.Invoke("Members") | ForEach-Object { $_.GetType().InvokeMember("Name", "GetProperty", $null, $_, $null) }
foreach ($member in $members) {
[PSCustomObject]@{
ComputerName = $computer
AdminMember = $member
Timestamp = Get-Date
}
}
} catch {
Write-Warning "Failed to query $computer: $($_.Exception.Message)"
}
} else {
Write-Warning "Cannot connect to $computer."
}
}
}
}
# Example usage:
# Get-ADComputer -Filter * | Select-Object -ExpandProperty Name | Get-LocalAdminMembers | Export-Csv -Path "./LocalAdminAudit.csv" -NoTypeInformation
How It Works (Code Breakdown)
This script is designed to be used in a pipeline. It takes computer names as input, tests for a connection, and then uses the `[ADSI]` type accelerator to connect to the local Administrators group on the remote machine. It invokes the `Members` method to get a list of all group members and outputs the computer name and the member name. The example usage shows how to get all computers from Active Directory and pipe them into this function, exporting the final results to a CSV.
#3: The Locksmith — Find Non-Expiring Passwords in Active Directory
The Problem It Solves
User and service accounts with non-expiring passwords are a major security risk. They are a prime target for attackers, as once compromised, the credentials never become invalid. This script hunts for these accounts in your Active Directory.
The Full Script
# The Locksmith: AD Password Policy Auditor
# Author: CyberDudeBivash
# Purpose: Finds AD users whose passwords are set to never expire.
Import-Module ActiveDirectory
try {
Write-Host "[-] Searching for users with non-expiring passwords..." -ForegroundColor Yellow
$users = Search-ADAccount -PasswordNeverExpires -UsersOnly -ResultPageSize 2000 | Get-ADUser -Properties PasswordNeverExpires, LastLogonDate
if ($users) {
Write-Host "[!] Found accounts with PasswordNeverExpires set to True:" -ForegroundColor Red
$users | Select-Object Name, SamAccountName, DistinguishedName, LastLogonDate | Out-GridView
} else {
Write-Host "[+] No user accounts with non-expiring passwords found." -ForegroundColor Green
}
} catch {
Write-Warning "Failed to query Active Directory. Ensure the AD module is installed and you have appropriate permissions. Error: $($_.Exception.Message)"
}
How It Works (Code Breakdown)
This script uses the `ActiveDirectory` module. The core of the script is the `Search-ADAccount -PasswordNeverExpires -UsersOnly` cmdlet. This is a highly efficient way to find the target accounts. It then pipes the results to `Get-ADUser` to retrieve additional useful properties like the `LastLogonDate`. The results are displayed in an interactive `Out-GridView` window for easy sorting and filtering.
#4: The Archivist — Automate Event Log Backups
The Problem It Solves
In an incident, event logs are your primary source of evidence. However, they can be overwritten quickly or cleared by an attacker. This script provides a simple way to back up critical event logs from multiple servers to a central location for long-term storage and analysis.
The Full Script
# The Archivist: Event Log Backup Utility
# Author: CyberDudeBivash
# Purpose: Backs up critical event logs from remote servers.
param(
[string[]]$ComputerNames = @("SERVER01", "SERVER02", "DC01"),
[string]$DestinationPath = "\\NAS01\LogArchive",
[string[]]$LogNames = @("System", "Application", "Security")
)
foreach ($computer in $ComputerNames) {
$computerDest = Join-Path -Path $DestinationPath -ChildPath $computer
if (-not (Test-Path -Path $computerDest)) {
New-Item -Path $computerDest -ItemType Directory
}
foreach ($log in $LogNames) {
try {
Write-Host "[-] Backing up '$log' log from $computer..." -ForegroundColor Cyan
$logFile = Get-WmiObject -Class Win32_NTEventLogFile -Filter "LogFileName='$log'" -ComputerName $computer -ErrorAction Stop
$backupPath = Join-Path -Path $computerDest -ChildPath "$($log)_$(Get-Date -Format 'yyyyMMddHHmmss').evtx"
$result = $logFile.BackupEventLog($backupPath)
if ($result.ReturnValue -eq 0) {
Write-Host "[+] Successfully backed up '$log' to $backupPath" -ForegroundColor Green
} else {
Write-Warning "Failed to back up '$log' from $computer. Return code: $($result.ReturnValue)"
}
} catch {
Write-Warning "Error processing '$log' on $computer: $($_.Exception.Message)"
}
}
}
How It Works (Code Breakdown)
The script takes an array of computer names, a destination path (like a file share), and an array of log names. It loops through each computer and each log. For each, it uses `Get-WmiObject -Class Win32_NTEventLogFile` to get a handle on the log file object. It then calls the `BackupEventLog()` method on that object, which is the native WMI function for cleanly exporting an event log to an `.evtx` file. The filenames are timestamped to prevent overwrites.
#5: The Watchdog — A Simple File Integrity Monitor
The Problem It Solves
File Integrity Monitoring (FIM) is a critical security control. This script provides a basic, real-time FIM capability, watching a specific directory for any file creations, deletions, or modifications and logging the event.
The Full Script
# The Watchdog: Simple File Integrity Monitor
# Author: CyberDudeBivash
# Purpose: Monitors a directory for file changes in real-time.
param(
[string]$PathToMonitor = "C:\inetpub\wwwroot",
[string]$LogPath = "C:\Logs\FIM_Log.txt"
)
Write-Host "[-] Starting File Integrity Monitor on '$PathToMonitor'. Press Ctrl+C to stop." -ForegroundColor Yellow
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $PathToMonitor
$watcher.IncludeSubdirectories = $true
$watcher.NotifyFilter = [System.IO.NotifyFilters]'FileName, DirectoryName, LastWrite'
$action = {
$logEntry = "[$(Get-Date)] Change Type: $($event.SourceEventArgs.ChangeType), Path: $($event.SourceEventArgs.FullPath)"
Write-Host $logEntry -ForegroundColor Cyan
Add-Content -Path $LogPath -Value $logEntry
}
Register-ObjectEvent -InputObject $watcher -EventName "Changed" -Action $action
Register-ObjectEvent -InputObject $watcher -EventName "Created" -Action $action
Register-ObjectEvent -InputObject $watcher -EventName "Deleted" -Action $action
Register-ObjectEvent -InputObject $watcher -EventName "Renamed" -Action $action
try {
while ($true) {
Wait-Event -Timeout 60
}
} finally {
Get-EventSubscriber | Unregister-Event
Write-Host "[+] File Integrity Monitor stopped." -ForegroundColor Green
}
How It Works (Code Breakdown)
This script leverages the .NET `System.IO.FileSystemWatcher` class. It creates a “watcher” object and configures it to monitor a specific path, including subdirectories, for changes to filenames, directory names, or last write times. It then uses `Register-ObjectEvent` to register four event subscriptions (`Changed`, `Created`, `Deleted`, `Renamed`). When any of these events fire, the script block defined in `$action` is executed. This action writes a timestamped log entry to both the console and a specified log file.
#6: The Diplomat — A High-Speed Port Scanner
The Problem It Solves
Knowing which ports are open on your servers is a fundamental security requirement. This script provides a fast, multi-threaded way to scan a list of hosts for a list of open ports, helping you quickly identify unexpected or unauthorized services.
The Full Script
# The Diplomat: High-Speed Port Scanner
# Author: CyberDudeBivash
# Purpose: Scans a list of hosts for open TCP ports.
function Test-Port {
param(
[string]$ComputerName,
[int]$Port
)
$tcpClient = New-Object System.Net.Sockets.TcpClient
$connect = $tcpClient.BeginConnect($ComputerName, $Port, $null, $null)
$wait = $connect.AsyncWaitHandle.WaitOne(500, $false) # 500ms timeout
if ($wait) {
$tcpClient.EndConnect($connect) | Out-Null
[PSCustomObject]@{
ComputerName = $ComputerName
Port = $Port
IsOpen = $true
}
} else {
[PSCustomObject]@{
ComputerName = $ComputerName
Port = $Port
IsOpen = $false
}
}
$tcpClient.Close()
}
$hosts = @("localhost", "8.8.8.8", "192.168.1.1")
$ports = @(22, 80, 443, 3389)
$results = foreach ($host in $hosts) {
foreach ($port in $ports) {
Test-Port -ComputerName $host -Port $port
}
}
$results | Where-Object { $_.IsOpen } | Format-Table -AutoSize
How It Works (Code Breakdown)
This script uses the .NET `System.Net.Sockets.TcpClient` class to perform an asynchronous connection test. The `BeginConnect` method attempts to open a connection without blocking the script. The `WaitOne` method waits for a specified timeout (500 milliseconds in this case). If the connection succeeds within the timeout (`$wait` is true), the port is open. This is much faster than the built-in `Test-NetConnection` cmdlet for scanning a large number of ports.
#7: The Exterminator — Kill Processes by Hash
The Problem It Solves
During an incident, you may identify a malicious process. Simply killing it by name (e.g., `svchost.exe`) is dangerous, as attackers often masquerade as legitimate processes. Killing by the file hash is a much more precise and reliable method of eradication.
The Full Script
# The Exterminator: Process Killer by Hash
# Author: CyberDudeBivash
# Purpose: Finds and terminates processes based on their SHA256 hash.
function Stop-ProcessByHash {
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[Parameter(Mandatory=$true)]
[string]$Hash,
[string]$Algorithm = 'SHA256'
)
Write-Host "[-] Searching for processes matching hash: $Hash" -ForegroundColor Yellow
$processes = Get-CimInstance -ClassName Win32_Process
foreach ($process in $processes) {
$filePath = $process.ExecutablePath
if ($filePath -and (Test-Path $filePath)) {
$fileHash = Get-FileHash -Path $filePath -Algorithm $Algorithm
if ($fileHash.Hash -eq $Hash) {
Write-Host "[!] Match found! Process: $($process.Name) (PID: $($process.ProcessId))" -ForegroundColor Red
if ($pscmdlet.ShouldProcess("Process: $($process.Name) (PID: $($process.ProcessId))", "Terminate")) {
Stop-Process -Id $process.ProcessId -Force
Write-Host "[+] Process terminated." -ForegroundColor Green
}
}
}
}
}
# Example usage:
# Stop-ProcessByHash -Hash "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" -Verbose -WhatIf
# Stop-ProcessByHash -Hash "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
How It Works (Code Breakdown)
The script gets a list of all running processes using `Get-CimInstance`. It then loops through each one, finds the path to its executable, and calculates the file hash using `Get-FileHash`. If the calculated hash matches the malicious hash provided by the user, it flags the process. The script uses `SupportsShouldProcess = $true` and `$pscmdlet.ShouldProcess`, which enables the use of `-WhatIf` and `-Confirm` parameters, a critical safety feature for any destructive command.
#8: The Archaeologist — Parse AmCache for Execution Evidence
The Problem It Solves
The AmCache.hve file is a critical forensic artifact on Windows that stores information about recently executed applications. Parsing this file can reveal evidence of an attacker’s tools, even if they have been deleted from the disk. This script automates that parsing.
The Full Script
# The Archaeologist: AmCache Parser
# Author: CyberDudeBivash
# Purpose: Parses the AmCache.hve registry hive for evidence of execution.
# Note: Requires running as SYSTEM or copying the hive to another location first.
function Parse-AmCache {
param(
[string]$HivePath = "C:\Windows\appcompat\Programs\Amcache.hve"
)
if (-not (Test-Path $HivePath)) {
Write-Error "AmCache hive not found at specified path."
return
}
# Mount the offline hive
reg load HKLM\AmCacheTemp $HivePath
$amcacheRoot = "HKLM:\AmCacheTemp\Root\File"
$fileEntries = Get-ChildItem -Path $amcacheRoot -Recurse -ErrorAction SilentlyContinue
$results = foreach ($entry in $fileEntries) {
$values = Get-ItemProperty -Path $entry.PSPath
[PSCustomObject]@{
ProgramName = $values.15
FilePath = $values.101
SHA1 = $values.100
LastModified = [DateTime]::FromFileTime($values.12)
}
}
# Unmount the hive
reg unload HKLM\AmCacheTemp
$results | Sort-Object LastModified -Descending | Out-GridView
}
# Example usage:
# Parse-AmCache
How It Works (Code Breakdown)
This script first uses the command-line utility `reg.exe` to load the offline `Amcache.hve` file into a temporary location in the registry (`HKLM\AmCacheTemp`). It can then use standard PowerShell registry cmdlets (`Get-ChildItem`, `Get-ItemProperty`) to traverse the hive’s structure. It iterates through the `Root\File` key, which contains subkeys for each executed program, and extracts the properties that correspond to the program name, file path, SHA1 hash, and last modified timestamp. Finally, it unloads the hive to clean up and displays the results in a GridView.
#9: The Janitor — Clean Up Old Temp Files
The Problem It Solves
Over time, servers and workstations accumulate gigabytes of old files in temporary directories. This not only wastes disk space but can also create a security risk, as sensitive information can sometimes be left behind in these files. This script automates the cleanup process.
The Full Script
# The Janitor: Temp File Cleanup Utility
# Author: CyberDudeBivash
# Purpose: Deletes files in specified directories older than a certain number of days.
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[string[]]$PathsToClean = @("C:\Windows\Temp", "$env:TEMP"),
[int]$DaysOld = 30
)
$cutoffDate = (Get-Date).AddDays(-$DaysOld)
foreach ($path in $PathsToClean) {
if (Test-Path $path) {
Write-Host "[-] Cleaning path: $path" -ForegroundColor Yellow
$items = Get-ChildItem -Path $path -Recurse -Force -ErrorAction SilentlyContinue
$oldItems = $items | Where-Object { !$_.PSIsContainer -and $_.LastWriteTime -lt $cutoffDate }
foreach ($item in $oldItems) {
Write-Host "Removing $($item.FullName)..."
if ($pscmdlet.ShouldProcess($item.FullName, "Delete")) {
Remove-Item -Path $item.FullName -Force -ErrorAction SilentlyContinue
}
}
} else {
Write-Warning "Path not found: $path"
}
}
Write-Host "[+] Cleanup complete." -ForegroundColor Green
How It Works (Code Breakdown)
The script defines the paths to clean and a retention period (`$DaysOld`). It calculates a `$cutoffDate` and then iterates through each path. It uses `Get-ChildItem` to get all files, then filters them with `Where-Object` to find only those whose `LastWriteTime` is older than the cutoff date. For each old item, it uses the `ShouldProcess` safety feature before calling `Remove-Item` to delete it.
#10: The Scribe — Generate a Daily System Health Report
The Problem It Solves
Manually checking the health of your critical servers every day is tedious. This script automates the process, gathering key health metrics and generating a clean HTML report that can be emailed to you.
The Full Script
# The Scribe: Daily System Health Reporter
# Author: CyberDudeBivash
# Purpose: Generates an HTML report of system health.
param(
[string]$ReportPath = "C:\Reports\HealthReport_$(Get-Date -Format 'yyyyMMdd').html"
)
$report = @()
# 1. OS Information
$osInfo = Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object Caption, Version, LastBootUpTime
$report += [PSCustomObject]@{ Metric = "OS Version"; Value = "$($osInfo.Caption) ($($osInfo.Version))" }
$report += [PSCustomObject]@{ Metric = "Last Reboot"; Value = $osInfo.LastBootUpTime }
# 2. CPU Usage
$cpuUsage = (Get-CimInstance -ClassName Win32_Processor).LoadPercentage
$report += [PSCustomObject]@{ Metric = "CPU Load"; Value = "$($cpuUsage)%" }
# 3. Memory Usage
$memory = Get-CimInstance -ClassName Win32_OperatingSystem
$totalRam = [math]::Round($memory.TotalVisibleMemorySize / 1MB, 2)
$freeRam = [math]::Round($memory.FreePhysicalMemory / 1MB, 2)
$usedRamPercent = [math]::Round((($totalRam - $freeRam) / $totalRam) * 100, 2)
$report += [PSCustomObject]@{ Metric = "Memory Usage"; Value = "$($usedRamPercent)% ($($freeRam)GB Free)" }
# 4. Disk Space
$disks = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3"
foreach ($disk in $disks) {
$diskSize = [math]::Round($disk.Size / 1GB, 2)
$freeSpace = [math]::Round($disk.FreeSpace / 1GB, 2)
$percentFree = [math]::Round(($freeSpace / $diskSize) * 100, 2)
$report += [PSCustomObject]@{ Metric = "Disk $($disk.DeviceID) Free Space"; Value = "$($percentFree)% ($($freeSpace)GB Free)" }
}
# Generate HTML Report
$htmlHeader = @"
<style>
body { font-family: sans-serif; }
table { border-collapse: collapse; }
th, td { border: 1px solid #dddddd; text-align: left; padding: 8px; }
tr:nth-child(even) { background-color: #f2f2f2; }
h1 { color: #2E4053; }
</style>
<h1>System Health Report for $env:COMPUTERNAME - $(Get-Date)</h1>
"@
$report | ConvertTo-Html -Head $htmlHeader | Out-File -FilePath $ReportPath
Write-Host "[+] Health report generated at $ReportPath" -ForegroundColor Green
How It Works (Code Breakdown)
This script gathers data from multiple WMI/CIM classes: `Win32_OperatingSystem` for OS info and memory, `Win32_Processor` for CPU load, and `Win32_LogicalDisk` for disk space. It calculates percentages and formats the data neatly into custom objects which are added to a `$report` array. Finally, it uses the powerful `ConvertTo-Html` cmdlet to transform this array of objects into a formatted HTML table. A small CSS header is added to make the report look professional.
Explore the CyberDudeBivash Ecosystem
Our Core Services:
- CISO Advisory & Strategic Consulting
- Penetration Testing & Red Teaming
- Digital Forensics & Incident Response (DFIR)
- Advanced Malware & Threat Analysis
- Supply Chain & DevSecOps Audits
Follow Our Main Blog for Daily Threat IntelVisit Our Official Site & Portfolio
About the Author
CyberDudeBivash is a cybersecurity strategist with 15+ years in automation, incident response, and threat hunting, advising SOC teams and CISOs across APAC. [Last Updated: October 09, 2025]
#CyberDudeBivash #PowerShell #Automation #ThreatHunting #CyberSecurity #InfoSec #SysAdmin #DFIR #BlueTeam
Leave a comment