PowerShell in Depth, Second Edition (2015)
Appendix A. Best practices
This chapter covers
· General best practices
· Scripting best practices
· PowerShell in the enterprise best practices
Throughout this book, we’ve noted—and sometimes hinted at—practices you can follow to make your PowerShell efforts, especially scripts, more maintainable, better performing, and more flexible than you ever thought possible. In this appendix, we’ll reiterate and organize those suggestions for easier reference. The number one recommendation is to use PowerShell every day. The following recommendations aren’t in any particular order of importance.
PowerShell general best practices
These recommendations are designed for you to get the best out of PowerShell:
· Read the help files—there’s a mass of good information, especially in the examples.
· In PowerShell v3 and later, set up a schedule to update help on a regular basis. Just remember that if you’re saving help in a mixed-version environment you must keep saved help in separate locations. Monitor the PowerShell help versions RSS feed athttp://sxp.microsoft.com/feeds/msdntn/PowerShellHelpVersions for news of revised help files being available.
· Set your script execution policy to RemoteSigned (at the least).
· Use the pipeline—PowerShell is designed for pipeline usage. If you apply coding styles from older scripting languages, you’ll lose a lot of functionality and create work for yourself. That said, don’t feel you have to do everything in one-line commands. Sometimes it’s helpful to break long commands into several steps, each still using the pipeline.
· Give variables meaningful names, such as $computer rather than $c.
· Avoid variables with spaces or special symbols in their names, such as ${my odd variable}.
· Never set $ErrorActionPreference (or $VerbosePreference or any other “preference” variable) globally in the shell or in a script or function. Instead, use parameters, such as a cmdlet’s –ErrorAction parameter or a function’s -Verbose parameter, to set the preference on an as-needed basis.
· Avoid enumerating collections—using ForEach-Object or the ForEach scripting construct—unless there’s no other way to accomplish your task.
· Use single quotes unless you explicitly need the variable-replacement and expression-evaluation capabilities of double quotes. If you’re working with SQL Server databases, remember that they use single quotes for strings.
· String substitution (or multiplication) is much easier than string concatenation.
· Use the built-in numeric constants—PowerShell understands KB, MB, GB, TB, and PB.
· Avoid using native .NET classes and methods unless there’s no cmdlet alternative. The exception is for large-scale enterprise scripting where you might see some performance gains using native .NET classes. You have to be willing to trade simplicity for performance.
· Be careful with code downloads from the internet and always double-check what the code is doing—your environment may be different enough from the author’s that you’ll encounter problems. When pasting downloaded or copied code from a web page, watch out for curly single and double quotes. Change them to single quotes.
· Filter early and format late. Restrict the data set as soon as possible, but don’t format the data until you’re just about to display your data.
PowerShell scripting best practices
These recommendations are designed for you to get the best out of PowerShell scripts:
· Give variables a type, such as [string]$logfile, especially if they’re parameters.
· Avoid using variables that haven’t first been given a value within the current scope.
· Give functions and workflows cmdlet-style, verb-noun names such as Get-DiskInfo.
· When a script performs a given task, rather than having it just acting as a container for several functions, give the script a cmdlet-style, verb-noun name such as Set-UserAttribute.ps1.
· When naming functions and scripts with a verb-noun style name, apply a two- or three-character prefix to the noun. This will generally be a prefix that relates to your company. For example, a company named Great Things, Inc. might name a function Get-GTUserInfo or might name a script Set-GTUserInfo.ps1. Alternatively, apply a prefix when loading the module.
· If you create private (that is, nonexported) variables in a script module, give those variables distinct names. Many developers will use an underscore for this distinction, such as $_private or $_counter.
· When defining script or function parameters, use parameter names that correspond to native cmdlet parameters that have a similar purpose. For example, a parameter intended to collect computer names would be –Computername rather than –host or –machine, because native PowerShell cmdlets use –Computername. You can always define a parameter alias for alternate or shorter parameter names such as –host.
· Avoid using Write-Host unless your sole purpose is to produce output that will only ever need to be seen onscreen. If you use Write-Host, use a foreground or background color so your messages can be distinguished from your output.
· Use [CmdletBinding] in your scripts and functions to give easy access to verbose messages, debugging, and other advanced functionality.
· If your script or function will change the system, add support for –Whatif and -Confirm.
· Use Write-Verbose to produce “progress information,” such as messages that tell you what a script or function is about to attempt.
· Use Write-Debug to produce messages intended to assist with the debugging process, keeping in mind that Write-Debug will pause the script and offer an opportunity to suspend it.
· Remember that Write-Warning exists for those times when you need to output informational messages to screen.
· Scripts and functions should produce one, and only one, kind of output. That output should usually be an object and may be a custom object that combines information from multiple sources.
· Always define help for scripts and functions, even if it’s just comment-based help. XML-based help files are often only needed when you need to provide help messages in multiple languages.
· Avoid changing aliases, variables, and other scoped elements of a scope other than the current one.
· Break tasks into distinct, small units of functionality and implement each as a function. For example, a script that performs 10 different things should be broken up into 10 functions, with a script that calls those functions in the proper sequence.
· Sign code examples that you plan to share with the public. Yes, it’ll require a code-signing certificate and we’re not kidding ourselves about the likelihood of people following this advice. But it’s a good way to help the public confirm that your code hasn’t been tampered with by someone else.
· Avoid using Hungarian notation for variable names; conventions like $strName and $intCounter are outdated and unnecessary.
· Indent the contents of a script block, such as the {contents} of an If construct, loop, or other scripting construct.
· Consider using Write-Verbose, Write-Debug, and so forth to provide inline documentation for scripts and functions, rather than using inline comments for that purpose.
· In a script, function, or workflow, avoid aliases (except for widely understood ones like Dir) and truncated parameter names. Spell out full cmdlet and parameter names for better readability and maintainability.
· Avoid using the backtick (`) character at the end of a line so that you can continue the command on the next physical line. Instead, break lines at “natural” PowerShell points. Hitting Enter after any of these characters will allow the line to be continued on the next ( { , ; |.
· If you use proxy functions, make sure you publish them with your module.
· Remember Test-Path and use it to test for file or folder existence or any provider path.
· Try...Catch...Finally should be used anywhere that exceptions could cause problems in your processing.
· Don’t use Trap—Try...Catch is easier and better.
· Keep your logic simple—for example, avoid double negatives in If statements.
· Don’t use Notepad as a script editor, except for the most minimal of scripts. At the very least use the Windows PowerShell ISE. Notepad is good for quickly viewing code because PowerShell files open in Notepad by default.
· Avoid using the Return keyword. Instead, think about writing objects to the pipeline.
PowerShell in the enterprise best practices
These recommendations are designed for you to make the best use of PowerShell in the enterprise:
· Use Group Policy to configure and enforce PowerShell Remoting and script execution.
· Use a Remoting or CIM session if you’re accessing a remote machine more than once.
· Use PowerShell jobs for long-running tasks.
· Use PowerShell workflows where you need the ability to interrupt or restart scripts or use parallel execution.
· Store scripts in a source control solution so that you have both a backup and a way to roll back to a previous version if you mess something up.
· Restrict access to your production scripts to just those who need it.
· Use Test-Connection to test the availability of a remote machine before attempting a lot of processing.
· Make sure PowerShell Remoting is enabled on your servers.
· Credentials should be created before being used—don’t create them in your command, especially if you need them more than once.
· Use WSMAN rather than DCOM for CIM sessions if at all possible.
· Develop a standardized script template and style, especially when a team of administrators will be developing PowerShell scripts.
· Consider an enterprise execution policy of AllSigned, and use a code-signing certificate from your Active Directory PKI.
· Create shared scripts that define common functions, aliases, and variables you might need for your team. Store these scripts centrally, such as on a UNC, and then dot-source them in your profile script.
· Use PowerShell Web Access to provide additional remote access, and administration, capabilities.
· Use PowerShell Web Access and restricted endpoints to delegate permission to junior admins where appropriate.
· Use Desired State Configuration to create, and maintain, you server configurations. Use a Pull server to minimize manual intervention.
· We hope this goes without saying, but test everything in a nonproduction environment. With the widespread adoption of virtualization, there’s no reason you can’t. You don’t need a Hyper-V farm and a 10 TB SAN; you can get started with the open source VirtualBox and trial versions from Microsoft.