Site icon PowerShell is fun :)

Things I use most in my PowerShell scripts

Looking back at the scripts I created the last year, certain things always come back in most of them. In this blog post, I will show you a few and explain them.


In the past, I just started creating a script that just ran by pressing F5 in my PowerShell_ISE or starting .\script.ps1 from the command line. While this works, you can’t supply command line parameters and must do things like Read-Host and prompting for the output file name, server name, etc., if you want it to be dynamic instead of static. (By hardcoding all the options in the script itself)

Using the function feature of PowerShell, you can load a script into memory by putting it into your PowerShell profile or call it from within a script with parameters. For example:

function Get-LocalGroupMembers {
    [CmdletBinding(DefaultParameterSetName = 'All')]
    param (
        [parameter(Mandatory = $true)][string]$Outfile,
        [parameter(Mandatory = $false, parameterSetName = "ComputerNameFilter")][string]$ComputerNameFilter,
        [parameter(Mandatory = $false, parameterSetName = "OUFilter")][string]$OUfilter,
        [parameter(Mandatory = $false, parameterSetName = "FileName")][string]$FileName,
        [parameter(Mandatory = $false)][string[]]$GroupNameFilter

This is a part of my Get-LocalGroupMembers function, which let you specify parameters for Outfile, ComputerNameFilter, OUFilter, FileName, and GroupNameFilter. Some are Mandatory, which will prompt you when you use the function without parameters, and some are optional and not needed but allowed. It also has the ParameterSetName option, which allows you to hide parameters not in the same set. So, if you specify the ComputerNameFilter, the OUFilter/FileName/GroupNameFilter parameters will be hidden. This is easy when parameters can’t be used together.

You can also use the Param part without the Function statement. This way, you can start a script from the command line like this:

.\Get-LocalGroupMembers.ps1 - Outfile c:\data\members.csv -ComputerNameFilter Sharepoint


I use this to store items, and you can create a Custom Object and set properties and value pairs in it. (I wrote a blog post about that with more details.) I mainly use it in foreach loops to store everything in that loop in a variable. For example:

$total = foreach ($file in Get-ChildItem -Path C:\Temp) {
        Folder    = $file.DirectoryName
        Name      = $file.Name
        LastWrite = $file.LastWriteTime

This will add all items found in Get-ChildItem -Path C:\temp in the PSCustomObject $total, and this will look like this:

Folder  Name                  LastWrite
------  ----                  ---------
C:\Temp apps.csv              17-5-2023 10:01:22
C:\Temp files.csv             24-3-2023 14:39:00
C:\Temp services.txt          25-4-2023 23:38:48
C:\Temp services2.txt         25-4-2023 23:39:09
C:\Temp ServicesFix-3.5.1.Log 3-5-2023 10:42:39
C:\Temp userslicenses.csv     8-5-2023 19:16:38
C:\Temp windows.csv           3-7-2023 17:30:52
C:\Temp x.csv                 19-7-2023 19:24:37
C:\Temp x.log                 20-3-2023 16:53:22
C:\Temp x.xlsx                19-7-2023 18:44:56
C:\Temp y.csv                 3-7-2023 12:49:51


This makes your scripts more readable. You put all your Parameters in a variable and use that as a parameter set in a cmdlet. For example:

$options = @{
    Path    = 'd:\temp'
    Recurse = $true
    Include = '*.ps1'
    Depth   = '2'

Get-ChildItem @options

In this example, all the Get-ChildItem parameters were stored inside $options, and Get-ChildItem was started using @options, meaning it should use all things stored inside $options. This makes it much more readable than having a long command line with multiple parameters.

String formatting

Writing output to your screen can sometimes be challenging with special characters, escaping variables, etc. With string formatting, you can make things easier, for example:

Write-Host ("Connecting to server {0}" using IP-Address {1} -f $Server.Name, $Server.IP)

Using curly brackets with a number (0 and 1 in the example above), you can specify with the -f parameters which number is which variable. Using string formatting, you don’t have to do things like this:

Write-Host "Connecting to server $($ using IP-Address $($server.ip)"


It’s become my standard for running things and ensuring that if something fails, you will receive a message about that, and the script will stop without potentially causing issues with things that were not created. For example:

try {
    New-Item -Path $Outfile -ItemType File -Force:$true -Confirm:$false -ErrorAction Stop | Out-Null
    $total | Sort-Object Name, Property | Export-Csv -Path $Outfile -Encoding UTF8 -Delimiter ';' -NoTypeInformation
    Write-Host ("`nExported results to {0}" -f $Outfile) -ForegroundColor Green
catch {
    Write-Warning ("`nCould not export results to {0}, check path and permissions" -f $Outfile)

In this Try/Catch, I try to create a new file in the specified path ($outfile). The ErrorAction Stop will stop processing the Try section, switch to the Catch section, and return a warning. The return statement will stop the script afterward, you can also use continue instead of return to get an indication of the error, but the script will continue. (Only applicable when there’s no chance of something destructive 😉 )


Sometimes you must wait for a specific condition before continuing your script, while helps you with that. For example:

while (Get-Process -Name Notepad -ErrorAction SilentlyContinue) {
    Write-Host ("Notepad is still running, waiting for it to close...")
    Start-Sleep -Seconds 1

Write-Host ("Notepad closed, continuing...")

In this example above, Get-Process is used to check if Notepad is still running and will report that it is running and wait 1 second before checking again. When it sees that Notepad is no longer running, it will report that it is closed and continue…

Notepad is still running, waiting for it to close...
Notepad is still running, waiting for it to close...
Notepad is still running, waiting for it to close...
Notepad closed, continuing...

But you can also use while to keep running a script until you press Ctrl-C. For example:

while ($true) {
Get-ChildItem -Path C:\Temp
Start-Sleep -Seconds 30

This will show the files in C:\temp. Wait 30 seconds, clear the screen, and show the files in C:\temp until you press Ctrl-C. (I also use this to monitor services or processes. It keeps doing everything inside the curly brackets until you stop it. Better than refreshing/pressing F5 all the time 😉 )

Exit mobile version