PowerShell (My) Best Practices

PowerShell is one of my favorite scripting languages, there are many reasons for this. The first is that it’s the language I know the best, it gets points for that. I like the modules that you can import, how it works with Task Scheduler for automated runs, and the fact that I can do most of a module’s function natively by writing my own code. I’ve written literally thousands of scripts over the years to basically take humans out of a tedious process. This allows standard operations to be automated, reducing the likelihood of human errors. The only downside is multithreading and some specific security and network-based operations that require or at least are easier to do in Python that are better documented. PowerShell is also made for linear execution not multithreading. I have only seen a need to multithread a few times for Infrastructure Management, and you can do it in PowerShell, but it gets complicated fast.

  • Import Modules at the top of the script. This lets you know what modules are available so you can choose the cmdlets you want to run later.

  • Variables should be placed under the modules so that you can call them as needed. Having them at the top makes them easier to change without hunting through the body.

  • I rarely define variables in the body that will regularly change. Those stay at the top. Dynamic values from a .csv or .json live in the body.

  • Create Functions under the Variables where appropriate so you can reuse code without duplicating it.

  • When you need to connect to services such as M365 or Remote PowerShell, put the connection code next. Get the logons done before the work starts.

  • Script as you will not be the person running it or who understands what it does.

  • Less is more. Most of the time you just need it to do a function well.

  • Error checking for production scripts. I don’t waste time on scripts only I run. If another user or process runs it, I log failures to a database or a .csv.

The layout below is a guide. The code doesn’t do anything useful on purpose.

PowerShellscript-layout.ps1
## Modules
Import-Module WhateverModuleYouWant

## Variables
$TestVar1 = "Testing1"
$TestVar2 = "Testing2"
$outputFile = "C:\Errors.csv"
$errorList = @()

## Functions
function TestFunction {
    param (
        $Test,
        $Test123 = "Testing 123"
    )
}

## Connect to Services
$CredM365 = Connect-ExchangeOnline

## Start Script Body
try {
    $Testing = TestFunction -Test 123456
} catch {
    $errorDetails = [PSCustomObject]@{
        TimeStamp = Get-Date
        Message   = $_.Exception.Message
    }
    $errorList += $errorDetails
}

if ($errorList.Count -gt 0) {
    $errorList | Export-Csv -Path $outputFile -NoTypeInformation -Force
    Write-Output "Errors have been logged to $outputFile"
} else {
    Write-Output "No errors occurred."
}

GitHub: Public scripts live in Technolitero/Powershell-Public.