Summary: Guest commentator, Boe Prox, talks about practical tips for preparing for the 2012 Windows PowerShell Scripting Games.
Microsoft Scripting Guy, Ed Wilson, is here. Guest blogger, Boe Prox, is with us today. He is going to talk to us about preparing for the 2012 Scripting Games. First, a little bit about Boe…
Boe Prox is currently a senior systems administrator with BAE Systems. He has been in the IT industry since 2003, and he has been working with Windows PowerShell since 2009. Boe looks to script whatever he can, whenever he can. He is also a moderator on the Hey, Scripting Guy! Forum. Check out his current projects published on CodePlex: PoshWSUS and PoshPAIG.
Boe’s blog: Learn PowerShell | Achieve More
Take it away Boe…
With the Scripting Games coming up on April 2, you might be asking yourself, “What do I need to do to be prepared for these games?” You are in luck because I am going to show some things that will help you in your quest for scripting glory!
These tips are in no particular order of importance, and they cover a wide variety of items that should be used to increase the readability of your code and to ease any issues that the people who are using your scripts may run into. So without further ado, let’s get started!
Variables that make sense
When you are deciding what you want to call the variables that are used in your code, keep in mind that others will be reading your code, whether it is in the Scripting Games or at your place of work. Or you might have to debug or add something to your code several months later, so using something like this for your code will only make the effort to read the code more difficult.
$strServers = 'server1','server2'
$d = Get-Date
ForEach ($C in $strServers) {
$objp = Get-WmiObject -computer $c Win32_Process
}
There are a couple of things here that can stand to be changed. First off, the $strservers that is using Hungarian notation is not needed. Although it is often used in VBScript script, it isn’t needed in Windows PowerShell script. Using something that lets you know what the variable is holding—in this case, servers—is suitable, rather than knowing the type of the variable.
Although, I would rather use ComputerName because that is a common parameter in Windows PowerShell cmdlets. $d, $objp, and $c are the remaining the variables that fail to make it easy to know what is being used for each variable. Given, this is a small snippet of code, but imagine if you are working with 100+ lines of code. It would be more difficult to back track and find out what each variable is. Something like this would be much easier to read and understand.
$Computername = 'server1','server2'
$date = Get-Date
ForEach ($computer in $Computername) {
$processes = Get-WmiObject -computer $computer Win32_Process
}
Error handling
If you are writing functions or scripts (or whatever), you need to be using some sort of error handling in your code. This will prevent unwanted errors from popping up and preventing your code from working. Now, I don’t mean to add $ErrorActionPreference = ‘SilentlyContinue’ at the top of your code and let it fly. If you are using that to solve your error handling, you are only fooling yourself. In other words, remove it immediately! It’s OK, I’ll wait.
What you should be doing is investing in using Try/Catch in your code to handle any potential errors that might pop up. All cmdlets have the ErrorAction parameter, and you can choose to use Stop. Or if you really don’t want to catch any errors (or even know about errors), you can use SilentlyContinue. The benefit of using this parameter is that it will only affect that command, not the all of the commands in the code. The following example of using this method will catch an error if something goes wrong.
ForEach ($Computer in $Computername) {
If (Test-Connection -ComputerName $Computer -Count 1 -Quiet) {
Try {
Get-WmiObject -Computer $Computer -Class Win32_Process -ErrorAction Stop
} Catch {
Write-Warning ("{0}: {1}" -f $Computer,$_.Exception.Message)
}
} Else {
Write-Warning ("{0}: Unavailable" -f $Computer)
}
}
A couple of other types of error handling use specific cmdlets to test for. Test-Connection checks if a system is online before making a connection for a query or some other operation. Test-Path checks to see if a folder or file exists.
Also, checking for Administrator rights is a useful thing to do within your code, especially when working with remote systems or accessing parts of a system that are otherwise off limits to a regular user account. It also helps if you have UAC enabled and are not running the script “as an Administrator.” I wrote a guest blog last year that talked about this: Check for Admin Credentials in a PowerShell Script.
Code formatting
Making your code readable can be just as important as making your code work properly to perform the action that you want it to do. Having code that is not readable makes it difficult to debug when something goes wrong. In addition, others trying to read the code may end up spending more time putting it into a proper format than actually debugging it or modifying it for their own environment. Things to consider when writing your code are:
- Use spacing between areas of code to separate blocks of code.
- Use natural line breaks: “|”,”{“.
- Indent your code in areas where groups of code are being used logically, such as after an opening bracket “{“ or after a pipeline character is used. This ensures that all the lines of code within the construct are easy to identify and work with.
- Use splatting for parameters where it threatens to force the use of a back tick (“`”) to enable a line break to continue the code.
Following is an example of poorly formatted code:
There is a lot wrong with this code, ranging from no spacing, to indenting blocks of code where it is incased in a {} construct, to cmdlets using enough parameters to warrant a line break to keep everything from going off of the screen. Also, there are areas in the code that really aren’t needed, such as saving the result of the Test-Connection cmdlet and then comparing the return when it is a Boolean value in the If statement.
This is what it should look like:
What you have now is something that is not only easier to read, but also takes advantage of other techniques, such as splatting the parameters for Get-WmiObject and taking advantage of the Boolean return value for using the Quiet parameter in Test-Connection for the If statement. Instead of using something like @{L='Computer';E={$_.__Server}},@{L='TimeGenerated';E={Get-Date}}} to add a couple extra properties to the output that is difficult to read, I used a ForEach loop and created my own object to make it easier to read. This really starts to show its usefulness when you are trying to add multiple properties to the output that are not available any other way.
There are more lines of code being used here. But trust me, it is worth it to have more readable code than taking shortcuts by cramming it all together.
Comment-based Help
Something that I saw last year (especially in the first few events), and I sometimes still see, are people writing their own Help in their code. Although it is great to see that some sort of Help is being added into their code so other people know what is going on with it, there is no reason to be doing this. The Windows PowerShell team has put together an amazing thing called comment-based Help that does practically all of the work for you. It has some great features such as displaying examples, viewing the full Help of the command, and looking at the details of a particular parameter. It is so simple that you can practically do it in your sleep! See this example:
Everything within “<#” and “ #>” that uses the appropriate keywords will be read by Windows PowerShell and used to format the Help correctly so it shows something similar to the following:
Get-Help Get-StoppedService
Everything that can help a person figure out what to do with this code is here. Want to know the specifics about the ComputerName property? Simple! Just do this:
Get-Help Get-StoppedService –Parameter Computername
How cool is that? This is much better than only getting one option when writing your own Help—which is typically to display everything at once. Need an example? Just do this:
Get-Help Get-StoppedService -Example
Remember, comment-based Help is your friend, and it should be used instead of writing your own Help. For more information about comment-based Help, run the following command:
Get-Help about_Comment_Based_Help
Other things to consider
I don’t have the time to dive into detail about these other items, but they are by no means any less important to think about and use while writing your code. It is just that I only have a limited amount of time and space to pick and choose which items to talk about.
- Use output objects instead of text, and make it pipeline friendly.
- Do not use aliases for anything (this includes cmdlets and parameters).
- Use properly named functions (Verb-Singular noun).
- Use Get-Verb to see approved verbs.
- Keep inline comments to a minimum (consider using Write-Verbose instead).
- Use [cmdletbinding()] and Param() in advanced functions; This allows the use of common parameters such as Verbose, Debug, WhatIf, and ErrorAction.
- Consider using Write-Progress for long running tasks where you are looping through a collection; this allows the user to know where the script is and does not assume it has locked up.
Final thoughts
Keep in mind that during the Scripting Games you may not always get the score that you were hoping for. But don’t let that get you down! Keep writing code and submitting your scripts, learn from what the judges say, and look at the scripts your fellow scripters have submitted. You will improve on your current skills and continue to put together great scripts!
To quote myself from last year on twitter during the 2011 Scripting Games: “Remember that as long as you did your best on your script and are happy with what you did, then grading should be secondary.”
Good luck to all of the scripters this year!
~Boe
Thank you Boe. This is a great summary of things to keep in mind for the 2012 Scripting Games. Join us tomorrow for more Scripting Games prep week.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy