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