a desk full of papers

Powershell Cheatsheet

This is a work-in-progress post. It’s never really finished!

Every Cmdlet has this structure: VerbNoun.

Always prefer single quotes over double quotes for strings that do not contain variables to be expanded.

The help system

First, you need to download the help files to your machine using Update-Help because otherwise the output of Get-Help is not very useful.

Get-Help

Displays the help file contents for the provided cmdlet, Name does not need to be exact, * wildcard supported

Get-Help -Name Select-Object -Detailed or Get-Help Select-Object or Get-Help Select

PS > Get-Help Select

NAME
    Select-Object

SYNOPSIS
    Selects objects or object properties.

SYNTAX
...
DESCRIPTION
...
RELATED LINKS
...
REMARKS
...

Get-Command

Lists all available commands (Aliases, Functions, Cmdlets) and which module they are from.

  • Show all “Remove-*” commands
    Get-Command -Verb Remove
  • Show all “*-Service” commands
    Get-Command -Noun Service
  • Show all commands that accept a “ServiceController” as input (via the-InputObject Property)
    Get-Command -ParameterType ServiceController
  • Show all commands from modules containing “Powershell”
    Get-Command -Module *Powershell*
PS > Get-Command *Compress* -Module *Powershell* | Sort-Object -Property Source

CommandType     Name                                               Version
-----------     ----                                               -------
Function        Compress-Archive                                   1.2.5
Function        Compress-Archive                                   1.0.1.0

Get-Member

Displays Properties and Methods of an Object piped into it. Also Displays the type of the object.

PS > Get-UICulture | Get-Member

   TypeName: System.Globalization.CultureInfo

Name                           MemberType Definition
----                           ---------- ----------
ClearCachedData                Method     void ClearCachedData()
ToString                       Method     string ToString()
Calendar                       Property   System.Globalization.Calendar Calendar {get;}
...

Most frequently used commands and features

Sorting, filtering and selecting

You can pipe objects through Sort-Object, Where-Object and Select-Object.

To expand an object completely (display all properties) you can use

  • Select-Object * or if the Cmdlet supports it:
  • Get-ADUser -Identity mike -Properties *

To filter on multiple properties, pass a script block to Where-Object. It returns all objects for which the script block evaluation returns true.

Get-Module -ListAvailable | Where-Object {
    ($_.Name -notlike "Microsoft*" -and $_.Name -notlike "PS*") -and $_.HelpInfoUri
}

Filtering with Wildcards can be accomplished by using -Like "ABC*XYZ", regular expressions can be used with -Match "RegEx" or their case-sensitive pendants -CLike and -CMatch.

As with Selecting Properties, some Cmdlets support filtering at the source which is handy especially if we deal with a lot of data. For example Get-AdUser allows filtering at the source using either PowerShell Expression Language or LDAP query strings:

Get-ADUser -Filter "Name -eq 'ChewDavid'"
Get-ADUser -LDAPFilter '(!userAccountControl:1.2.840.113556.1.4.803:=2)'

Multilining

A newline is allowed after a natural line break character

  • | pipe,
  • , comma,
  • [ { ( opening brackets, braces and parenthesis,
  • ; semicolon,
  • = equality sign and
  • " ' opening double or single quotes.

or anywhere if you use the backtick (`) as the last character at the previous line.

PS > Get-Command | Select-Object * |
>> Sort-Object -Descending Verb |
>> Where-Object CommandType -eq Cmdlet

Formatting

Do this at the end of the pipeline as formatted output can’t be used as input for more Cmdlets.

PS > Get-Command Format-* -Module Microsoft.PowerShell.Utility |
>> Select-Object Name

Name
----
Format-Custom
Format-Hex
Format-List
Format-Table
Format-Wide

Loops

In a pipeline you can use ForEach-Object to run a script block for each object. The current Object is available via the $_ variable.

PS> 1..10 | ForEach-Object { Write-Host $_ }

There is also a for loop.

for ($i = 1; $i -le 10; $i++) {
  Write-Host $i
}

And a while loop.

$i = 1;
while($i -le 10){
  Write-Host $i
  ++$i
}

For the do… loops there are two flavors: do until and do while.

$i = 1;
do{
  Write-Host $i
   ++$i
}until($i -gt 10)
$i = 1;
do{
  Write-Host $i
   ++$i
}while($i -le 10)

Comments

Single line comments are started with # and span till the end of the line.

Multiline comments start with <# and span till the the end is specified with #>.

The pipeline

Piping Objects between Cmdlets. The pipe operator passes the object returned by the Cmdlet on the left side to the InputObject property of the right side Cmdlet. This simplifies code significantly.

#Cumbersome
$serviceControllerObject = Get-Service -Name *WMI*
Select-Object DisplayName -InputObject $serviceControllerObject

#Pipelined and simple
Get-Service -Name *WMI* | Select-Object DisplayName

If a Cmdlet accepts an input object and which types this object can be is documented in the help content:

PS > Get-Help ForEach-Object -Full
...
INPUTS
    System.Management.Automation.PSObject
        You can pipe any object to this cmdlet.
...

Creating and manipulating objects

You can create objects (= anonymous objects, anonymous types, custom objects)

PS > $michael = [pscustomobject]@{
>>   FirstName = 'Michael'
>>   LastName = 'Oberauer'
>>   DoB = '18.07.1994'
>> }

You can use Select-Object to rename Properties

PS > $michael2 = $michael |
>> Select-Object -Property `
>>   @{ Name='DateOfBirth'; Expression={$_.DoB} }, FirstName, LastName
PS > $michael2

DateOfBirth FirstName LastName
----------- --------- --------
18.07.1994  Michael   Oberauer

You can use Objects as inputs to some Cmdlets when their Properties match by name

Piping right to left

If you put something in parentheses, it will be evaluated first

PS >'Background Intelligent Transfer Service', 'Windows Time' |
>> Out-File -FilePath $env:TEMP\services.txt
PS > Stop-Service -DisplayName (Get-Content -Path $env:TEMP\services.txt)

Sending the output of any command into the pipe

If you want to make objects out of strings there are many methods that can help:

  • Import-Csv/ConvertFrom-CSV
  • ConvertFrom-Json
  • ConvertFrom-StringData
  • Add-Member
.\redis-cli.exe -n 0 KEYS * | Foreach-Object { "$_`r`n$(.\redis-cli.exe -n 0 GET $_)`r`n$((.\redis-cli.exe -n 0 TTL $_)/60)" } | ConvertFrom-String -PropertyNames Key, Value, TTL

Providers

They provide file system like access to resources like: the actual filesystem, the registry, a SQL server DB, Active Directory, etc. Some provider modules are loaded by default, others must be included with Import-Module.

You can get the provider<->actual drive mappings using Get-PSDrive.

Listing Elements in a provider is done using Get-ChildItem. When using the FileSystem Provider use -Filter instead of -Include or -Exclude to speed things up.

PS >Get-Childitem -Path Cert:\LocalMachine\My -DNSName "*oberauer*"
PS >Get-Childitem -Path Temp:/ -Filter "*.log"

Functions

Modules (like the ones you can get via PowerShellGet or you can find in $env:PSModulePathor load with Import-Module) contain functions you can call.

Naming Functions, Creating Functions, Deleting Functions

Use the following naming conventions:

  • Pascal casing
  • <Verb>-<Prefix><Noun>
  • The verb must be an approved verb, you can see a list with Get-Verb | Sort-Object -Property Verb
  • The noun must be in singular form (Item not Items)
  • The prefix is used to avoid naming conflicts, so include something personal like your initials
function Get-MopsDevices { ... }

you can find your defined functions this way:

Get-ChildItem -Path Function:\*Mops*

and delete them like this:

Get-ChildItem -Path Function:\*Mops* | Remove-Item

Parameters

Use parameter names equal to or similar to names that are already used by other Cmdlets. You can find them using this snippet:

$searchTerm = "default"; Get-Command -ParameterName *$searchTerm* | Select-Object -ExpandProperty Parameters | Select-Object -ExpandProperty Keys -Unique | Where-Object { $_ -like "*$searchTerm*" }

#SHORTER VERSION

$searchTerm = "default"; (Get-Command -ParameterName *$searchTerm*).Parameters.Keys | Sort-Object | Get-Unique | Where-Object { $_ -like "*$searchTerm*" }

You define the parameters to a function in the params block, mandatory ones will be queried if not provided, and default values can also be specified:

PS> function Get-MopsGreeting {
>>   param(
>>     [Parameter(Mandatory)]
>>     $Name,
>>     $Greeting="Hello"
>>   )
>>   Write-Host "$Greeting $Name"
>> }
PS> Get-MopsGreeting -Name Michael
Hello Michael
P> Get-MopsGreeting -Name Michael -Greeting Hiho
Hiho Michael
PS> Get-MopsGreeting

cmdlet Get-MopsGreeting at command pipeline position 1
Supply values for the following parameters:
Name: Michael
Hello Michael
PS>

Advanced Functions

function Get-RedisKey {
<#
.SYNOPSIS
    Returns a list of keys stored in the redis database

.DESCRIPTION
    Somesome

.PARAMETER Database
    The redis database to retrieve the keys for.

.EXAMPLE
    Get-RedisKey

.EXAMPLE
    Get-RedisKey -Database 5

.INPUTS
    String

.OUTPUTS
    PSCustomObject

.NOTES
    Author:  ...
    Website: ...
    Twitter: ...
#>
    [CmdLetBinding()]
    param (
        [int]$Database = 0,
        [string]$Pattern = '*',
        [ValidateNotNullOrWhiteSpace()] #Mandatory does not work with default values
        [string]$ExecutablePath = "$env:ProgramFiles\Redis\redis-cli.exe"
    )
    
    BEGIN { #Called at the beginning, params coming from pipelines not available
        #Displayed to the user only with -Verbose property set
        Write-Verbose "Using Redis Executable: $ExecutablePath"
    }
    PROCESS { #Called for each param value received via pipeline

    }
    END { #Called at the end of processing to do cleanups etc.

    }
    #You can also just write code without these BEGIN/PROCESS/END blocks

Scoping

You can find the functions defined in the current scope with this command: Get-ChildItem -Path Function:\

By default, functions are script-scoped.

If you need to import functions from another script into the global scope, use dot-sourcing.

#dot-sourcing all functions from myfuncs.ps1
. .\myfuncs.ps1

Comparison

-eqEqual to
-neNot equal to
-gtGreater than
-geGreater than or equal to
-ltLess than
-leLess than or equal to
-LikeMatches using the * wildcard character
-NotLikeInversion of the above
-MatchMatches the specified regular expression
-NotMatchNon-matching the specified regulInversion of the above
-ContainsDetermines if a collection contains a specified value
-NotContainsDetermines if a collection does not contain a specific value
-InDetermines if a specified value is in a collection
-NotInDetermines if a specified value is not in a collection
-ReplaceReplaces the specified value
From https://learn.microsoft.com/en-gb/powershell/scripting/learn/ps101/05-formatting-aliases-providers-comparison?view=powershell-7.4#comparison-operators

Arithmetic

Min, Max, Avg, Sum, Count

Calculating Min, Max, Average or Sum can be done with the Measure-Object Cmdlet. If your Object contains more than one property, use the -Property switch to select which property to do the calculations for.

PS> 0..10 | Measure-Object -AllStats

Count             : 11
Average           : 5
Sum               : 55
Maximum           : 10
Minimum           : 0
StandardDeviation : 3,3166247903554
Property          :

Grouping

You can group an object by distinct property values and count how many objects have distinct values. You also get a set of the objects contained in each group. Grouping by service status as an Example:

PS> Get-Service | Group-Object -Property Status

Count Name                      Group
----- ----                      -----
  162 Stopped                   {…}
  135 Running                   {…}

Error Handling

Terminating vs. Nonterminating Errors

You can turn any nonterminating errors a command might throw into terminating ones using-ErrorAction Stop.

Do not change the $ErrorActionPreference global variable directly. If you need to do it, restore its original value after you’re done.

Modules

By default, all functions and Cmdlets from a Module are exported while variables and aliases are not. For dynamic and script modules, this should be altered with the Export-ModuleMember Cmdlet. Even if you “confirm” the defaults, it’s a best practice and shows your intentions more explicitly. If you plan to share your module consider creating a module manifest with New-ModuleManifest. Modules without manifests show up with version 0.0 in Get-Module‘s output. In the manifest, you should specify FunctionsToExport to mark which functions you wish to export. This removes the need to call Export-ModuleMember.

Dynamic Modules

These only exist in Memory for the current session. They are created with the New-Module Cmdlet.

Script-Modules

They are regular Powershell script files but with the .psm1 file extension.

Importing a script module manually:

Import-Module C:\..\MyModule.psm1

You can also make use of module autoloading. For that create a folder with the same base name as the script module in one of these paths:

  • C:\Users\xxx\Documents\WindowsPowerShell\Modules if you want the module to autoload for a specific user only or
  • C:\Program Files\WindowsPowerShell\Modules if you want the module to autoload for all users

Learning Resources

Book by Mike F. Robbins – free online read

Photo by Firmbee.com on Unsplash