Tools · Python and Bash for security automation · 15 / 20
PowerShell scripting for defenders: objects, the pipeline, and exporting results
Understand that PowerShell passes objects, not text, filter with Where-Object, shape output with Select-Object, and export audit data to CSV or JSON.
Objects, not text
PowerShell commands output .NET objects with properties. Instead of parsing columns, you filter and sort by property names. Get-Member shows what properties an object has. This makes audit scripts more reliable than text parsing.
Get-Service | Get-Member -MemberType Property
Get-Service | Where-Object { $_.Status -eq 'Running' -and $_.StartType -eq 'Automatic' } | Select-Object Name, DisplayName
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 Name, Id, CPU, PathAudit examples
The examples below list local administrators, scheduled tasks not authored by Microsoft, services running from user-writable paths, and recent installations. Each produces objects you can export.
Get-LocalGroupMember -Group Administrators | Select-Object Name, ObjectClass, PrincipalSource
Get-ScheduledTask | Where-Object { $_.Author -notlike 'Microsoft*' -and $_.TaskPath -notlike '\Microsoft\*' } |
Select-Object TaskName, TaskPath, Author, @{n='Action'; e={ ($_.Actions | ForEach-Object { $_.Execute + ' ' + $_.Arguments }) -join '; ' }}
Get-CimInstance Win32_Service | Where-Object { $_.PathName -match '\\Users\\|\\ProgramData\\|\\Temp\\' } |
Select-Object Name, StartMode, State, PathName
Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='MsiInstaller'; Id=11707} -MaxEvents 20 | Select-Object TimeCreated, MessageLab: a host audit script
On a Windows lab VM, combine the commands above into one script that writes each section to CSV in an output folder named after the computer and date, plus a JSON summary with counts. Run it, open the CSVs, and add one more check of your choosing, such as listening ports with Get-NetTCPConnection -State Listen and their owning processes.
$out = Join-Path $PWD ("audit-{0}-{1:yyyyMMdd}" -f $env:COMPUTERNAME, (Get-Date))
New-Item -ItemType Directory -Path $out -Force | Out-Null
Get-LocalGroupMember Administrators | Export-Csv "$out\admins.csv" -NoTypeInformation
Get-NetTCPConnection -State Listen | Select-Object LocalAddress, LocalPort, OwningProcess, @{n='Process'; e={ (Get-Process -Id $_.OwningProcess).ProcessName }} |
Export-Csv "$out\listening.csv" -NoTypeInformation
[pscustomobject]@{ Host = $env:COMPUTERNAME; Admins = (Import-Csv "$out\admins.csv").Count } | ConvertTo-Json | Set-Content "$out\summary.json"Good habits
Use full cmdlet names in scripts instead of aliases, add [CmdletBinding()] and param blocks for reusable scripts, use -ErrorAction Stop with try and catch for critical steps, and sign scripts if your environment enforces execution policy with AllSigned. PowerShell 7 runs on Windows, Linux, and macOS, though some Windows-specific cmdlets are only in Windows PowerShell 5.1.
Diagram
PowerShell audit pipeline
- Get
Get-Service, Get-ScheduledTask, Get-CimInstance.
- Filter
Where-Object on properties.
- Shape
Select-Object with calculated properties.
- Export
Export-Csv or ConvertTo-Json for review.
Check yourself
1. Which cmdlet lists the properties of objects in the pipeline?
2. How do you keep only running services?
3. Why prefer full cmdlet names over aliases in scripts?