Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Writing Robust PowerShell Code

Guy Leech
November 20, 2020

Writing Robust PowerShell Code

In this session Guy shares some of his tips for writing PowerShell code that will reduce the occurrences of errors and unexpected behaviour, which helps increase reliability and user confidence. Non-PowerShell conversant folk tend to run away screaming when they see those red errors, so learn how to avoid them completely, make them intelligible/useful or rectify the errors within the script, so users see no indication that anything was ever amiss.

Guy Leech

November 20, 2020
Tweet

More Decks by Guy Leech

Other Decks in Technology

Transcript

  1. WHOAMI • Wrote my first code (on Commodore PET) in

    1980 (in BASIC) • Unix developer in C/C++ for 6 years after Comp Sci degree from University of Manchester • Started with Windows (NT 3.51) in 1995 via Citrix • Invented & wrote what is now Ivanti Application Control security product • Freelance consultant writing PowerShell for software vendors like ControlUp, Flexxible IT • Only been doing PowerShell (and C#) for 7 years but it's "just another language"Microsoft • MVP, Citrix CTP, VMware vExpert
  2. WHY WRITE ROBUST CODE? • Less comeback • Fewer problems

    to rectify afterwards • Higher successful outcomes • Why not? Do it as you code, not "later" so becomes habit • Reputation • "Write it once, write it right" • It's a mindset
  3. QUESTIONS ABOUT ERROR HANDLING • Does it benefit the user

    if the error is reported? • Is there anything that can be done to avoid/predict the error? • Is there anything that can be done to fix the error? • Is it a fatal error or can we continue (change to warning)? • Does the error message make sense to mere mortals? • Change the error text to display remedial action, KB references, etc • Can the user actually deal with raw errors?
  4. ERRORS • Check for errors even if not obvious that

    has errored • Be Consistent • Expected errors (e.g. create folder when already exists) • If you explicitly ignore/hide an error have a comment saying why • Beware modules/cmdlets that output error to stdout • Or don't output errors at all or return status (and use Write-Host) • Zero can be error ($false) or success (ERROR_SUCCESS in Windows APIs)
  5. CHECKING FOR ERRORS • Validate return values • $? •

    $error • Array • $error.Clear() • -ErrorVariable (works with –ErrorAction SilentlyContinue) • -PassThru • Extra checks, e.g. if folder creation, check folder created
  6. EXCEPTIONS • Designed to simplify error checking • Compromise –

    1x try catch around whole code vs try catch around everything • Can be ignored but is that wise? • Use $_ to examine exception in catch block • Catch specific exceptions if you are going to deal with them differently • catch [System.Management.Automation.DriveNotFoundException] • $_.Exception.gettype().FullName • Try catch finally – finally useful for tidy up like removing temp files • Beware error message containing "At line:1 char:1"
  7. VARIABLES: STRONG TYPING & INITIALISATION • Declaring type helps catch

    static errors & helps intellisense • Script variables and parameters • Get type name from a variable via GetType().FullName • Declare and initialise to known value, typically $null or -1 • De-initialisation • Call Dispose(), Close() etc as relevant (Get-Member) • Set to $null when finished or even remove variable
  8. ADDITIONALLY #1 • Comments (and update/delete them) • Meaningful variable

    names $i • $ErrorActionPreference = 'SilentlyContinue' Never! • But do use it on individual cmldlets as necessary/appropriate • $null and zero are different – which do you need to check for? –not ! • Check property exists before referencing if may be missing • If( $guysobject.PSObject.Properties[ 'SomeProperty' ] -$ne $null ) { • Invoke-SomeFunction –InputObject $guysobject.SomeProperty } • Use named parameters with full name, not positional or abbreviated • Sanity checks parameters as much as possible & give meaningful errors • No aliases in scripts, just use them interactively
  9. ADDITIONALLY #2 • Check size of array before indexing in

    to it • [-1] for last item • Check not $null before using Count property • Watch for array flattening where single item not returned as array • Wrap in @() • Opposite – get an array when expecting single item • Select –First/-Last, depending on sort order and if appropriate • -is [array] • Use –as instead of [type] cast – soft fail • TryParse & TryParseExact methods • [string]::IsNullOrEmpty() • Use extra parentheses if makes it easier to read/understand • If you are copying & pasting the same chunk of code, you should use functions
  10. TESTING • Different PowerShell versions (#requires –version) • Different environments

    • Different operating systems • Admin vs non-admin (#requires -runasadministrator) • Edge cases (what if the user has (stupidly) done …) • Different users • Clean profile • "What If" code walk throughs – what could fail?