Managing Microsoft 365, automating routine operations, or obtaining data from systems? PowerShell scripts really streamline your day-to-day tasks. PowerShell is a need when working in the IT industry.

For Windows, Linux, and macOS, PowerShell is a sophisticated command line interface (CLI) and scripting language. We may carry out operations like getting users from the Active Directory or checking a server’s network connection with the aid of cmdlets. These actions and procedures can be combined into scripts that we can swiftly execute on numerous PCs or set as daily activities.

In this article, I will explain how you can create your own PowerShell Scripts. Which tools you can use, how to format your scripts and some general tips to get started. At the end of the article, you will also find a template you can use for your scripts.

PowerShell Editor

PowerShell Editor is a tool that provides an integrated development environment (IDE) for writing, debugging, and testing PowerShell scripts. It offers a range of features that make it easier to work with PowerShell, including syntax highlighting, code completion, debugging tools, and integrated help.

For PowerShell, one of the best free editors to start with is Visual Studio Code. This editor, from Microsoft, is completely free and can be used on Windows, Linux, and macOS. To use the editor with PowerShell, you will need to install a plugin (extension).

Visual Studio Code is a great choice for PowerShell scripting, and it is available for free on multiple platforms. To use Visual Studio Code with PowerShell, you will need to install the PowerShell extension, which provides additional features and functionality.

To install the PowerShell extension for Visual Studio Code, follow these steps:

  1. Open Visual Studio Code and click on the Extensions icon on the left-hand side of the screen.
  2. In the search bar, type ‘PowerShell’ and press Enter.
  3. The PowerShell extension should appear in the search results. Click on the ‘Install’ button to install the extension.
  4. Once the extension is installed, you can use it to create, edit, and run PowerShell scripts directly from Visual Studio Code.

Some of the features provided by the PowerShell extension for Visual Studio Code include syntax highlighting, code completion, debugging tools, and integrated terminal support. With these features, you can write and test PowerShell scripts more efficiently and with greater ease.

Running a PowerShell Script

Running a PowerShell script is a straightforward process that involves opening PowerShell and executing the script using the appropriate command. Here are the steps to run a PowerShell script:

  1. Open PowerShell: To open PowerShell, click on the Start menu and search for ‘PowerShell’. Alternatively, you can press the Windows key + R to open the Run dialog box, type ‘powershell’, and press Enter.
  2. Navigate to the directory containing the script: If your script is saved in a specific directory, you will need to navigate to that directory using the ‘cd’ command. For example, if your script is saved in the C:\Scripts directory, type ‘cd C:\Scripts’ and press Enter.
  3. Set the execution policy: By default, PowerShell does not allow the execution of scripts. To run a script, you will need to set the execution policy using the ‘Set-ExecutionPolicy’ command. For example, to set the execution policy to ‘RemoteSigned’, type ‘Set-ExecutionPolicy RemoteSigned’ and press Enter.
  4. Run the script: To run the script, type its name followed by the ‘.ps1’ extension. For example, if your script is named ‘myscript.ps1’, type ‘myscript.ps1’ and press Enter. The script will be executed, and any output will be displayed in the PowerShell window.

You can also run a PowerShell script from within Visual Studio Code by opening the script in the editor and clicking on the ‘Run’ button in the top right-hand corner of the screen. This will execute the script in the integrated terminal, and any output will be displayed in the terminal window.

Using Visual Studio Code

One of the advantages when using Visual Studio Code as an editor for your PowerShell scripts is that you can test and run your scripts in the built-in terminal. You can run the whole script by pressing F5 or run a selection of lines by pressing F8.

Using PowerShell

Another method is using PowerShell itself to run your script. Open Windows PowerShell or Windows Terminal (right-click on Start) and navigate to the folder where your script is saved. Type the filename of your script and press enter.

The Basics

Using Cmdlets in PowerShell

In the command prompt, we have commands, which are built-in functions (commands) that perform a task. For example, if you type hostname in the command prompt, it will show your computer name. Or the command dir will list the contents of the current directory.

In PowerShell, we have command-lets (cmdlets). You can recognize a cmdlet by its naming because it exists of a Verb-Noun pair. This naming convention helps you to understand what a particular cmdlet does.

NoteIn some examples below I am using cmdlets from the Active Directory module. Make sure that you have the module installed if you want to follow the steps. Run the following command to install the module:

Add-WindowsCapability –online –Name “Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0”

For example, the Get-Computername cmdlet gets the computer name (just like hostname in command prompt). And to show the contents of a folder, we can use Get-ChildItem, for example.

Besides the built-in cmdlets, you can also install modules in PowerShell. These modules can, for example, be used to work with Exchange Online or Azure AD. Each module comes with its own cmdlets for its specific task.

PowerShell Verbs

The are a lot of approved verbs that you can use when you are creating your own PowerShell commands or functions. But the most common verbs that you will come across are:

VerbActionExample
AddAdds or appends are resources to another itemAdd-MailboxPermission
ClearRemoves all resources from a container, but doesn’t
delete the container
Clear-Host
ConnectMakes a connection to another systemConnect-AzureAD
DisconnectBreak the connection with the other systemDisconnect-AzureAD
GetRetrieves data from a resourceGet-ChildItem
NewCreates a new resourceNew-Mailbox
RemoveRemove a resource from a containerRemove-Mailbox
SetReplaces data in an existing resourceSet-Mailbox
SelectSelects a resource in a containerSelect-Object

Variables

When writing scripts you will often need to store data temporarily, so you can use it later in your script. For this we use variables. In PowerShell we don’t need to initialize the variables, we can just create them when needed. Variables can not only store data, like strings, and integers, but also the complete output of cmdlets.

Let’s start with a simple example, we take the Get-Computer cmdlet from before. The cmdlet returns your computer name:

NoteThe contents after the # in a PowerShell script is a comment. Comments are used to explain what a functions does and to make your code more reable.

Get-Computer
# Result
LT3452

We don’t want to just show the computer name, but instead, we want to include the computer name inside a nice string. In this case, we can first store the result of the Get-Computername cmdlet into a variable, which we will call computername. And then include this variable inside the string:

# Store computer name inside the variable
$computername = Get-Computername

# Include the variable inside the string
Write-Host "The name of your computer is $computername"

# Result
The name of your computer is LT3452

Comparison Operators and If-Else Conditions

In PowerShell, we can use comparison operators to compare or find matching values. By default, the operators are case-insensitive, but you can pace a c before an operator to make it case-sensitive. For example:

'lazyadmin' -eq 'LazyAdmin'
# Result
True

'lazyadmin' -ceq 'LazyAdmin'
# Result
False

The operators that we can use in PowerShell are:

OperatorCounter-Part operatorDescription
-eq-neEqual or not equal
-gt-ltGreater or less than
-geGreat than or equal to
-leLess than or equal to
-Like-NotLikeMatch a string using * wildcard or not
-Match-NotMatchMatches or not the specified regular expression
-Contains-NotContainsCollection contains a specified value or not
-In-NotInSpecified value in collection or not
-ReplaceReplace specified value

We can use these operators in combination with for example If-Else statements. If statements allow us to check if a particular comparison is true or not. Depending on the outcome we can execute a piece of code or skip to another part.

Let’s take the computer name example again. We have stored the computer name inside the variable. Now let’s check if the computer name is my laptop:

$computername = Get-Computername

# Check if the computer name equal LT3452
if ($computername -eq 'LT3452') {
  Write-Host "This computer is from LazyAdmin"
}else{
  Write-host "This is someone else's computer"
}

Our computer names start with LT for laptops and PC for desktops. So we could determine if the device is a laptop or not based on the computer name. For this, we are going to use an if statement with an -like operator. The like operator accepts a wildcard, so we can, for example, check if a string starts with “LT” in this case

$computername = Get-Computername

# Check if the computer name start with LT
if ($computername -like 'LT*') {
  Write-Host "This is a laptop"
}else{
  Write-host "This is something else"
}

Passing true results with Pipelining

Besides variables, there is another way to pass data through to another cmdlet in PowerShell, which is using the pipeline operator . The pipeline operator passes the results of the command to the next command. The most common example of piping is formating or selecting the result of a cmdlet.

Let’s take the following example, the cmdlet Get-NetIPAddress returns the IP Address configuration of all your network interfaces. By default, it will return the results in a list format

Get-NetIPAddress

To make the results more readable we can format the results into a tablet or select only the properties that we need. We do this by piping the cmdlet format-table (ft) behind it or the cmdlet select-object (select):

Get-NetIPAddress | FT

# Or select the fields
Get-NetIPAddress | Select InterfaceAlias, IPAddress, PrefixOrigin

Now, this is a simple example of piping cmdlets. But let’s take a look at a more advanced use of piping cmdlets. We are going to collect all user mailboxes, from each mailbox we are going to look up the mailbox statistics, select only the fields that we need, and export the results to a CSV file. Without the pipe operator, we would need to write a code similar to this:

NoteGet-EXOMailbox cmdlet is part of the Exchange Online module. Make sure that you have installed it if you want to follow the steps below.

$mailboxes = Get-EXOMailbox -RecipientTypeDetails UserMailbox

$mailboxes.ForEach({
  $Mailboxstats = Get-EXOMailboxStatistics -Identity $_.Identity
  $fields = Select-Object -InputObject $Mailboxstats -Property DisplayName,ItemCount,TotalItemSize
  Export-CSV -InputObject $fields -Path c:\temp\file.csv -Append
})

But with piping in PowerShell we can simply do the following:

Get-EXOMailbox -RecipientTypeDetails UserMailbox | Get-EXOMailboxStatistics | Select DisplayName, ItemCount, TotalItemSize | Export-CSV c:\temp\filename.csv

Storing data in Arrays and Hashtables

Arrays and hashtables can be used to store a collection of data. Hashtables use a key value principle where you need to define the key before you can store the value. Arrays use an automatically generated index to store the values.

To create an array we can simply assign multiple values to a variable, separating each of them with a common. For example:

# Create an array of fruits
$array = 'apple','raspberry','kiwi'

Another option is to first initialize the array and add values to the array later on. To create an empty array we will use the @ symbol followed by parentheses :

# Create an empty array
$fruits = @()

# Add content to the array
$fruits += "apple"

Hashtables are also known as associative arrays, they are used when you need t store data in a more structured manner. Instead of a numbered index, it’s based on a key-value pair, where you need to define the key yourself. To create an empty hashtable you will need to use curly brackets and the @ symbol:

# Create empty hashtable
$hashTable = @{}

You could for example use a hashtable to store the server IP Addresses

$serverIps= @{
  'la-srv-lab02' = '192.168.10.2'
  'la-srv-db01' = '192.168.10.100'
}

# Result
Name                           Value
----                           -----
la-srv-lab02                   192.168.10.2
la-srv-db01                    192.168.10.100

Or as a config file inside your script:

$mail = @{
  SmtpServer  = 'smtp.contoso.com'
  To          = '[email protected]'
  From        = '[email protected]'
  Subject     = 'super long subject goes here'
  Body        = 'Test email from PowerShell'
  Priority    = High
}

Looping through data with Foreach and Do-While

Looping through data is one of the common tasks in any scripting language. ForEach loops allow you to go through each item in a collection and do something with that item. For example, we take the array of fruits and write each item (fruit) to the console:

$fruits = @('apple','pear','banana','lemon','lime','mango')

# Foreach block
Foreach ($fruit in $fruits) {
    Write-Host $fruit;
}

# Shorthand
$fruits.foreach( {
    Write-Host $_;
})

In the example above we only used a simple array, but you can also use ForEach on objects. Inside the ForEach block you can access each property of the object, for example, if we get all the mailboxes, we can access the display name as follows:

$mailboxes = Get-EXOMailbox -RecipientTypeDetails UserMailbox

$mailboxes.ForEach({
  # Write the displayname of each mailbox
  Write-host $_.DisplayName
})

Besides ForEach loops, we can also use While and Do-While loops. A while loop will only run when a condition is met, the Do-While always runs once and as long as the condition is true.

# Do While loop
Do {
    Write-Host "Online"
    Start-Sleep 5
}
While (Test-Connection -ComputerName 8.8.8.8 -Quiet -Count 1)

Write-Host "Offline"

# While loop
$i = 0;
$path = "C:\temp"

While ($i -lt 10) {
    # Do Something
    $newFile = "$path\while_test_file_" + $i + ".txt";
    New-Item $newFile
    $i++;
}

Now, this is a basic implementation of a try-catch block, it’s even possible to use multiple catch blocks on a single Try statement, allowing you to catch different errors. 

Creating PowerShell Scripts

You should now have a brief understanding of what tools you can use in PowerShell to create scripts. So let’s take a look at how we combine this into true PowerShell scripts. For the examples below we are going to create a small script that creates test files in a given folder.

Below you will find the basic principle of the script. We have a path, hardcoded in the script, an array with the numbers 1 to 10, and a ForEach loop. With the examples below we are going to enhance this script to a true PowerShell script.

$path = "C:\temp"

1..10 | ForEach-Object {
    $newFile = "$path\test_file_$_.txt";
    New-Item $newFile
}

Documenting and Comments

Documenting and commenting your PowerShell scripts is an important practice that can help you and others understand your code better. Here are some tips for documenting and commenting your PowerShell scripts:

  1. Use comments to explain your code: Comments are lines of text that are ignored by PowerShell and are used to explain your code. You should use comments to explain what your code does, how it works, and why it is needed. For example:
# This script generates a report of all users in the HR department.
  1. Use meaningful variable and function names: Use descriptive names for your variables and functions that make it clear what they are used for. This will make your code easier to understand for others who may read it. For example:
$HRUsers = Get-ADUser -Filter "Department -eq 'HR'"
  1. Add help information: PowerShell scripts can include help information that describes what the script does, its parameters, and how to use it. To add help information to your script, use the ‘comment-based help’ syntax. For example:
<#
.SYNOPSIS
Generates a report of all users in the HR department.

.DESCRIPTION
This script generates a report of all users in the HR department, including their name, job title, and email address.

.PARAMETER OutputFile
The name of the output file to save the report to.

.EXAMPLE
.\Get-HRUsersReport.ps1 -OutputFile C:\Reports\HRUsers.csv
Generates a report of all users in the HR department and saves it to the specified output file.

#>

Param (
    [string]$OutputFile
)

$HRUsers = Get-ADUser -Filter "Department -eq 'HR'"
$HRUsers | Select-Object Name, Title, EmailAddress | Export-Csv $OutputFile -NoTypeInformation

By following these tips, you can make your PowerShell scripts easier to understand and maintain, and improve their overall quality.

Cmdlet parameters are a way to customize and fine-tune the behavior of PowerShell cmdlets. They allow you to pass input data to a cmdlet, and modify how the cmdlet processes that data. Here are some tips for working with cmdlet parameters in PowerShell:

  1. Understand parameter types: PowerShell supports different types of parameters, including positional parameters, named parameters, and switch parameters. Positional parameters are identified by their position in the cmdlet syntax, while named parameters are identified by their name. Switch parameters are used to specify Boolean values, and are identified by a dash (-) followed by the name of the switch parameter.
  2. Use tab completion: PowerShell provides tab completion for cmdlet parameters, which can save you time and prevent errors. To use tab completion, type the first few letters of a parameter, then press the Tab key. PowerShell will display a list of matching parameters, which you can select by typing more letters or pressing the Tab key again.
  3. Use parameter sets: PowerShell supports parameter sets, which are groups of parameters that are used together in a specific way. Parameter sets allow you to create more specialized cmdlets that can handle specific scenarios. To use a parameter set, add the [Parameter()] attribute to each parameter, and specify the name of the parameter set that the parameter belongs to.
  4. Use validation attributes: PowerShell provides validation attributes that you can use to validate parameter values before they are processed by a cmdlet. Validation attributes can help you prevent errors and ensure that your cmdlets behave correctly. To use a validation attribute, add the attribute to the parameter definition, and specify the validation criteria.
  5. Use default values: PowerShell allows you to specify default values for cmdlet parameters, which can simplify the use of your cmdlets. To specify a default value, use the [Parameter()] attribute and include the default value in the attribute.

Read-Host

Our script is going to create text files in the given folder. If the user makes a typo, then it is possible that the script is going to create 1000 files for example, instead of 100. So before we are going to create the files, it might be a good idea to ask for confirmation. For this, we are going to use the Read-Host cmdlet.

Read-Host is used to prompt the user for input. This can be a string or password for example. The results of the input can be validated with a simple comparison. In this case, we are first going to write the console the values that the user has given up through the parameters, and then ask if the user wants to continue:

# Ask for confirmation
Write-host "Creating $amount test files in $path" -ForegroundColor Cyan
$reply = Read-Host -Prompt "Is this correct? [Y] Yes [N] No "

# Check the reply and create the files:
if ( $reply -match "[yY]" ) {
    # Create files
    1..10 | ForEach-Object {
      $newFile = "$path\test_file_$_.txt";
      New-Item $newFile
    }
}

Functions

Functions are a great way to organize and reuse pieces of code inside your script. A function should perform a single task. You can find a good example of that in this OneDrive Size Report script. The function ConvertTo-Gb takes a value and converts it to Gigabytes and returns the result. If you scroll a bit down in the script you will see that the function Get-OneDriveStats gathers all OneDrives and process each of them. Inside the ForEach loop, I convert the values using the ConvertTo-GB function.

The structure of a function is basically a small script. We start with a short description, the synopsis. If we need to pass values to the script, we will use Parameters. You may also notice that some functions have 3 code blocks, a begin, process, and end. The process block is executed for all values that you pass to the function and the begin and end blocks only once. Now I have to say that I don’t use begin and end often.

So we are going to create a simple function for our test files script. I have added the parameter Path in the function as well. It’s not really necessary, because the function has also access to the parameters that we have set at the beginning of the script. (this is more to demonstrate the possibilities)

Function New-TestFiles{
  <#
    .SYNOPSIS
        Create test files
  #>
  param(
    [Parameter(Mandatory = $true)]
    [string]$path
  )
  1..$amount | ForEach-Object {
    $newFile = "$path\test_file_$_.txt";
    New-Item $newFile
  }
} 

We can now use the function in our script as follows:

if ( $reply -match "[yY]" ) {
    New-TestFiles -path $path
}

Breakpoints

A typo, a forgotten quote, or a small mistake is easily made when creating a PowerShell script. That is why it’s important to constantly test your script when you are writing it. But even then it can sometimes be hard to find the cause of the error in your script.

To help you find errors in your script you can use breakpoints. If you are using Visual Studio Code, then you can use the built-in Run and Debug function, allowing you to set line breakpoints and run your script line by line.

In visual studio code, we can set breakpoints by clicking on the red dot just before the line number. In the screenshot below, I have set a breakpoint (1) on the New-Item cmdlet at line 46. When we click on Run (2) the script will be executed and halt on the breakpoint at line 46. At the moment, we can see the variables on the left side (3). This tells us that the ForEach-Object loop is at the first item, and the new file will be created in c:\temp\files with the name test_file_1.txt

With the buttons at the top (5), we can move to the next step, skip a step, etc.

Using breakpoints in PowerShell

Another option to debug your PowerShell script is to use the cmdlet Set-PSBreakpoint in the console. This cmdlet allows you to set breakpoints for any script that you want to run in PowerShell. With the cmdlet, we can set breakpoints on a line number, action, or variable. So let’s set the same breakpoint as we did in Visual Studio Code:

# Set the breakpoint on line 46 for the script CreateTestFiles.ps1
Set-PSBreakPoint -Script .\CreateTestFiles.ps1 -Line 46 

When we now run the script, it will stop at line 46:

The script is stopped, but it doesn’t show anything. So we are going to start by showing the code around our breakpoint. Just type in the letter L in the console:

The asterisk symbol on line 46 indicated the breakpoints that we have set. If we want to know what the path variable is, or the new file name, we can simply type in the variable names in the console and press enter:

The asterisk symbol on line 46 indicated the breakpoints that we have set. If we want to know what the path variable is, or the new file name, we can simply type in the variable names in the console and press enter:

Get-PSBreakPoint | Remove-PSBreakpoint

Wrapping Up

When creating PowerShell Scripts, always start small and test your script often. Also, make sure that you add comments to your code where necessary. It may be clear now what the codes those, but if you need to edit your script a year later it can sometimes be a puzzle to figure out what the script does and how it works.

If you like to learn more about PowerShell then the book below is really a good read. This is one of the best sellers when it comes to learning PowerShell:

In the examples above we create a small script that creates test files, below you will find the complete script. As mentioned, it’s a bit overkill for such a simple task, but it gives you an idea of how to build up your script.

<#
  .SYNOPSIS
  Create test files in given directory

  .DESCRIPTION
  The script generates an x amount (by default 10) of text file based test file in the
  given folder. The files don't contain any content.

  .EXAMPLE
  CreateTestFiles.ps1 -path c:\temp -amount 50

  Create 50 files in c:\temp

  .NOTES
  Version:        1.0
  Author:         R. Mens - LazyAdmin.nl
  Creation Date:  04 oct 2022
  Modified Date:
  Purpose/Change: Init
  Link:           https://lazyadmin.nl/powershell/powershell-scripting
#>

param(
  [Parameter(
    Mandatory = $true,
    HelpMessage = "Enter path were test files should be created"
  )]
  [string]$path,

  [Parameter(
    HelpMessage = "How many files should be created"
  )]
  [int]$amount = 10
)

Function New-TestFiles{
  <#
    .SYNOPSIS
        Create test files
  #>
  param(
    [Parameter(Mandatory = $true)]
    [string]$path
  )
  1..$amount | ForEach-Object {
    $newFile = "$path\test_file_$_.txt";
    New-Item $newFile
  }
}

# Ask for confirmation
Write-host "Creating $amount test files in $path" -ForegroundColor Cyan
$reply = Read-Host -Prompt "Is this correct? [Y] Yes [N] No "

if ( $reply -match "[yY]" ) {
    New-TestFiles -path $path
}
Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *