Code Snippets

TechSnips

The TechSnips site is a great resource for learning and especially PowerShell, so check out TechSnips/SnipScripts: This is where all scripts demonstrated by our TechSnips contributors will be provided free of charge for lots of small pieces of code.

Comments

There are two ways to add comments to a PowerShell script.

Line Comment

Just make sure the line starts with a hash character or # and the whole line will be ignored and treated as a commend. I sometimes put these on a line after my code, to explain what it does, so you can just comment out the last part of a line, if you want.

Block Comment

You can comment out a whole block of code be enclosing it within "<#" and "#>", however this also has a special purpose for documenting your code, see about_Comment_Based_Help for details.

Hide Output

Some commands default to giving output to the console, New-Item for example does this. However it is not hard to suppress this as in the following example:
New-Item -ItemType Directory "C:\Temp\NewFolder" | Out-Null

Read File

There seems to be one standard, reliable approach, which is as follows: $content = [IO.File]::ReadAllText(".\test.txt"), this works well, however in PowerShell 3 and above you can use $text = Get-Content .\file.txt -Raw . Note that without the -Raw you will get an array of lines.

Loops

There are a number of options when looping: For, ForEach, While, Do...While, Do...Until, these are all described at PowerShell Loops - TechNet Articles - United States (English) - TechNet Wiki. However, if you want to exit a loop "early" then use Break.

Some examples:
$array = 1,2,3,4,5
ForEach ($value in $array) {
    Write-Host "Value: $($value)"
}
For ($i; $i-lt5; $i++) {
    Write-Host $i
}

I have also used ForEach with collections.

Switch and If

You will probably need to read about_Comparison_Operators | Microsoft Docs and about_Logical_Operators | Microsoft Docs if you are using if statements.

Great to see switch working on strings! For examples of using If, see Hey, Scripting Guy! How Can I Use the If Statement in Windows PowerShell? - Hey, Scripting Guy! Blog - Site Home - TechNet Blogs. However the most comprehensive explanation of switch I have seen is Powershell: Everything you ever wanted to know about the switch statement, there is also a similar article for if which is Powershell: Everything you wanted to know about the IF statement.

Booleans

The key point to note is that $TRUE and $FALSE represent True and False but see Boolean Values and Operators - Windows PowerShell Blog - Site Home - MSDN Blogs and Understanding Booleans in PowerShell - TechNet Articles - TechNet Wiki for more details. However for comparisons etc see about_Comparison_Operators and also see about_Logical_Operators.

Temporary File

Here is a nice little example of working with a temporary file:
 
$TmpFile = [System.IO.Path]::GetTempFileName()
Get-ChildItem $TmpFile # Same as ls
Remove-Item $TmpFile
ls $TmpFile

Script Directory

Sometimes you need to know the directory your script file is located in, especially if it is not the current directory. This is easily done with $MyInvocation.MyCommand.Path, well that gets the filename as well, doing Split-Path $MyInvocation.MyCommand.Path will get you just the directory. In PowerShell 2 and above you can use $PSScriptRoot, however it only works in script modules (.psm1) but in PowerShell 3 it works in regular scripts as well.

Null

This is one of those sometimes useful programming constructs and other times troublesome. If you want a null, then in PowerShell you can use $null. It is well worth reading Powershell: Everything you wanted to know about $null to fully understand how null works in PowerShell.

PowerShell Versions

Example code for getting the versions:
Write-Host "PowerShell Version: $($PSVersionTable.PSVersion)"
Write-Host "Compatible Versions: $($PSVersionTable.PSCompatibleVersions)"
ForEach ($version in $PSVersionTable.PSCompatibleVersions) {
    Write-Host "-Version: $version"
}

If you wish to display a more complete version then the following is a good option:
Write-Output "PowerShell $($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor).$($PSVersionTable.PSVersion.Patch)"
It is worth noting that "Patch" only exists in PowerShell Core, or 6.0 and above, so in general it is best to stick with just Major and Minor for now. More often though you will want to test the version number in your code, so that you can be compatible with multiple versions of PowerShell, in this case I have used things like:
if ($PSVersionTable.PSVersion.Major -ge 6)

Runtime

There are plenty of interesting options documented at Environment Class (System), however the following code has proved useful:
Write-Host "Is this 64-bit PowerShell? $([Environment]::Is64BitProcess)"
It is worth reading the link and seeing what else you can do.

Platform Differences

One commonly encountered difference between Windows and Linux is the "new line" characters. So if you want to get and use the new line characters for the current platform then I suggest a definition as follows:
Set-Variable -Option Constant -Name "newline" -Value $([Environment]::NewLine)
This constant is then easy to use but it is defined at runtime.

Adding Users to Groups

This is a handy but simple code example that adds a domain user account to a group on the local machine. This is handy when the visual Computer Management tools are not available or you are running Server Core. To add the user account "john.smith" from the "CORP" domain into the local computer's Power Users group execute the following line of PowerShell:
([ADSI]"WinNT://$env:computername/Power Users,group").Invoke('Add', "WinNT://CORP/john.smith")
You can clearly wrap this into a function and get user input at runtime etc but this is the basic.

By way of reference the above is the same as the following:
net localgroup "Power Users" /ADD CORP\john.smith

Sending E-Mail

The key is using Send-MailMessage, however I would recommend also using Get-Credential to control the message displayed to the user.

String Splitting

One approach is to find the position of the separator and then get the text before and after it, as follows:

$text = "One,Two,Three;Four;Five-Six"
$pos = $text.IndexOf(";")
$leftPart = $text.SubString(0, $pos)
$rightPart = $text.SubString($pos+1)
This results in $leftPart having the text "One,Two,Three" and $rightPart having the text "Four;Five-Six". This technique is nice and simple and clear, however it really only works with one separator and two parts.

The second approach, which gives the same output is $text.Split(";", 2), however this returns an array, so the left part is $text.Split(";", 2)[0] and the right part is $text.Split(";", 2)[1], note that the number 2 specifies a maximum of two parts. If you leave the number off then every semi-colon is treated as a separator. Furthermore if you use $text.Split(",;-") then all three of those characters are used as separators and 6 items are returned, each one containing a word.

Counting Lines

You can use the Measure-Object cmdlet for this, as follows:
dir -Recurse *.txt | Get-Content | Measure-Object -Line
However, I have noticed it does not count blank lines or lines that are just whitespace.

Windows 10 Apps

This is a nice neat one-liner to get all the "apps" installed on Windows 10:

Get-AppXPackage | Select-Object -Property Name, Publisher | Sort-Object -Property Name

Filtering

Although PowerShell Performance: Filtering Collections is focused on performance it is a useful summary of ways to filter. A handy example is as follows:

ForEach ($Process in Get-Process | Where Name -eq "Firefox") {
  $FirefoxWS = $Process.WorkingSet64
  $FirefoxWSKB = $FirefoxWS/1024
  Write-Host "$($Process.Name) - $($Process.ProductVersion)  WS: $($FirefoxWSKB.ToString('#,###').PadLeft(10)) KB - PID: $($Process.ID.ToString('#####').PadLeft(5)) - Threads: $($Process.Threads.Count)"
}
Which shows two different techniques, the "where" and the "ForEach Loop".

Windows Firewall

Originally I was looking for a way to create a group in the "Windows Firewall with Advanced Security" on a Windows Server 2016 box. I do not believe this can be done through the user interface, and so it is easier to use PowerShell to create new rules, see New-NetFirewallRule for the documentation and below for my example:

New-NetFirewallRule -DisplayName "GDS Server" -Group "GeoffDoesStuff" -Description "Allow remote access to the specified ports" -Profile Public -Direction Inbound -Action Allow -LocalPort 9400-9409 -Protocol TCP

Admin Rights Check

$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal] $identity
if ($principal.IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
    Write-Host "Running with Administrator privilege"
} else  {
    Write-Host "Running as standard user"
}