HackBlue

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.

11 min read · practice only with authorization

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.

Snippet
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, Path

Audit 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.

Snippet
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, Message

Lab: 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.

Snippet
$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

  1. Get

    Get-Service, Get-ScheduledTask, Get-CimInstance.

  2. Filter

    Where-Object on properties.

  3. Shape

    Select-Object with calculated properties.

  4. 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?