# PowerShell – Best Practices – Part 2

PowerShell is a very flexible language with many ways to solve a problem, and many ways to write the same code. To ensure readability, consistency, and reuseability, it’s very important to always adhere to community accepted best practices. Not everyone will agree with everything, but the majority will agree that these practices will improve your code by leaps and bounds, and make it easier for other people to both understand and use. We will split up this into a series of blog post, so we have room for showing examples along the way.

Note that these practices only apply when authoring scripts. What happens in the command line, stays in the command line.. So without further ado, here’s some best practices you should follow when writing PowerShell code.

## Use proper indention

You should always indent your code where necessary. An indention should consist of 4 spaces (TAB), and goes long ways to make your code much easier to read. It also denotes to which construct each line belong.

# Bad practice:
function Get-MultiplicationTable {
param(
[int]$Start, [int]$End
)
$Numbers =$Start..$End foreach ($NumberX in $Numbers) { foreach ($NumberY in $Numbers) { [PSCustomObject]@{ X =$NumberX
Y = $NumberY Multiplied =$NumberX * $NumberY } } } } # Good practice: function Get-MultiplicationTable { param( [int]$Start,
[int]$End )$Numbers = $Start..$End
foreach ($NumberX in$Numbers) {
foreach ($NumberY in$Numbers) {
[PSCustomObject]@{
X = $NumberX Y =$NumberY
Multiplied = $NumberX *$NumberY
}
}
}
}


## Avoid unecessary commenting

PowerShell is a very verbal language, that closely resembles english. When using full parameter and function names, it’s by design very self-documenting what a piece of code does. Excessive commenting is unnessecary, and will make your code harder to follow.

# Bad practice:

# The variable $BannedProcesses contains a list # of banned processes that should be stopped$BannedProcesses = @('spoolsv', 'firefox')

# The variable $Processes contains information about the running processes$Processes = Get-Process

# Start a foreach loop, iterating over all processes
foreach ($Process in$Processes){
# Check if the process is banned
if ($Process.ProcessName -in$BannedProcesses){
# If the process is banned, stop it
Stop-Process -Name $Process.ProcessName } } # End foreach loop on$Processes

# Best practice:

$BannedProcesses = @('spoolsv', 'firefox')$Processes = Get-Process

foreach ($Process in$Processes){
if ($Process.ProcessName -in$BannedProcesses){
Stop-Process -Name $Process.ProcessName } }  ## Write comment-based help If you are writing advanced functions, you should be documenting the functions through comment-based help. I have written about this in another post on this blog: PowerShell – Build Advanced Functions ## Use #region In order to segment large chunks of code, use the #region functionality. This allows you to document where something is starting and ending, and to fold away the code when you don’t need it. This is a must in long scripts. ## Use CIM cmdlets instead of WMI WMI cmdlets are deprecated in favor of the CIM cmdlets. For most WMI use-cases, a similiar CIM method exists. # Bad practice: Get-WmiObject -ClassName Win32_Volume Get-WmiObject -ClassName Win32_Computersystem # Best practice: Get-CimInstance -ClassName Win32_Volume Get-CimInstance -ClassName Win32_Computersystem  ## Use #requires Very few beginners actually know about this seemingly simple functionality in PowerShell, and what it is capable of. Stick it to the top of your script to define requirements that must be compliant before running your script. You can add one, or more of these statements, separated by a new line. ##### Version Specify the minimum PowerShell version to run the script. #Requires -Version 5.1  ##### Run as administrator Require that the PowerShell session that invokes your script, must be run with local admin privileges. #Requires -RunAsAdministrator  ##### Modules Specify modules that your script requires to be installed and imported. If not already imported in the session, this will do that for you. #Requires -Modules ActiveDirectory, DNSServer  ##### Assembly Specify required DLL’s to load: #Requires -Assembly path\to\my.dll  ## Use splatting In PowerShell version 3, they introduced this great functionality called splatting. It allows us to defied a series of parameters as a hashtable, instead of writing endlessly long lines of code. Think about how many parameters New-ADUser takes for example. It has always been posible to separate parameters on a new line by using back-ticks (  ) but this is frowned upon by the community, stating that it can be hard to spot, and if you place a space after the back-tick, you break your code. To use splatting define a hashtable with properties, then use variable on the cmdlets, but with the ‘@’ sign instead of ‘$’.

# Bad practice:
New-ADUser -Name 'TestName' -DisplayName ' Test Name' -EmailAddress 'test.name@testdomain.test' -UserPrincipalName 'test.name@testdomain.test' -Path "OU=Users,DC=testdomain,DC=test" -Enabled $true -ChangePasswordAtLogon$true -AccountPassword (Read-Host -Prompt 'Password' -AsSecureString)

New-ADUser -Name 'TestName' 
-DisplayName 'Test Name'
-EmailAddress 'test.name@testdomain.test' 
-UserPrincipalName 'test.name@testdomain.test'
-Path "OU=Users,DC=testdomain,DC=test" 
-Enabled $true  -ChangePasswordAtLogon$true 
$Properties = @{ Name = 'TestName' DisplayName = 'Test Name' EmailAddress = 'test.name@testdomain.test' UserPrincipalName = 'test.name@testdomain.test' Path = "OU=Users,DC=testdomain,DC=test" Enabled =$true