PowerShell Essentials#
Variables & types#
$name = "Jay"
$count = 42
$pi = 3.14159
$flag = $true
$nothing = $null
# Typed variables
[int]$port = 8080
[string]$label = "prod"
[bool]$enabled = $false
[datetime]$now = Get-Date
[string[]]$hosts = "web1","web2","web3"
[int[]]$ports = 80,443,8080
# Environment variables
$env:PATH
$env:USERPROFILE
$env:COMPUTERNAME
$env:USERNAME
# Automatic variables
$_ # current pipeline object
$PSItem # alias for $_
$args # arguments to a script/function
$MyInvocation # info about current script
$PSScriptRoot # directory of current script
$LASTEXITCODE # exit code of last native command
$? # $true if last command succeeded
$Error # array of recent errors; $Error[0] is the most recent
$null # explicit null value
$true/$false # Boolean literals
Operators#
# Comparison
-eq -ne -lt -gt -le -ge
# Case-insensitive string
-eq "hello" # default is case-insensitive
-ceq "hello" # case-sensitive
-ieq "hello" # explicit case-insensitive
# Pattern matching
-like "node*" # wildcard (* and ?)
-notlike "*.log"
-clike "Node*" # case-sensitive wildcard
-match "^\d+" # regex match; populates $Matches
-notmatch "error"
-cmatch "^Error" # case-sensitive regex
# Containment
-contains # array contains value: @(1,2,3) -contains 2
-notcontains
-in # value in array: 2 -in @(1,2,3)
-notin
# Type
-is [string] # type test
-isnot [int]
-as [int] # type coercion (returns $null on failure)
# Logical
-and -or -not !
-xor
# Arithmetic
+ - * / % # modulo
[Math]::Pow(2,10) # 1024
[Math]::Round(3.567, 2)
# String
-f # format operator: "{0:N2}" -f 3.14159 β "3.14"
+ # string concatenation
* # repeat: "ab" * 3 β "ababab"
# Assignment
= += -= *= /= %=
++ -- # increment/decrement
# Null coalescing (PowerShell 7+)
$x ?? "default" # return $x unless null
$x ??= "default" # assign if null
# Ternary (PowerShell 7+)
$x -gt 0 ? "pos" : "neg"
# Pipeline chain (PowerShell 7+)
cmd1 && cmd2 # run cmd2 only if cmd1 succeeds
cmd1 || cmd2 # run cmd2 only if cmd1 fails
Strings#
# Interpolation (double quotes)
"Hello, $name β today is $(Get-Date -Format 'yyyy-MM-dd')"
"Pi is {0:F4}" -f [Math]::PI # "Pi is 3.1416"
# Literal string (single quotes β no interpolation)
'SELECT * FROM users WHERE name = ''$name''' # escape ' by doubling
# Here-strings
$body = @"
Line 1
Line 2 with $name interpolated
"@
$raw = @'
No $interpolation here
'@
# Common methods
"hello world".ToUpper() # "HELLO WORLD"
"hello world".ToLower()
" trim me ".Trim() # "trim me"
" trim me ".TrimStart()
" trim me ".TrimEnd()
"a,b,c".Split(",") # @("a","b","c")
"a,b,c".Split(",", 2) # @("a","b,c") β max 2 parts
[string]::Join("|", @("x","y","z")) # "x|y|z"
"hello".Replace("l","r") # "herro"
"hello".Contains("ell") # $true
"hello".StartsWith("hel")
"hello".EndsWith("llo")
"hello".IndexOf("l") # 2
"hello".Substring(1, 3) # "ell"
"hello".PadLeft(10) # " hello"
"hello".PadLeft(10, '-') # "-----hello"
"hello".PadRight(10, '.') # "hello....."
" ".IsNullOrWhiteSpace(" ") # use static method
[string]::IsNullOrEmpty($var)
[string]::IsNullOrWhiteSpace($var)
# Regex
$text = "Order 12345 placed"
$text -match 'Order (\d+)' # $Matches[1] = "12345"
$text -replace '\d+', 'XXXXX' # "Order XXXXX placed"
[regex]::Matches($text, '\d+') | ForEach-Object { $_.Value }
Arrays#
$arr = @("a", "b", "c")
$arr = 1..10 # range
$arr += "d" # append (creates new array)
$arr[0] # first element
$arr[-1] # last element
$arr[1..3] # slice β elements 1, 2, 3
$arr.Count # length
$arr.Length
$arr.Contains("b") # $true
$arr.IndexOf("b") # 1
$arr -contains "b" # $true (operator form)
# Generic List (mutable, faster for many appends)
$list = [System.Collections.Generic.List[string]]::new()
$list.Add("item1")
$list.Add("item2")
$list.Remove("item1")
$list.Count
# Strongly typed array
[int[]]$nums = 1,2,3,4,5
# Array of custom objects
$people = @(
[pscustomobject]@{ Name = "Alice"; Age = 30 }
[pscustomobject]@{ Name = "Bob"; Age = 25 }
)
$people | Sort-Object Age
Hash tables#
$hash = @{
Name = "Jay"
Port = 8080
Active = $true
}
$hash["Name"] # access by key
$hash.Name # dot notation
$hash.Keys # all keys
$hash.Values # all values
$hash.ContainsKey("Port") # $true
$hash.ContainsValue(8080)
$hash.Count
$hash["NewKey"] = "value" # add/update
$hash.Remove("NewKey") # delete
# Ordered hash table (preserves insertion order)
$ordered = [ordered]@{
First = 1
Second = 2
Third = 3
}
# Enumerate
$hash.GetEnumerator() | ForEach-Object {
"$($_.Key) = $($_.Value)"
}
# Merge two hash tables
$merged = $hash + @{ Extra = "field" }
Where-Object β filtering#
# Script block form (full power)
Get-Process | Where-Object { $_.CPU -gt 100 }
Get-Process | Where-Object { $_.Name -like "node*" -and $_.WorkingSet -gt 100MB }
# Comparison form (PowerShell 3+, clean for simple cases)
Get-Process | Where-Object CPU -gt 100
Get-Process | Where-Object Name -like "node*"
Get-Process | Where-Object Name -eq "svchost"
Get-Process | Where-Object Name -match "^sql"
Get-Process | Where-Object Name -in @("chrome","firefox","edge")
Get-Process | Where-Object Name -notlike "idle*"
# Alias: ? or where
Get-Process | ? { $_.CPU -gt 10 }
# Filter on calculated / nested property
Get-Process | Where-Object { $_.MainWindowTitle -ne "" }
Get-Service | Where-Object { $_.Status -eq "Running" }
Get-ChildItem | Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) }
# Null / not-null check
Get-Process | Where-Object { $null -ne $_.Company }
Get-Process | Where-Object Company # truthy β excludes empty/null
# Multiple conditions
Get-Process | Where-Object {
$_.CPU -gt 10 -and
$_.WorkingSet -gt 50MB -and
$_.Name -notmatch "^idle"
}
# Filter on string content
Get-Content log.txt | Where-Object { $_ -match "ERROR" }
Get-Content log.txt | Where-Object { $_ -notmatch "^#" -and $_.Trim() -ne "" }
# All comparison operators work in Where-Object
# -eq -ne -lt -gt -le -ge -like -notlike -match -notmatch
# -contains -notcontains -in -notin -is -isnot
ForEach-Object β iterating the pipeline#
# Basic β process each item
1..5 | ForEach-Object { $_ * 2 }
# Alias: %
Get-Service | % { $_.DisplayName }
# Named parameter form (-Process)
Get-Service | ForEach-Object -Process { Start-Service $_.Name }
# Begin / Process / End blocks
Get-Process | ForEach-Object `
-Begin { $total = 0 } `
-Process { $total += $_.CPU } `
-End { "Total CPU: $total" }
# Access the current object
Get-ChildItem *.log | ForEach-Object {
$size = $_.Length / 1KB
"$($_.Name): $([Math]::Round($size,1)) KB"
}
# Parallel execution (PowerShell 7+ only)
1..10 | ForEach-Object -Parallel {
Start-Sleep -Seconds 1
"Processed $_"
} -ThrottleLimit 5 # max 5 concurrent threads
# Parallel with shared variable (using $using:)
$multiplier = 3
1..5 | ForEach-Object -Parallel {
$_ * $using:multiplier
}
# ForEach statement (not pipeline β faster for in-memory collections)
foreach ($item in $arr) {
Write-Host $item
}
# ForEach method on collection (fastest β no pipeline overhead)
@("a","b","c").ForEach({ $_.ToUpper() })
$arr.ForEach({ Write-Host $_ })
# Measure execution time
Measure-Command { 1..1000 | ForEach-Object { $_ * 2 } }
Select-Object β shaping output#
# Pick specific properties
Get-Process | Select-Object Name, Id, CPU, WorkingSet
# Rename and calculate β calculated properties
Get-Process | Select-Object Name, @{ Name="MemMB"; Expression={ [Math]::Round($_.WorkingSet/1MB,1) } }
Get-Process | Select-Object Name, @{ N="CPU%"; E={ "{0:N1}" -f $_.CPU } }
# First / Last n items
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10
Get-Process | Sort-Object CPU | Select-Object -Last 5
# Skip n items
Get-Process | Select-Object -Skip 5
# Skip until condition is met (PowerShell 6+)
Get-Process | Select-Object -SkipUntil { $_.CPU -gt 10 }
# Unique values
Get-Process | Select-Object -Unique Name
# Expand a single property to its value (unwrap from object)
Get-Process | Select-Object -ExpandProperty Name # returns strings, not objects
(Get-Service).Name # equivalent dot access
# Select all properties plus add new ones
Get-Process | Select-Object *, @{ N="MemGB"; E={ $_.WorkingSet/1GB } }
# Exclude properties
Get-Process | Select-Object -ExcludeProperty __*
# Wildcard in property names
Get-Process | Select-Object *Memory*
# Nested property
Get-Process | Select-Object Name, @{ N="Module"; E={ $_.Modules[0].ModuleName } }
# Index
Get-Process | Select-Object -Index 0, 2, 4 # elements at these positions
Sort-Object#
# Ascending (default)
Get-Process | Sort-Object CPU
# Descending
Get-Process | Sort-Object CPU -Descending
# Multiple sort keys
Get-Process | Sort-Object Name, CPU -Descending
# Case-sensitive sort
Get-ChildItem | Sort-Object Name -CaseSensitive
# Unique values during sort
Get-Process | Sort-Object Name -Unique
# Sort on calculated expression
Get-ChildItem | Sort-Object { $_.LastWriteTime }
Get-Process | Sort-Object { $_.WorkingSet / $_.CPU }
# Descending on one key, ascending on another
Get-Process | Sort-Object @{ E="Name"; Asc=$true }, @{ E="CPU"; Desc=$true }
# Sort strings as numbers
"10","9","20","1" | Sort-Object { [int]$_ }
Group-Object#
# Group processes by name
Get-Process | Group-Object Name
# Output: Name (group key), Count, Group (the objects)
Get-Process | Group-Object Name | Sort-Object Count -Descending
# Group files by extension
Get-ChildItem | Group-Object Extension
# Group with No-Element (just counts, saves memory)
Get-Process | Group-Object Name -NoElement | Sort-Object Count -Descending
# Access groups programmatically
$groups = Get-Process | Group-Object Name
foreach ($g in $groups) {
"$($g.Name): $($g.Count) instance(s)"
}
# Group then select from each group
Get-Process | Group-Object Name | ForEach-Object {
$_.Group | Sort-Object CPU -Descending | Select-Object -First 1
}
# Group on calculated property
Get-ChildItem | Group-Object { $_.LastWriteTime.Date }
Get-Process | Group-Object { [Math]::Round($_.CPU / 10) * 10 } # bucket by 10s
Measure-Object#
# Count
Get-Process | Measure-Object
# Sum, average, min, max
Get-Process | Measure-Object CPU -Sum -Average -Minimum -Maximum
# File sizes
Get-ChildItem C:\Windows -Recurse -File | Measure-Object Length -Sum -Average
# Word/line/character count on text
Get-Content log.txt | Measure-Object -Word -Line -Character
# Sum on calculated property (pipe through Select-Object first)
Get-Process | Select-Object @{ N="MemMB"; E={ $_.WorkingSet/1MB } } |
Measure-Object MemMB -Sum -Average
Compare-Object#
# Compare two sets of objects
$ref = Get-Content reference.txt
$diff = Get-Content current.txt
Compare-Object $ref $diff
# Output: InputObject, SideIndicator (<= in ref only, => in diff only)
Compare-Object $ref $diff -IncludeEqual # also show matching lines
# Compare processes on two machines
$local = Get-Process | Select-Object -ExpandProperty Name | Sort-Object -Unique
$remote = Invoke-Command server01 { Get-Process | Select-Object -Expand Name | Sort-Object -Unique }
Compare-Object $local $remote
# Suppress equal β show only differences (default behavior)
Compare-Object $ref $diff | Where-Object SideIndicator -eq "=>"
Tee-Object β split the pipeline#
# Send to variable AND continue pipeline
Get-Process | Tee-Object -Variable procs | Measure-Object
# Send to file AND continue pipeline
Get-Process | Tee-Object -FilePath C:\procs.txt | Select-Object Name, CPU
# Append to file
Get-Process | Tee-Object -FilePath C:\procs.txt -Append | Measure-Object
# Table
Get-Process | Format-Table Name, CPU, WorkingSet
Get-Process | Format-Table -AutoSize # auto-fit column widths
Get-Process | Format-Table -Wrap # wrap long values
Get-Process | Format-Table Name, @{ N="MemMB"; E={ [Math]::Round($_.WorkingSet/1MB,1) }; Width=10 }
Get-Process | Format-Table -GroupBy Name
# List
Get-Process | Format-List * # show all properties
Get-Service wuauserv | Format-List *
Get-Process | Format-List Name, CPU, @{ N="MemMB"; E={ $_.WorkingSet/1MB } }
# Wide (one-column display)
Get-Process | Format-Wide Name -Column 4 # 4 columns of names
# Custom
Get-Process | Format-Custom -Depth 2
# IMPORTANT: Format-* is for display only β never pipe Format-* output to other cmdlets
# Use Select-Object for downstream processing instead
Out-* cmdlets#
Out-Default # default output (implied at end of pipeline)
Out-Host # write to console (bypass variable capture)
Out-Null # discard output
Out-String # convert objects to string (useful for logs)
Out-File C:\out.txt # write to file
Out-File C:\out.txt -Append
Out-File C:\out.txt -Encoding UTF8
Out-GridView # interactive grid (Windows PowerShell / PowerShell with GUI)
Out-GridView -PassThru # select rows interactively, return selected objects
Out-Printer # send to default printer
# Redirect streams
Get-Process 2>&1 | Out-File all.log # capture both stdout and stderr
Get-Member β discover object structure#
# Show all properties and methods of objects in the pipeline
Get-Process | Get-Member
Get-Process | Get-Member -MemberType Property # properties only
Get-Process | Get-Member -MemberType Method # methods only
Get-Process | Get-Member -MemberType ScriptProperty
# Find what type an object is
(Get-Process)[0].GetType().FullName
(Get-Date).GetType()
# Static members of a type
[Math] | Get-Member -Static
[string] | Get-Member -Static
[DateTime]::Now
[Math]::Sqrt(144)
Splatting β cleaner parameter passing#
# Instead of long parameter lines:
$params = @{
Path = "C:\output.csv"
Delimiter = ","
NoTypeInformation = $true
Encoding = "UTF8"
}
Export-Csv @params
# Splatting with positional args (array splatting)
$args = @("server01", "-Port", 5985)
Test-NetConnection @args
# Combine splatting with additional parameters
$common = @{ ErrorAction = "Stop"; Verbose = $true }
Get-Service @common -Name "wuauserv"
Error handling#
# Terminating errors β caught by try/catch
try {
$content = Get-Content "C:\nonexistent.txt" -ErrorAction Stop
} catch [System.IO.FileNotFoundException] {
Write-Error "File not found: $_"
} catch [System.UnauthorizedAccessException] {
Write-Error "Access denied: $_"
} catch {
Write-Error "Unexpected: $($_.Exception.Message)"
} finally {
Write-Host "Always runs"
}
# Non-terminating β check $?
Get-Item "C:\maybe\exists.txt" -ErrorAction SilentlyContinue
if (-not $?) { Write-Warning "Not found" }
# Suppress errors entirely
Get-Item "C:\missing" -ErrorAction SilentlyContinue
# Treat all errors as terminating in this scope
$ErrorActionPreference = "Stop"
# Per-command error capture
Get-Item "C:\missing" -ErrorVariable myErr -ErrorAction SilentlyContinue
if ($myErr) { Write-Warning $myErr[0].Message }
# $Error automatic variable β array of all recent errors
$Error[0] # most recent error
$Error[0].InvocationInfo # where the error came from
$Error[0].Exception.Message # exception message
$Error.Clear() # clear the error log
# Check native command exit codes
git status
if ($LASTEXITCODE -ne 0) { throw "git failed with exit code $LASTEXITCODE" }
Functions and advanced parameters#
function Invoke-Deploy {
[CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
param(
[Parameter(Mandatory, Position=0)]
[string]$Environment,
[Parameter(Mandatory, ValueFromPipeline)]
[string[]]$Service,
[Parameter()]
[ValidateSet("blue","green","canary")]
[string]$Strategy = "blue",
[Parameter()]
[ValidateRange(1,100)]
[int]$Replicas = 2,
[Parameter()]
[ValidateScript({ Test-Path $_ })]
[string]$ConfigPath,
[switch]$Force
)
begin {
Write-Verbose "Starting deployment to $Environment"
}
process {
foreach ($svc in $Service) {
if ($PSCmdlet.ShouldProcess("$svc in $Environment", "Deploy")) {
Write-Verbose "Deploying $svc with strategy $Strategy"
[pscustomobject]@{
Service = $svc
Environment = $Environment
Strategy = $Strategy
Replicas = $Replicas
Timestamp = Get-Date
Status = "Deployed"
}
}
}
}
}
# Call it
"api","web" | Invoke-Deploy -Environment "prod" -Strategy "canary" -WhatIf
Remoting β Invoke-Command and sessions#
# Run a command on a remote machine (one-off)
Invoke-Command -ComputerName server01 -ScriptBlock { Get-Service | Where-Object Status -eq Running }
# Run on multiple machines in parallel
Invoke-Command -ComputerName server01,server02,server03 -ScriptBlock { hostname }
# Pass variables with $using:
$serviceName = "wuauserv"
Invoke-Command -ComputerName server01 -ScriptBlock { Get-Service $using:serviceName }
# Reusable persistent session (faster for multiple commands)
$session = New-PSSession -ComputerName server01
Invoke-Command -Session $session -ScriptBlock { Get-Process }
Invoke-Command -Session $session -ScriptBlock { Stop-Process -Name "notepad" -Force }
Remove-PSSession $session
# Interactive remote shell
Enter-PSSession -ComputerName server01
# ... run commands ...
Exit-PSSession
# Run a script file remotely
Invoke-Command -ComputerName server01 -FilePath C:\scripts\deploy.ps1
# Copy file to remote (using session)
$s = New-PSSession server01
Copy-Item C:\local\file.txt -Destination C:\remote\ -ToSession $s
Copy-Item C:\remote\file.txt -Destination C:\local\ -FromSession $s
Remove-PSSession $s
# Disconnect / reconnect sessions (PowerShell remoting over WinRM)
$s = New-PSSession server01 -Name "LongJob"
Invoke-Command -Session $s -ScriptBlock { Start-Sleep 60 } -AsJob
Disconnect-PSSession $s
# ... later ...
$s = Get-PSSession -ComputerName server01 | Connect-PSSession
Jobs β background execution#
# Start a background job
$job = Start-Job -ScriptBlock { Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 }
# Check job status
Get-Job
Get-Job -Id $job.Id
# Wait for job to finish
Wait-Job $job
Wait-Job $job -Timeout 30 # timeout in seconds
# Retrieve results
Receive-Job $job # get output (removes from buffer)
Receive-Job $job -Keep # get output without removing
# Clean up
Remove-Job $job
# Wait for all jobs and get results
$jobs = 1..5 | ForEach-Object { Start-Job -ScriptBlock { Start-Sleep 2; $using:_ * 10 } }
$results = $jobs | Wait-Job | Receive-Job
$jobs | Remove-Job
# Thread jobs (PowerShell 7+ β faster, in-process)
$job = Start-ThreadJob -ScriptBlock { Get-Process }
Receive-Job $job -Wait -AutoRemoveJob
# Scheduled jobs (persistent, survives logoff)
Register-ScheduledJob -Name "NightlyReport" -ScriptBlock {
Get-Process | Export-Csv C:\reports\procs.csv -NoTypeInformation
} -Trigger (New-JobTrigger -Daily -At "02:00")
CIM / WMI#
# Get CIM instance (preferred β uses WS-Man; works remote without DCOM)
Get-CimInstance -ClassName Win32_OperatingSystem
Get-CimInstance -ClassName Win32_ComputerSystem
Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType = 3"
# Query on remote machine
Get-CimInstance -ClassName Win32_Process -ComputerName server01
# Create a CIM session for multiple queries
$cs = New-CimSession -ComputerName server01,server02
Get-CimInstance -ClassName Win32_Service -CimSession $cs -Filter "State = 'Running'"
Remove-CimSession $cs
# Common CIM classes
Get-CimInstance Win32_BIOS
Get-CimInstance Win32_Processor
Get-CimInstance Win32_PhysicalMemory | Measure-Object Capacity -Sum
Get-CimInstance Win32_NetworkAdapterConfiguration -Filter "IPEnabled = True"
Get-CimInstance Win32_UserAccount
Get-CimInstance Win32_Product | Sort-Object Name # installed software (slow)
Get-CimInstance MSCluster_Cluster -Namespace root/MSCluster # failover cluster
# WMI query (older β uses DCOM)
Get-WmiObject -Class Win32_Service -Filter "State='Running'" | Select-Object Name,State
Registry#
# Navigate registry like a filesystem
Set-Location HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion
Get-ChildItem HKLM:\SOFTWARE
# Read a value
Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName
(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").CurrentBuild
# Set a value
Set-ItemProperty -Path "HKCU:\Software\MyApp" -Name "Theme" -Value "Dark"
# Create a key
New-Item -Path "HKCU:\Software\MyApp" -Force
# Create a value
New-ItemProperty -Path "HKCU:\Software\MyApp" -Name "Version" -Value "1.0" -PropertyType String
# Delete a value
Remove-ItemProperty -Path "HKCU:\Software\MyApp" -Name "OldValue"
# Delete a key (recursive)
Remove-Item -Path "HKCU:\Software\MyApp" -Recurse
# Test if key exists
Test-Path "HKCU:\Software\MyApp"
File system operations#
# List files
Get-ChildItem C:\logs
Get-ChildItem C:\logs -Filter "*.log"
Get-ChildItem C:\logs -Recurse -Filter "*.log"
Get-ChildItem C:\logs -Recurse -File # files only
Get-ChildItem C:\logs -Recurse -Directory # directories only
Get-ChildItem C:\logs -Include "*.log","*.txt" -Recurse
Get-ChildItem C:\logs -Exclude "*.bak" -Recurse
Get-ChildItem C:\logs | Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-1) }
# Read / write
Get-Content C:\file.txt
Get-Content C:\file.txt -Raw # single string, not array of lines
Get-Content C:\file.txt -Tail 100 # last 100 lines
Get-Content C:\file.txt -Wait # live tail
Set-Content C:\file.txt "content"
Add-Content C:\file.txt "append this"
Clear-Content C:\file.txt
# Copy, move, rename, delete
Copy-Item C:\src\file.txt C:\dst\
Copy-Item C:\src\ C:\dst\ -Recurse
Move-Item C:\old.txt C:\new.txt
Rename-Item C:\old.txt "new.txt"
Remove-Item C:\file.txt
Remove-Item C:\folder -Recurse -Force
# Create directories
New-Item -ItemType Directory C:\newfolder
New-Item -ItemType Directory C:\a\b\c -Force # create parents as needed
[System.IO.Directory]::CreateDirectory("C:\a\b\c")
# Paths
Join-Path "C:\logs" "app.log"
Split-Path "C:\logs\app.log" # parent: "C:\logs"
Split-Path "C:\logs\app.log" -Leaf # filename: "app.log"
[System.IO.Path]::GetExtension("app.log") # ".log"
[System.IO.Path]::GetFileNameWithoutExtension("app.log") # "app"
Resolve-Path .\relative\path # absolute path
Test-Path C:\file.txt
(Get-Item C:\file.txt).Length / 1MB # file size in MB
# Hash a file
Get-FileHash C:\file.exe -Algorithm SHA256
JSON, CSV, and XML#
# JSON
$obj = [pscustomobject]@{ Name="Jay"; Age=42; Active=$true }
$json = $obj | ConvertTo-Json
$json | Out-File data.json
$back = Get-Content data.json -Raw | ConvertFrom-Json
$back.Name # "Jay"
# Nested / deep JSON
$obj | ConvertTo-Json -Depth 10 # default depth is 2
# Invoke REST API
$response = Invoke-RestMethod "https://api.example.com/data"
$response.items | Select-Object Name, Id
# CSV
$data = @(
[pscustomobject]@{ Name="Alice"; Score=95 }
[pscustomobject]@{ Name="Bob"; Score=87 }
)
$data | Export-Csv output.csv -NoTypeInformation -Encoding UTF8
$imported = Import-Csv output.csv
$imported | Where-Object Score -gt 90
# CSV with custom delimiter
$data | Export-Csv output.csv -Delimiter ";" -NoTypeInformation
Import-Csv output.csv -Delimiter ";"
# XML
[xml]$xml = Get-Content config.xml
$xml.root.element # navigate nodes
$xml.SelectNodes("//item") # XPath
$xml.Save("config.xml") # save changes
Date and time#
$now = Get-Date
$now.ToString("yyyy-MM-dd HH:mm:ss")
$now.ToString("yyyyMMdd")
Get-Date -Format "yyyy-MM-dd"
Get-Date -Format "HH:mm"
Get-Date -UFormat "%Y-%m-%d" # Unix format string
# Arithmetic
$yesterday = (Get-Date).AddDays(-1)
$nextWeek = (Get-Date).AddDays(7)
$in1hr = (Get-Date).AddHours(1)
(Get-Date).AddMinutes(-30)
(Get-Date).AddMonths(1)
# Compare dates
(Get-Date) -gt [datetime]"2026-01-01"
([datetime]"2026-12-31" - (Get-Date)).Days # days until
# Parse a string
[datetime]::Parse("2026-04-24")
[datetime]::ParseExact("24/04/2026", "dd/MM/yyyy", $null)
# Timestamp for filenames
$stamp = Get-Date -Format "yyyyMMdd_HHmmss" # 20260424_153045
Modules#
# Find modules
Find-Module -Name *Azure* # search PowerShell Gallery
Find-Module SqlServer
# Install
Install-Module Az -Scope CurrentUser
Install-Module SqlServer -AllowClobber
# Import
Import-Module Az.Accounts
Import-Module SqlServer
# List installed
Get-Module -ListAvailable
Get-Module -ListAvailable *Az*
# List loaded
Get-Module
# Remove from session
Remove-Module SqlServer
# Module commands
Get-Command -Module Az.Compute
Get-Command -Module SqlServer | Select-Object Name, CommandType
Profile β persistent customization#
# View profile paths
$PROFILE # current user, current host
$PROFILE.CurrentUserAllHosts # current user, all hosts
$PROFILE.AllUsersCurrentHost # all users, current host
$PROFILE.AllUsersAllHosts # all users, all hosts
# Create/edit profile
notepad $PROFILE
code $PROFILE # VS Code
# Example profile content
Set-Alias g git
Set-Alias k kubectl
function prompt { "PS $($executionContext.SessionState.Path.CurrentLocation)> " }
$env:PATH += ";C:\tools"
Import-Module PSReadLine
Set-PSReadLineOption -PredictionSource History
Network cmdlets#
# TCP connectivity test
Test-NetConnection "github.com" -Port 443
Test-NetConnection "10.0.0.1" -Port 22 -InformationLevel Detailed
# DNS lookup
Resolve-DnsName "example.com"
Resolve-DnsName "example.com" -Type MX
Resolve-DnsName "example.com" -Server 8.8.8.8
# Listening ports
Get-NetTCPConnection -State Listen | Select-Object LocalPort,OwningProcess | Sort-Object LocalPort
Get-NetTCPConnection | Where-Object State -eq Established
# Network adapters
Get-NetAdapter | Select-Object Name, Status, LinkSpeed, MacAddress
Get-NetIPAddress | Where-Object AddressFamily -eq IPv4
# ARP table
Get-NetNeighbor -State Reachable
# Routes
Get-NetRoute -AddressFamily IPv4
# Firewall rules
Get-NetFirewallRule -Enabled True | Select-Object DisplayName, Direction, Action
New-NetFirewallRule -DisplayName "Allow SSH" -Direction Inbound -Protocol TCP -LocalPort 22 -Action Allow
# Download
Invoke-WebRequest "https://example.com/file.zip" -OutFile "$env:TEMP\file.zip"
Invoke-WebRequest "https://api.example.com/data" -Method GET -Headers @{ Authorization="Bearer $token" }
# POST with JSON body
$body = @{ user="jay"; pass="secret" } | ConvertTo-Json
Invoke-RestMethod "https://api.example.com/login" -Method POST -Body $body -ContentType "application/json"
Process and service management#
# Processes
Get-Process
Get-Process -Name "chrome"
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10
Stop-Process -Name "notepad" -Force
Stop-Process -Id 1234
Start-Process "notepad.exe"
Start-Process "msiexec.exe" -ArgumentList "/i setup.msi /quiet" -Wait -NoNewWindow
Wait-Process -Name "setup" -Timeout 120
# Services
Get-Service
Get-Service -Name "wuauserv"
Get-Service | Where-Object Status -eq Stopped
Start-Service "wuauserv"
Stop-Service "wuauserv" -Force
Restart-Service "wuauserv"
Set-Service "wuauserv" -StartupType Automatic
New-Service -Name "MySvc" -BinaryPathName "C:\svc\myservice.exe" -DisplayName "My Service" -StartupType Automatic
Event logs#
# Classic event log (Windows EventLog)
Get-EventLog -LogName Application -Newest 50
Get-EventLog -LogName System -EntryType Error -Newest 20
Get-EventLog -LogName Application -Source "MSSQLSERVER" -Newest 100
Get-EventLog -LogName Application -After (Get-Date).AddHours(-1)
# Modern event log (Get-WinEvent β preferred)
Get-WinEvent -LogName Application -MaxEvents 50
Get-WinEvent -LogName System -FilterHashtable @{ Level=2; StartTime=(Get-Date).AddDays(-1) }
Get-WinEvent -FilterHashtable @{
LogName = "Application"
Id = 1000,1001
StartTime = (Get-Date).AddDays(-7)
}
# Search event message text
Get-WinEvent -LogName Application -MaxEvents 1000 |
Where-Object { $_.Message -match "failed" }
# Write to event log
New-EventLog -LogName Application -Source "MyScript"
Write-EventLog -LogName Application -Source "MyScript" -EventId 1001 -EntryType Information -Message "Script started"
Scheduled tasks#
# List tasks
Get-ScheduledTask
Get-ScheduledTask -TaskPath "\MyApp\"
Get-ScheduledTask | Where-Object State -eq Running
# Create a task
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-File C:\scripts\daily.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At "06:00"
$setting = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Hours 1)
Register-ScheduledTask -TaskName "DailyReport" -TaskPath "\MyApp\" `
-Action $action -Trigger $trigger -Settings $setting -RunLevel Highest
# Run / stop / disable
Start-ScheduledTask -TaskName "DailyReport" -TaskPath "\MyApp\"
Stop-ScheduledTask -TaskName "DailyReport" -TaskPath "\MyApp\"
Disable-ScheduledTask -TaskName "DailyReport" -TaskPath "\MyApp\"
Enable-ScheduledTask -TaskName "DailyReport" -TaskPath "\MyApp\"
Unregister-ScheduledTask -TaskName "DailyReport" -Confirm:$false
Credentials#
# Prompt user for credential
$cred = Get-Credential
$cred = Get-Credential -UserName "DOMAIN\user" -Message "Enter admin password"
# Use credential
Invoke-Command -ComputerName server01 -Credential $cred -ScriptBlock { whoami }
# Store credential securely (current user only)
$cred.Password | ConvertFrom-SecureString | Set-Content C:\cred.txt
# Reload credential
$user = "DOMAIN\user"
$pass = Get-Content C:\cred.txt | ConvertTo-SecureString
$cred = New-Object PSCredential($user, $pass)
# Plain text to SecureString (avoid in production)
$ss = ConvertTo-SecureString "plaintext" -AsPlainText -Force
Useful one-liners#
# Top 10 processes by CPU
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 Name,Id,CPU,@{N="MemMB";E={[Math]::Round($_.WorkingSet/1MB,1)}}
# All stopped services that should be automatic
Get-Service | Where-Object { $_.StartType -eq "Automatic" -and $_.Status -eq "Stopped" }
# Disk space on all drives
Get-PSDrive -PSProvider FileSystem | Select-Object Name,@{N="FreeGB";E={[Math]::Round($_.Free/1GB,2)}},@{N="UsedGB";E={[Math]::Round($_.Used/1GB,2)}}
# Files modified in the last 24 hours
Get-ChildItem C:\logs -Recurse -File | Where-Object LastWriteTime -gt (Get-Date).AddDays(-1)
# Largest files in a directory
Get-ChildItem C:\Windows -Recurse -File -ErrorAction SilentlyContinue |
Sort-Object Length -Descending | Select-Object -First 20 FullName,@{N="MB";E={[Math]::Round($_.Length/1MB,1)}}
# Kill all chrome processes
Get-Process chrome -ErrorAction SilentlyContinue | Stop-Process -Force
# Count lines in all log files
Get-ChildItem C:\logs -Filter *.log | ForEach-Object { @{File=$_.Name; Lines=(Get-Content $_).Count} }
# Test multiple ports on a host
80,443,8080,8443 | ForEach-Object { Test-NetConnection "example.com" -Port $_ -WarningAction SilentlyContinue } |
Select-Object RemotePort,TcpTestSucceeded
# Find text in files
Select-String -Path "C:\logs\*.log" -Pattern "ERROR" -List | Select-Object Filename,LineNumber,Line
# Export running services to CSV
Get-Service | Where-Object Status -eq Running | Export-Csv services.csv -NoTypeInformation
# Restart multiple services if stopped
"wuauserv","BITS","cryptsvc" | ForEach-Object {
$s = Get-Service $_ -ErrorAction SilentlyContinue
if ($s -and $s.Status -ne "Running") { Start-Service $_ }
}
# Get local admins group members
Get-LocalGroupMember -Group "Administrators" | Select-Object Name,ObjectClass
# Archive logs older than 30 days
Get-ChildItem C:\logs -Filter *.log | Where-Object LastWriteTime -lt (Get-Date).AddDays(-30) |
ForEach-Object { Move-Item $_.FullName "C:\archive\" }
# Count unique IP addresses in an IIS log
Get-Content C:\inetpub\logs\*.log | Where-Object { $_ -notmatch "^#" } |
ForEach-Object { ($_ -split " ")[2] } | Group-Object -NoElement | Sort-Object Count -Descending
# Unzip all zip files in a directory
Get-ChildItem C:\downloads -Filter *.zip | ForEach-Object {
Expand-Archive $_.FullName -DestinationPath "C:\extracted\$($_.BaseName)" -Force
}
# Get Windows version info
(Get-CimInstance Win32_OperatingSystem) | Select-Object Caption,Version,BuildNumber,OSArchitecture
# Live tail a log file
Get-Content C:\logs\app.log -Wait -Tail 50