Setting Up Your Scripts for Success

Over the past few months I have written quite a few scripts for myself and my clients. When I write them I tend to concentrate more on the process/purpose of the script which means coding the middle of the script without thought for the end or beginning. As such, I am now beginning to realize the importance of making the most out of the beginning or start of a PowerShell script. This means variables, parameters or global settings.

Why is this important? Having your prerequisites clearly and cleanly laid out at the top of the script tends to lead to better readability and understanding of what your script does. It also makes it easier to add additional components – another module that may be needed for additional cmdlets, more log files – to log a file to be disseminated via email or global variables that may be needed between functions or even a section for more functions to be called upon later.

In this blog post we’ll run through some examples of what can be done to help make your script a successful process. Make sure to note that this is not a comprehensive list by any stretch. However it will hopefully inspire you to improve your scripts or come up with a block of code you will use at the beginning of your scripts on a consistent basis.

Transcript

Imagine you are automating a script and you want to have a general idea of what PowerShell cmdlets were run while the scheduled task is executed. A good way to do this is with a PowerShell transcript cmdlet. With this cmdlet we can start a transcript before the script begins and then end it when the script is done processing. We can enhance this a bit with some error handling in case there is an issue with access the file or even stopping / starting a transcription session in PowerShell. We can thus use a code sample below which will designate a path and file name for the PowerShell Transcript as well as start a new and or stop an old Transcript for running too log:
[sourcecode language=”powershell”]
# Setup Transaction file name
$TranscriptFile = "Script-Transcript-"+"$Date"+".txt"
$Transcript = $Path+"\"+$TranscriptFile
# Stop any existing transcript
Try {
Stop-Transcript -ErrorAction SilentlyContinue
$Line = "$Date,Old transcript was stopped." | Out-File $LogDestination -Append
} Catch {
$Stop = $True
$Line = "$Date,Could not stop transcript." | Out-File $LogDestination -Append
}
Try {
Start-Transcript -Path $Transcript -NoClobber -ErrorAction STOP
$Line = "$Date,Transcript Created." | Out-File $LogDestination -Append
} Catch {
Write-host 'No transcript is in progress.' -ForegroundColor Red
$Line = "$Date,Transcript was not started." | Out-File $LogDestination -Append
}
[/sourcecode]
Log Files

Depending on the purpose of your script, logging actions or at least posting what has occurred at a certain point in a script, can prove quite useful in troubleshooting or in an auditing situation. By adding this code we can initial a log file and then add information to that log as needed. The logged information could be about the success or failure or an action, documenting the current state of mailboxes or databases or logging information found from a cmdlet. There really aren’t that many limitations to what could be logged here:
[sourcecode language=”powershell”]
$Path = (Get-Item -Path ".\" -Verbose).FullName
$File = "ExchangeServerInformation.Txt"
$Destination = $Path+"\"+$File
[/sourcecode]
Modules / Script Prerequsites

For this one we’ll explore some options for preparing a script to enable the successful execution of cmdlets later in a script. These cmdlets may fail or not produce the correct results if this prep work is not done at the beginning of the script.

First example – Loading the Active Directory Module

Typical of some environments, a PowerShell shell may not load all relevant PowerShell modules needed to execute certain cmdlets later in a script. In order to make these cmdlets accessible, we may need to load certain modules. Example modules are Active Directory, Exchange, Azure AD Connect and Azures new AZ module. These modules can be loaded with a ‘Import-Module cmdlet. To enable some error correcting, we can also wrap it in a Try {} Catch {} code block, like so:
[sourcecode language=”powershell”]
Try {
Import-Module ActiveDirectory -ErrorAction STOP
} Catch {
Write-Host "Active Directory Module could not be loaded"
}
[/sourcecode]
Now the same process and code block could be potentially reused for any number of modules. It just assumes the module is installed. If the module is critical, we could either exit the script if the module could not be loaded or installed if it is missing entirely. That is an additional option that could go hand in hand with the above code snippet.

Second Example

Typical of some environments, a PowerShell shell may not load all relevant PowerShell modules needed to execute certain cmdlets later in a script. In order to make these cmdlets accessible, we may need to load certain modules. Example modules are Active Directory, Exchange, Azure AD Connect and Azures new AZ module. These modules can be loaded with a ‘Import-Module cmdlet. To enable some error correcting, we can also wrap it in a Try {} Catch {} code block, like so:
[sourcecode language=”powershell”]
Try {
Set-ADServerSettings -ViewEntireForest $True
} Catch {
$ForestWide = $False
}
[/sourcecode]
In Conclusion, we can see there are some useful code snippets that could be added to your everyday scripts that would enhance the experience or at the very least, ensure that all the requirements are in place before the script hits the main code sections and executes PowerShell cmdlets in an errant manner. Additionally we could add #Requires if there are cmdlet compatibility issues with different versions of PowerShell. Documentation on this can be found HERE. Thanks for reading this post.

Related Post

A Good EndingA Good Ending

Like all good scripts, starting off well should be reciprocated with a good ending as well. What does that mean? Think processing, cleanup, ending transcripts, truncating log files and more.