skip to content

PowerShell Essentials

Variables, operators, pipelines, Where-Object, ForEach-Object, Select-Object, sorting, grouping, remoting, jobs, CIM, registry, JSON, error handling, and common cmdlets.

19 min read 34 snippets 2d ago

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

Format-* cmdlets β€” controlling display#

# 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