Search Bit-Wizards
PowerShell Best Practices
 PowerShell

PowerShell Best Practices

I should start out this article being very upfront: I’m no programmer, and I’m no great shakes at PowerShell, either. However, as a SharePoint Developer at Bit-Wizards, I get to use PowerShell for light administrative tasks, e.g. Office 365 bulk licensing, user management in Active Directory, etc. So I’ve been learning how to get better at writing my own PowerShell scripts to accomplish tasks I do regularly. I don’t intend to get into too many specifics in this blog post—PowerShell is versatile and there are many different commands to learn—but I do want to talk about three things I’ve learned that help me write better scripts and give me feedback for figuring out what is going on behind the scenes. These, I’m sure, apply to other scripting and coding languages, but they are robust enough to form the basis of my PowerShell scripts. 
 

Commenting

Commenting is a fundamental discipline and essential anywhere you write code. That’s because “$xyz” might make sense as a variable name when it’s 2 AM and you’re bleary-eyed and hopped up on Red Bull, but if you need to tweak your script a few months from now, or if someone else will ever need to work on the script after you, it’s going to be very hard to decipher what’s happening at different stages in the script without some kind of annotation explaining everything step by step. Commenting in PowerShell is very straight forward, but should not be overlooked! In PowerShell, there are two ways to add comments. A # (a.k.a “pound sign” for anyone over 20, “hashtag” for the kids in the crowd) tells the script interpreter that everything after that, and up to the next line break, is a comment and is not executable. 
 

Example

# Retrieve current users in tenancy
Get-MsolUser –All

If you need to block out an entire portion of the script (for a disclaimer, etc.), you simply wrap the comment with <# and #>. When this method is used, line breaks are also part of the comment, so it isn't necessary to comment out every single line. 
 

Example

<# Bulk assign licenses to users
Script by Samuel Blowes
Email sam@bit-wizards.com for help #>
Import-Module MSOnline
$cred = Get-Credential


Commenting is not only good practice for making your code easily readable, it’s also very handy for temporarily eliminating a line of code without completely deleting it… in case you need it again later. 
 

Try-Catch

Debugging is the art of figuring out why your script isn’t doing what you expect it to do. I call it an art, because it’s a thankless job that requires a lot of patience. Fortunately, Microsoft has built in the Try/Catch system in PowerShell to help you figure out what’s happening in your code. The way it works is instead of just executing a line of code, you tell PowerShell to Try to run the command, but if something goes wrong, to then Catch the error (instead of crashing) and feed it back to you in an array that you can review. It’s a clever system that can be extremely informative and save you a ton of time determining where things went wrong and why. 
 

Example

ForEach ($o365user in $o365userlist) {
       Try {
              $upn = $o365user.UserPrincipalName
              Set-ADUser -Clear ExtensionAttribute15 -Identity $o365user
       }
       Catch {
                    Add-Content c:\Scripts\ad-o365-log.log -Value "$upn not found because $($Error[0])"
       }
}

 

Output

PowerShell will quite happily sit quietly and process commands you tell it to process, and by default, it won’t generally bother to tell you what happened or if it was successful. Personally, coming from a UNIX shell scripting background, I’m used to this. In the world of the UNIX terminal, no news is good news. But it’s not terribly helpful, so getting feedback from your script is critical; whether as real-time feedback on-screen while the script is running, or as a generated report you can view after the process is completed. PowerShell offers lots of ways to do this, but here are a few that I make sure I include in my scripts. 
 

Outputting to the Screen

Outputting to the screen enables you to see what’s happening at any given moment while the script is running. For every line you write that executes a series of commands, you should also have it reflected on the screen. The command for this is Write-Host (as opposed to Read-Host, which I’ll cover in a separate post). Write-Host takes anything you put in quotes and puts it on the screen. Pro-tip: it’s a good idea to clear the screen first to have a blank canvas to write to. The command for that is Cls.
 

Example

Cls
Write-Host "$SyncUsers users in Active Directory filtered." -foregroundcolor red -backgroundcolor yellow

 

Outputting to a File

Sending output from the script to a file, and not just the screen, gives you a log file you can review later, which is a very handy tool. There are several ways you can do this, and one of the most common is to just pipe your results to the Export-CSV command, as it spits out a formatted file that you can open in Excel and organize the data. However, I like to have a little more control over the output file and use it more like a transcript, so I mostly rely on the Add-Content command, and format the output into something that makes sense to me. This is especially useful when combined with the error catching in the Try/Catch section.
 

Example

Add-Content c:\Scripts\ad-o365-log.log -Value "$upn not found because $($Error[0])"

 

Putting it All Together

Combining these three principles makes for good, clean PowerShell scripting, with built-in readability, error catching and logging. It’s good practice to comment as much as possible in the code, so I try to make sure that everything in my scripts is sandwiched within a Try/Catch block, that it is clearly documented in the comments, and that it is outputting to both a file and the screen.

Author

Samuel O. Blowes, Director of Solution Consulting
Samuel O. Blowes

Director of Solution Consulting