This is a work-in-progress post. It’s never really finished!
Every Cmdlet has this structure: Verb–Noun.
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
Aliasing
Create aliases for often-used commands/programs. These only survive the current PSSession, if you want to have them permanently, add them to your profile and reload it.
Only for current Session:
PS > Set-Alias -Name npp -Value 'C:\Program Files\Notepad++\notepad++.exe'
Permanent:
PS > "Set-Alias npp `"$env:ProgramFiles\Notepad++\notepad++.exe`"" >> $profile
PS > . $profile
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:PSModulePath
or 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
-eq | Equal to |
-ne | Not equal to |
-gt | Greater than |
-ge | Greater than or equal to |
-lt | Less than |
-le | Less than or equal to |
-Like | Matches using the * wildcard character |
-NotLike | Inversion of the above |
-Match | Matches the specified regular expression |
-NotMatch | Non-matching the specified regulInversion of the above |
-Contains | Determines if a collection contains a specified value |
-NotContains | Determines if a collection does not contain a specific value |
-In | Determines if a specified value is in a collection |
-NotIn | Determines if a specified value is not in a collection |
-Replace | Replaces the specified value |
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