PowerShell has evolved significantly over the years and introduced powerful language features that make scripting more efficient and readable. Whether you’re a system administrator, DevOps engineer, or developer - mastering these language constructs will dramatically improve your PowerShell productivity.


Variables and Data Types

Basic Variable Assignment

# Simple assignment
$name = "John Doe"
$age = 30
$isActive = $true

# Type constraints
[string]$username = "admin"
[int]$count = 0
[datetime]$startTime = Get-Date

Arrays and Collections

# Arrays
$servers = @("web01", "web02", "db01")
$numbers = 1..10

# Hash tables
$configuration = @{
    Server = "localhost"
    Port = 8080
    SSL = $true
}

# Accessing elements
Write-Host $servers[0]                    # web01
Write-Host $configuration.Server          # localhost
Write-Host $configuration["Port"]         # 8080

Advanced Collections

# ArrayList for dynamic arrays
$list = [System.Collections.ArrayList]@()
$list.Add("element1") | Out-Null
$list.AddRange(@("element2", "element3"))

# Ordered hash table
$orderedConfig = [ordered]@{
    First = "value1"
    Second = "value2"
}

Operators

Comparison Operators

# Basic comparisons
$a -eq $b       # Equal
$a -ne $b       # Not equal
$a -gt $b       # Greater than
$a -lt $b       # Less than
$a -ge $b       # Greater or equal
$a -le $b       # Less or equal

# String operations
$text -like "*pattern*"        # Wildcard matching
$text -match "regex"           # Regular expression
$text -contains "substring"    # Contains check
$text -in @("a", "b", "c")     # Membership test

Logical Operators

# Logical operations
$condition1 -and $condition2
$condition1 -or $condition2
-not $condition

# Practical example
if (($age -gt 18) -and ($hasLicense -eq $true)) {
    Write-Host "May drive car"
}

Modern Operators (PowerShell 7+)

# Ternary operator
$status = ($score -gt 80) ? "Passed" : "Failed"

# Null coalescing operators
$username = $env:USER ?? $env:USERNAME ?? "Unknown"

# Null coalescing assignment
$configuration.Timeout ??= 30

Conditional Logic

If-Else Statements

# Basic if-else
if ($score -gt 90) {
    Write-Host "Excellent!"
} elseif ($score -gt 70) {
    Write-Host "Good work!"
} else {
    Write-Host "Keep practicing!"
}

# One-liner conditions
if ($debug) { Write-Host "Debug mode activated" }

Switch Statements

# Basic switch
switch ($status) {
    "Running" { Write-Host "Service is active" }
    "Stopped" { Write-Host "Service is inactive" }
    "Paused"  { Write-Host "Service is paused" }
    default   { Write-Host "Unknown status" }
}

# Advanced switch with regex
switch -Regex ($logEntry) {
    "ERROR"   { Write-Host "Error found" -ForegroundColor Red }
    "WARNING" { Write-Host "Warning found" -ForegroundColor Yellow }
    "INFO"    { Write-Host "Information logged" }
}

Loops and Iteration

ForEach Loops

# Traditional foreach
foreach ($server in $servers) {
    Test-Connection -ComputerName $server -Count 1
}

# Pipeline foreach
$servers | ForEach-Object {
    Write-Host "Processing $_"
    Test-Connection -ComputerName $_ -Count 1
}

# Parallel processing (PowerShell 7+)
$servers | ForEach-Object -Parallel {
    Test-Connection -ComputerName $_ -Count 1
} -ThrottleLimit 5

For and While Loops

# For loop
for ($i = 0; $i -lt 10; $i++) {
    Write-Host "Iteration $i"
}

# While loop
$counter = 0
while ($counter -lt 5) {
    Write-Host "Counter: $counter"
    $counter++
}

# Do-While loop
do {
    $input = Read-Host "Enter 'quit' to exit"
} while ($input -ne "quit")

Functions and Parameters

Basic Functions

# Simple function
function Get-SystemInfo {
    $os = Get-CimInstance -ClassName Win32_OperatingSystem
    return @{
        ComputerName = $env:COMPUTERNAME
        OperatingSystem = $os.Caption
        FreeMemory = [math]::Round($os.FreePhysicalMemory / 1MB, 2)
    }
}

# Call function
$info = Get-SystemInfo
Write-Host "Computer: $($info.ComputerName)"

Advanced Functions with Parameters

function Test-ServiceStatus {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string[]]$ServiceName,
        
        [ValidateSet("Running", "Stopped", "All")]
        [string]$Status = "All",
        
        [switch]$Detailed
    )
    
    process {
        foreach ($service in $ServiceName) {
            $svc = Get-Service -Name $service -ErrorAction SilentlyContinue
            
            if ($svc) {
                if ($Status -eq "All" -or $svc.Status -eq $Status) {
                    if ($Detailed) {
                        $svc | Select-Object Name, Status, StartType, DisplayName
                    } else {
                        "$($svc.Name): $($svc.Status)"
                    }
                }
            } else {
                Write-Warning "Service '$service' not found"
            }
        }
    }
}

# Usage examples
Test-ServiceStatus -ServiceName "Spooler", "BITS"
"Spooler", "BITS" | Test-ServiceStatus -Status Running -Detailed

Error Handling

Try-Catch-Finally

try {
    $content = Get-Content -Path "C:\nonexistent.txt" -ErrorAction Stop
    Write-Host "File read successfully"
} catch [System.IO.FileNotFoundException] {
    Write-Host "File not found - creating new file"
    New-Item -Path "C:\nonexistent.txt" -ItemType File
} catch {
    Write-Host "An unexpected error occurred: $($_.Exception.Message)"
} finally {
    Write-Host "Cleanup completed"
}

Error Action Settings

# Set global error preference
$ErrorActionPreference = "Stop"

# Per-command error handling
Get-Process -Name "NonExistent" -ErrorAction SilentlyContinue
Get-Service -Name "Invalid" -ErrorAction Ignore

# Capture errors
Get-Process -Name "NonExistent" -ErrorAction SilentlyContinue -ErrorVariable myErrors
if ($myErrors) {
    Write-Host "Errors occurred: $($myErrors.Count)"
}

Object Manipulation

Creating Custom Objects

# PSCustomObject (recommended)
$server = [PSCustomObject]@{
    Name = "WEB01"
    IP = "192.168.1.10"
    Role = "WebServer"
    LastReboot = (Get-Date).AddDays(-7)
}

# Add methods to objects
$server | Add-Member -MemberType ScriptMethod -Name "GetUptime" -Value {
    (Get-Date) - $this.LastReboot
}

# Use method
$uptime = $server.GetUptime()
Write-Host "Server uptime: $($uptime.Days) days"

Object Filtering and Selection

# Get running services with specific properties
$services = Get-Service | Where-Object {
    $_.Status -eq "Running" -and $_.Name -like "Win*"
} | Select-Object Name, Status, StartType

# Group and sort objects
$processes = Get-Process | Group-Object ProcessName | 
    Sort-Object Count -Descending | Select-Object -First 5

# Measure objects
$memoryUsage = Get-Process | Measure-Object WorkingSet -Sum -Average
Write-Host "Total memory: $([math]::Round($memoryUsage.Sum / 1MB, 2)) MB"

Pipeline Operations

Pipeline Basics

# Chain commands with pipeline
Get-Service | 
    Where-Object Status -eq "Running" |
    Sort-Object Name |
    Select-Object Name, Status |
    Format-Table -AutoSize

# Pipeline variable $_
Get-ChildItem -Path C:\Logs -Filter "*.log" |
    ForEach-Object {
        $size = [math]::Round($_.Length / 1KB, 2)
        Write-Host "$($_.Name): ${size} KB"
    }

Advanced Pipeline Techniques

# Tee-Object for pipeline branching
Get-Process |
    Tee-Object -FilePath "processes.txt" |
    Where-Object CPU -gt 100 |
    Sort-Object CPU -Descending

# Out-GridView for interactive filtering
Get-EventLog -LogName System -Newest 100 |
    Out-GridView -Title "System Events" -PassThru |
    Export-Csv -Path "selected_events.csv"

Classes and Methods

Defining Classes

class Server {
    [string]$Name
    [string]$IP
    [string]$Role
    [datetime]$LastReboot
    
    # Constructor
    Server([string]$name, [string]$ip, [string]$role) {
        $this.Name = $name
        $this.IP = $ip
        $this.Role = $role
        $this.LastReboot = Get-Date
    }
    
    # Methods
    [timespan]GetUptime() {
        return (Get-Date) - $this.LastReboot
    }
    
    [void]Restart() {
        Write-Host "Restarting server $($this.Name)..."
        $this.LastReboot = Get-Date
    }
    
    [string]ToString() {
        return "$($this.Name) ($($this.IP)) - $($this.Role)"
    }
}

# Use class
$webServer = [Server]::new("WEB01", "192.168.1.10", "WebServer")
Write-Host $webServer.ToString()
Write-Host "Uptime: $($webServer.GetUptime().TotalHours) hours"

Inheritance

class DatabaseServer : Server {
    [string]$DatabaseEngine
    [int]$Port
    
    DatabaseServer([string]$name, [string]$ip, [string]$engine, [int]$port) : base($name, $ip, "Database") {
        $this.DatabaseEngine = $engine
        $this.Port = $port
    }
    
    [void]BackupDatabase() {
        Write-Host "Backing up $($this.DatabaseEngine) database on $($this.Name)"
    }
}

$dbServer = [DatabaseServer]::new("DB01", "192.168.1.20", "SQL Server", 1433)
$dbServer.BackupDatabase()

Modern Language Features

Splatting

# Parameter splatting
$parameters = @{
    Path = "C:\Logs"
    Filter = "*.log"
    Recurse = $true
}
Get-ChildItem @parameters

# Array splatting
$arguments = @("notepad.exe", "C:\temp\file.txt")
Start-Process @arguments

String Interpolation and Here-Strings

# String interpolation
$name = "John"
$age = 30
Write-Host "Hello, $name! You are $age years old."

# Subexpression operator
Write-Host "Current time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"

# Here-strings for multiline text
$script = @"
Get-Service | Where-Object {
    $_.Status -eq "Running"
} | Select-Object Name, Status
"@

# Execute here-string as script block
Invoke-Expression $script

Regular Expressions

# Basic regex matching
$text = "Error: Connection failed at 2024-01-15 10:30:25"
if ($text -match "(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})") {
    Write-Host "Timestamp found: $($Matches[1])"
}

# Replace with regex
$logEntry = "ERROR: Database connection failed"
$cleanedEntry = $logEntry -replace "ERROR:", "Warning:"

# Split with regex
$data = "apple,banana;orange:grape"
$fruits = $data -split "[,;:]"

Pipeline Chain Operators (PowerShell 7+)

# Success chain (&&)
Get-Service -Name "Spooler" && Write-Host "Spooler service exists"

# Error chain (||)
Get-Service -Name "NonExistent" 2>$null || Write-Host "Service not found"

# Combine chains
Test-Path "C:\temp" && Write-Host "Temp exists" || Write-Host "Temp missing"

Practical Examples

System Monitoring Script

function Get-SystemHealth {
    [CmdletBinding()]
    param(
        [string[]]$ComputerName = $env:COMPUTERNAME,
        [int]$CPUThreshold = 80,
        [int]$MemoryThreshold = 90
    )
    
    $results = foreach ($computer in $ComputerName) {
        try {
            # Get system information
            $os = Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $computer
            $cpu = Get-CimInstance -ClassName Win32_Processor -ComputerName $computer
            
            # Calculate metrics
            $memoryUsed = [math]::Round((($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / $os.TotalVisibleMemorySize) * 100, 2)
            $cpuUsage = Get-Counter "\Processor(_Total)\% Processor Time" -ComputerName $computer -SampleInterval 1 -MaxSamples 1 |
                Select-Object -ExpandProperty CounterSamples |
                Select-Object -ExpandProperty CookedValue
            
            # Create result object
            [PSCustomObject]@{
                ComputerName = $computer
                CPUUsage = [math]::Round($cpuUsage, 2)
                MemoryUsage = $memoryUsed
                Status = if ($cpuUsage -gt $CPUThreshold -or $memoryUsed -gt $MemoryThreshold) { "Warning" } else { "OK" }
                Timestamp = Get-Date
            }
        } catch {
            [PSCustomObject]@{
                ComputerName = $computer
                CPUUsage = "Error"
                MemoryUsage = "Error"
                Status = "Unreachable"
                Timestamp = Get-Date
            }
        }
    }
    
    return $results
}

# Usage
$healthCheck = Get-SystemHealth -ComputerName @("localhost", "server01") -CPUThreshold 70
$healthCheck | Format-Table -AutoSize

Log Analysis Script

function Analyze-Logs {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$LogPath,
        
        [string]$Pattern = "ERROR|WARNING|CRITICAL",
        
        [int]$LastHours = 24
    )
    
    $cutoffTime = (Get-Date).AddHours(-$LastHours)
    
    Get-ChildItem -Path $LogPath -Filter "*.log" |
        ForEach-Object {
            $content = Get-Content -Path $_.FullName
            $matches = $content | Where-Object { $_ -match $Pattern }
            
            if ($matches) {
                [PSCustomObject]@{
                    LogFile = $_.Name
                    ErrorCount = ($matches | Measure-Object).Count
                    Errors = $matches
                    LastModified = $_.LastWriteTime
                }
            }
        } |
        Where-Object { $_.LastModified -gt $cutoffTime } |
        Sort-Object ErrorCount -Descending
}

# Usage
$logAnalysis = Analyze-Logs -LogPath "C:\Logs" -LastHours 8
$logAnalysis | Select-Object LogFile, ErrorCount, LastModified | Format-Table

Best Practices and Tips

Performance Tips

# Use ArrayList instead of Array for frequent additions
$list = [System.Collections.ArrayList]@()
# Instead of: $array += $element (slow)
$list.Add($element) | Out-Null  # Much faster

# Use -contains instead of -in for single element checks
if ($servers -contains $targetServer) { }  # Faster
# Instead of: if ($targetServer -in $servers) { }

# Pre-allocate arrays when size is known
$results = New-Object System.Object[] 1000

Code Organization

# Use regions for large scripts
#region Helper Functions
function Get-Configuration {
    # Implementation
}
#endregion

#region Main Logic
# Main script logic here
#endregion

Best Practices for Error Handling

# Always use specific error types when possible
try {
    # Risky operation
} catch [System.UnauthorizedAccessException] {
    Write-Warning "Access denied - check permissions"
} catch [System.IO.FileNotFoundException] {
    Write-Error "Required file not found"
} catch {
    Write-Error "Unexpected error: $($_.Exception.Message)"
    throw  # Re-throw if not recoverable
}

Conclusion

PowerShell’s language features provide powerful tools for automation, system administration, and development tasks. Modern features like ternary operators, null coalescing operators, and parallel processing make PowerShell competitive with other scripting languages while maintaining its object-oriented nature and pipeline philosophy.


This cheatsheet covers PowerShell 5.1 to 7.5 features. Some advanced features require PowerShell 7+ and may not be available in Windows PowerShell 5.1.