Get-WmiObject | Get-CimInstance |
Get-EventLog | Get-WinEvent -FilterHashtable @{...} |
$array += $item in loops | Capture foreach output, or [List[PSObject]]::new() + .Add() |
Write-Host for data output | Write-Output / emit objects |
Format-Table mid-pipeline | Emit objects; format at end |
Invoke-Expression $cmd | Parameterised calls / splatting |
| Plaintext password in param | [PSCredential] + SecretManagement |
Empty catch {} | Always log or re-throw |
$? after native executables | $LASTEXITCODE (for cmdlets use try/catch) |
ConvertTo-Json without -Depth | Always ConvertTo-Json -Depth 10 (default 2 silently truncates) |
$global: for cross-function state | $script: scope — contained to the script file |
| Building HTML without encoding | Regex replace for 5 HTML special chars (CLM-safe); HttpUtility in FullLanguage only |
Read-Host for required input | [Parameter(Mandatory)] |
Aliases (gci, %, ?) | Full cmdlet names |
begin/process/end at script top | Only inside pipeline-aware functions |
Positional parameters (Get-Item $p) | Named parameters (Get-Item -Path $p) |
Out-Null in hot paths | [void](...) or $null = ... (no pipeline overhead) |
Where-Object when source has -Filter | Use -Filter on the source cmdlet |
ForEach-Object { $_.Prop } for single property | Select-Object -ExpandProperty Prop |
New-Object PSObject -Property @{} | [PSCustomObject]@{} (3x faster, cleaner) |
[array]::new() or List[T]::new() in CLM | Capture foreach output directly |
Add-Type in potentially CLM environments | Cmdlet-based alternatives |
continue inside ForEach-Object | return (acts as continue in pipeline context) |
if ($array -eq $null) | if ($null -eq $array) (left-side null check) |
if ($results) to test for empty collection | if ($results.Count -gt 0) |
-Encoding utf8 without knowing PS version | -Encoding utf8NoBOM (explicit, portable) |
"abc" -contains "ab" for substring | "abc".Contains("ab") or "abc" -match "ab" |
-like "pattern\d+" (regex in glob) | -match "pattern\d+" for regex patterns |
String "False" as a boolean | Explicit -eq 'True' or -eq $true comparison |
$list.Add($item) on ArrayList | [void]$list.Add($item) — .Add() returns the index to the pipeline |
New-Item/New-Object output leaked | $null = New-Item ... or assign to variable |
Write-Output $x (usually) | Just emit $x implicitly; use Write-Output -NoEnumerate only for arrays |
| Inconsistent output types per code path | Always emit the same type; use error stream for errors |
No [OutputType()] on functions | Declare [OutputType([PSCustomObject])] on functions with defined output |
Invoke-RestMethod without $ProgressPreference | Set $ProgressPreference = 'SilentlyContinue' at script top for non-interactive use |
Parameter named Verbose, Debug, WhatIf, etc. | These are reserved by [CmdletBinding()] — rename to avoid conflicts |
Parameter named Error, Input, Host, Args | These shadow automatic variables — use distinct names |
$string += "text" in loops | -join operator (790× faster at scale) |
Nested Where-Object for cross-collection joins | Hashtable lookup — O(n+m) vs O(n×m) |
FunctionsToExport = '*' in manifest | Explicit function list — avoids ~15s import penalty |
"str".Split('ab') for multi-char splitting | "str".Split([char[]]'ab') — portable across PS5.1 and PS7+ |
if ($IsWindows) without edition check | $PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows |
Invoke-RestMethod -Authentication + manual Authorization header | Use only one — -Authentication silently overrides the header |
-FollowRelLink without -MaximumFollowRelLink | Always set -MaximumFollowRelLink to prevent infinite loops |
| Class method with implicit output | Class methods discard all output except return — assign or return explicitly |
Import-Module MyModule; [ClassType]::new() | using module MyModule required for class types |
hidden property assumed private | hidden properties ARE serialized by ConvertTo-Json |
$obj.Prop on object that may lack Prop | if ($obj.PSObject.Properties['Prop']) { $obj.Prop } — safe under Set-StrictMode and avoids PropertyNotFoundException |
$hash[$key] without null-guarding the key | if ($key) { $hash[$key] } — null key crashes Dictionary<TKey,TValue>; guard first |
$hash.ContainsKey($key) without null guard | $key -and $hash.ContainsKey($key) — null argument throws on Dictionary<> types |
Loop body with no null guard on $item | if ($null -eq $item) { continue } at top — API/pipeline collections can contain null entries |
| Building lookup without guarding key or value | Check if ($id -and $value) before $lookup[$id] = $value — null keys create silent corrupt entries |
| `$connector | Add-Member ...; $connector._Prop` |
return ,$array in function + @(Func) at call site | Double-wrap bug: $items = @(Func) when function returns ,$arr gives a 1-element wrapper; use direct assignment $items = Func |