Get the code: LearnPowershell.ps1
PowerShell is a cross-platform task automation solution made up of a command-line shell, a scripting language, and a configuration management framework. PowerShell runs on Windows, macOS, and Linux.
Windows PowerShell (v5.1) ships with Windows and runs on the .NET Framework.
PowerShell 7.x is the modern, open-source edition built on .NET (currently .NET 9
for PowerShell 7.5 and .NET 10 for the upcoming PowerShell 7.6).
The executable was renamed from powershell.exe to pwsh.exe starting with
PowerShell 6+.
Nearly all examples below can be a part of a shell script or executed directly in the shell.
A key difference with Bash is that it is mostly objects that you manipulate rather than plain text. After years of evolving, it resembles Python a bit.
Powershell as a Language:
# Single line comments start with a number symbol.
<#
Multi-line comments
like so
#>
####################################################
## 1. Primitive Datatypes and Operators
####################################################
# Numbers
3 # => 3
# Math
1 + 1 # => 2
8 - 1 # => 7
10 * 2 # => 20
35 / 5 # => 7.0
# Powershell uses banker's rounding,
# meaning [int]1.5 would round to 2 but so would [int]2.5
# Division always returns a float.
# You must cast result to [int] to round.
[int]5 / [int]3 # => 1.66666666666667
[int]-5 / [int]3 # => -1.66666666666667
5.0 / 3.0 # => 1.66666666666667
-5.0 / 3.0 # => -1.66666666666667
[int]$result = 5 / 3
$result # => 2
# Modulo operation
7 % 3 # => 1
# Exponentiation requires longform or the built-in [Math] class.
[Math]::Pow(2,3) # => 8
# Enforce order of operations with parentheses.
1 + 3 * 2 # => 7
(1 + 3) * 2 # => 8
# Boolean values are primitives (Note: the $)
$True # => True
$False # => False
# negate with !
!$True # => False
!$False # => True
# Boolean Operators
# Note "-and" and "-or" usage
$True -and $False # => False
$False -or $True # => True
# True and False are actually 1 and 0 but only support limited arithmetic.
# However, casting the bool to int resolves this.
$True + $True # => 2
$True * 8 # => '[System.Boolean] * [System.Int32]' is undefined
[int]$True * 8 # => 8
$False - 5 # => -5
# Comparison operators look at the numerical value of True and False.
0 -eq $False # => True
1 -eq $True # => True
2 -eq $True # => False
-5 -ne $False # => True
# Using boolean logical operators on ints casts to booleans for evaluation.
# but their non-cast value is returned
# Don't mix up with bool(ints) and bitwise -band/-bor
[bool](0) # => False
[bool](4) # => True
[bool](-6) # => True
0 -band 2 # => 0
-5 -bor 0 # => -5
# Equality is -eq (equals)
1 -eq 1 # => True
2 -eq 1 # => False
# Inequality is -ne (notequals)
1 -ne 1 # => False
2 -ne 1 # => True
# More comparisons
1 -lt 10 # => True
1 -gt 10 # => False
2 -le 2 # => True
2 -ge 2 # => True
# Seeing whether a value is in a range
1 -lt 2 -and 2 -lt 3 # => True
2 -lt 3 -and 3 -lt 2 # => False
# (-is vs. -eq) -is checks if two objects are the same type.
# -eq checks if the objects have the same values, but sometimes doesn't work
# as expected.
# Note: we called '[Math]' from .NET previously without the preceding
# namespaces. We can do the same with [Collections.ArrayList] if preferred.
[System.Collections.ArrayList]$a = @() # Point a at a new list
$a = (1,2,3,4)
$b = $a # => Point b at what a is pointing to
$b -is $a.GetType() # => True, a and b equal same type
$b -eq $a # => None/$null (when comparing arrays, -eq filters matching elements)
[System.Collections.Hashtable]$b = @{} # => Point b at a new hash table
$b = @{'one' = 1
'two' = 2}
$b -is $a.GetType() # => False, a and b types not equal
# Strings are created with " or ' but " is required for string interpolation
"This is a string."
'This is also a string.'
# Strings can be added too! But try not to do this.
"Hello " + "world!" # => "Hello world!"
# A string can be treated like a list of characters
"Hello world!"[0] # => 'H'
# You can find the length of a string
("This is a string").Length # => 16
# You can also format using f-strings or formatted string literals.
$name = "Steve"
$age = 22
"He said his name is $name."
# => "He said his name is Steve"
"{0} said he is {1} years old." -f $name, $age
# => "Steve said he is 22 years old"
"$name's name is $($name.Length) characters long."
# => "Steve's name is 5 characters long."
# Strings can be compared with -eq, but are case insensitive. We can
# force with -ceq or -ieq.
"ab" -eq "ab" # => True
"ab" -eq "AB" # => True!
"ab" -ceq "AB" # => False
"ab" -ieq "AB" # => True
# Escape Characters in Powershell
# Many languages use the '\', but Windows uses this character for
# file paths. Powershell thus uses '`' to escape characters
# Take caution when working with files, as '`' is a
# valid character in NTFS filenames.
"Showing`nEscape Chars" # => new line between Showing and Escape
"Making`tTables`tWith`tTabs" # => Format things with tabs
# Negate pound sign to prevent comment
# Note that the function of '#' is removed, but '#' is still present
`#Get-Process # => Fail: not a recognized cmdlet
# $null is not an object
$null # => None ($null)
# $null, 0, and empty strings and arrays all evaluate to False.
# All other values are True
function Test-Value ($value) {
if ($value) {
Write-Output 'True'
}
else {
Write-Output 'False'
}
}
Test-Value ($null) # => False
Test-Value (0) # => False
Test-Value ("") # => False
Test-Value [] # => True
# *[] calls .NET class; creates '[]' string when passed to function
Test-Value ({}) # => True
Test-Value @() # => False
####################################################
## 2. Variables and Collections
####################################################
# Powershell uses the "Write-Output" function to print
Write-Output "I'm Posh. Nice to meet you!" # => I'm Posh. Nice to meet you!
# Simple way to get input data from console
$userInput = Read-Host "Enter some data: " # Returns the data as a string
# There are no declarations, only assignments.
# Convention is to use camelCase or PascalCase, whatever your team uses.
$someVariable = 5
$someVariable # => 5
# Accessing a previously unassigned variable does not throw exception.
# The value is $null by default
# Ternary Operators exist in Powershell 7 and up
0 ? 'yes' : 'no' # => no
# Null-coalescing operator ?? (PowerShell 7+)
# Returns left-hand operand if non-null, otherwise right-hand operand
$x = $null
$x ?? 100 # => 100
"hello" ?? "world" # => "hello"
# Null-coalescing assignment operator ??= (PowerShell 7+)
# Assigns the right-hand value only if the variable is $null
$x = $null
$x ??= 100
$x # => 100
$x ??= 200
$x # => 100 (unchanged, because $x is no longer $null)
# Null-conditional operators ?. and ?[] (PowerShell 7.1+)
# Access members/elements only if the operand is non-null
$a = @{ PropName = 100 }
${a}?.PropName # => 100
$a = $null
${a}?.PropName # => $null (no error)
$arr = 1..10
${arr}?[0] # => 1
$arr = $null
${arr}?[0] # => $null (no error)
# Pipeline chain operators && and || (PowerShell 7+)
# && executes the right-hand pipeline only if the left-hand succeeded
# || executes the right-hand pipeline only if the left-hand failed
Write-Output 'Success' && Write-Output 'Also runs' # Both print
Write-Output 'Success' || Write-Output 'Skipped' # Only first prints
# Background operator & (PowerShell 6+)
# Run a pipeline as a background job
Get-Process & # returns a Job object immediately
# The default array object in Powershell is an fixed length array.
$defaultArray = "thing","thing2","thing3"
# you can add objects with '+=', but cannot remove objects.
$defaultArray.Add("thing4") # => Exception "Collection was of a fixed size."
# To have a more workable array, you'll want the .NET [ArrayList] class
# or [System.Collections.Generic.List[T]] (preferred in modern PowerShell)
# It is also worth noting that ArrayLists are significantly faster
# Note: In PowerShell 7.5, the += operation for object arrays was heavily
# optimized and is now much faster than in previous versions.
# ArrayLists store sequences
[System.Collections.ArrayList]$array = @()
# You can start with a prefilled ArrayList
[System.Collections.ArrayList]$otherArray = @(5, 6, 7, 8)
# Modern alternative: use Generic List (preferred over ArrayList)
[System.Collections.Generic.List[int]]$typedList = @()
$typedList.Add(42)
$typedList.Add(99)
$typedList # => 42, 99
# Add to the end of a list with 'Add' (Note: produces output, append to $null)
$array.Add(1) > $null # $array is now [1]
$array.Add(2) > $null # $array is now [1, 2]
$array.Add(4) > $null # $array is now [1, 2, 4]
$array.Add(3) > $null # $array is now [1, 2, 4, 3]
# Remove from end with index of count of objects-1; array index starts at 0
$array.RemoveAt($array.Count-1) # => 3 and array is now [1, 2, 4]
# Let's put it back
$array.Add(3) > $null # array is now [1, 2, 4, 3] again.
# Access a list like you would any array
$array[0] # => 1
# Look at the last element
$array[-1] # => 3
# Looking out of bounds returns nothing
$array[4] # blank line returned
# Remove elements from a array
$array.Remove($array[3]) # $array is now [1, 2, 4]
# Insert at index an element
$array.Insert(2, 3) # $array is now [1, 2, 3, 4]
# Get the index of the first item found matching the argument
$array.IndexOf(2) # => 1
$array.IndexOf(6) # Returns -1 as "outside array"
# You can add arrays
# Note: values for $array and for $otherArray are not modified.
$array + $otherArray # => [1, 2, 3, 4, 5, 6, 7, 8]
# Concatenate arrays with "AddRange()"
$array.AddRange($otherArray) # Now $array is [1, 2, 3, 4, 5, 6, 7, 8]
# Check for existence in a array with "in"
1 -in $array # => True
# Examine length with "Count" (Note: "Length" on arrayList = each items length)
$array.Count # => 8
# You can look at ranges with slice syntax.
$array[1,3,5] # Return selected index => [2, 4, 6]
$array[1..3] # Return from index 1 to 3 => [2, 3, 4]
$array[-3..-1] # Return from last 3 to last 1 => [6, 7, 8]
$array[-1..-3] # Return from last 1 to last 3 => [8, 7, 6]
$array[2..-1] # Return from index 2 to last (NOT as most expect) => [3, 2, 1, 8]
$array[0,2+4..6] # Return multiple ranges with the + => [1, 3, 5, 6, 7]
# -eq doesn't compare array but extract the matching elements
$array = 1,2,3,1,1
$array -eq 1 # => 1,1,1
($array -eq 1).Count # => 3
# Tuples are like arrays but are immutable.
# To use Tuples in powershell, you must use the .NET tuple class.
$tuple = [System.Tuple]::Create(1, 2, 3)
$tuple.Item(0) # => 1
$tuple.Item(0) = 3 # Raises an error (property is read-only)
# or
$tuple.Item1 # => 1
$tuple.Item1 = 3 # Raises an error (property is read-only)
# You can do some of the array methods on tuples, but they are limited.
$tuple.Length # => 3
$tuple + (4, 5, 6) # => Exception
$tuple[0..2] # => $null (in powershell 5) => [1, 2, 3] (in powershell 7)
2 -in $tuple # => False
# Hashtables store mappings from keys to values, similar to (but distinct from) Dictionaries.
# Hashtables do not hold entry order as arrays do.
$emptyHash = @{}
# Here is a prefilled hashtable
$filledHash = @{"one"= 1
"two"= 2
"three"= 3}
# Look up values with []
$filledHash["one"] # => 1
# Get all keys as an iterable with ".Keys".
$filledHash.Keys # => ["one", "two", "three"]
# Get all values as an iterable with ".Values".
$filledHash.Values # => [1, 2, 3]
# Check for existence of keys or values in a hash with "-in"
"one" -in $filledHash.Keys # => True
1 -in $filledHash.Values # => False (in powershell 5) => True (in powershell 7)
# Looking up a non-existing key returns $null
$filledHash["four"] # $null
# Adding to a hashtable
$filledHash.Add("five",5) # $filledHash["five"] is set to 5
$filledHash.Add("five",6) # exception "Item with key "five" has already been added"
$filledHash["four"] = 4 # $filledHash["four"] is set to 4, running again does nothing
# Remove keys from a hashtable
$filledHash.Remove("one") # Removes the key "one" from filled hashtable
####################################################
## 3. Control Flow and Iterables
####################################################
# Let's just make a variable
$someVar = 5
# Here is an if statement.
# This prints "$someVar is smaller than 10"
if ($someVar -gt 10) {
Write-Output "$someVar is bigger than 10."
}
elseif ($someVar -lt 10) { # This elseif clause is optional.
Write-Output "$someVar is smaller than 10."
}
else { # This is optional too.
Write-Output "$someVar is indeed 10."
}
<#
Foreach loops iterate over arrays
prints:
dog is a mammal
cat is a mammal
mouse is a mammal
#>
foreach ($animal in ("dog", "cat", "mouse")) {
# You can use -f to interpolate formatted strings
"{0} is a mammal" -f $animal
}
<#
For loops iterate over arrays and you can specify indices
prints:
0 a
1 b
2 c
3 d
4 e
5 f
6 g
7 h
#>
$letters = ('a','b','c','d','e','f','g','h')
for($i=0; $i -le $letters.Count-1; $i++){
Write-Host $i, $letters[$i]
}
<#
While loops go until a condition is no longer met.
prints:
0
1
2
3
#>
$x = 0
while ($x -lt 4) {
Write-Output $x
$x += 1 # Shorthand for x = x + 1
}
# Switch statements are more powerful compared to most languages
$val = "20"
switch($val) {
{ $_ -eq 42 } { "The answer equals 42"; break }
'20' { "Exactly 20"; break }
{ $_ -like 's*' } { "Case insensitive"; break }
{ $_ -clike 's*'} { "clike, ceq, cne for case sensitive"; break }
{ $_ -notmatch '^.*$'} { "Regex matching. cnotmatch, cnotlike, ..."; break }
default { "Others" }
}
# Handle exceptions with a try/catch block
try {
# Use "throw" to raise an error
throw "This is an error"
}
catch {
Write-Output $_.Exception.Message
}
finally {
Write-Output "We can clean up resources here"
}
# Writing to a file
$contents = @{"aa"= 12
"bb"= 21}
$contents | Export-CSV "$env:HOMEDRIVE\file.csv" # writes to a file
$contents = "test string here"
$contents | Out-File "$env:HOMEDRIVE\file.txt" # writes to another file
# Read file contents and convert to json
Get-Content "$env:HOMEDRIVE\file.csv" | ConvertTo-Json
####################################################
## 4. Functions
####################################################
# Use "function" to create new functions
# Keep the Verb-Noun naming convention for functions
function Add-Numbers {
$args[0] + $args[1]
}
Add-Numbers 1 2 # => 3
# Calling functions with parameters
function Add-ParamNumbers {
param( [int]$firstNumber, [int]$secondNumber )
$firstNumber + $secondNumber
}
Add-ParamNumbers -FirstNumber 1 -SecondNumber 2 # => 3
# Functions with named parameters, parameter attributes, parsable documentation
<#
.SYNOPSIS
Setup a new website
.DESCRIPTION
Creates everything your new website needs for much win
.PARAMETER siteName
The name for the new website
.EXAMPLE
New-Website -Name FancySite -Po 5000
New-Website SiteWithDefaultPort
New-Website siteName 2000 # ERROR! Port argument could not be validated
('name1','name2') | New-Website -Verbose
#>
function New-Website() {
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline=$true, Mandatory=$true)]
[Alias('name')]
[string]$siteName,
[ValidateSet(3000,5000,8000)]
[int]$port = 3000
)
BEGIN { Write-Output 'Creating new website(s)' }
PROCESS { Write-Output "name: $siteName, port: $port" }
END { Write-Output 'Website(s) created' }
}
# Advanced functions support a Clean block (PowerShell 7.3+)
# The Clean block runs after all other blocks, useful for cleanup
function Get-Data {
[CmdletBinding()]
param()
begin { $resource = Open-Something }
process { $resource | Do-Something }
end { }
clean {
# Always runs, even if an error occurred.
# Great for releasing unmanaged resources.
if ($resource) { $resource.Dispose() }
}
}
####################################################
## 5. Modules
####################################################
# You can import modules and install modules
# The Install-Module is similar to pip or npm, pulls from Powershell Gallery
# In PowerShell 7.4+, Microsoft.PowerShell.PSResourceGet is the modern
# replacement for PowerShellGet.
Install-Module dbaTools # Legacy (PowerShellGet)
Install-PSResource dbaTools # Modern (PSResourceGet, PS 7.4+)
Import-Module dbaTools
$query = "SELECT * FROM dbo.sometable"
$queryParams = @{
SqlInstance = 'testInstance'
Database = 'testDatabase'
Query = $query
}
Invoke-DbaQuery @queryParams
# Note: Use '@' instead of '$' to "splat" (unpack) the hash table into parameters.
# Why? Using '$queryParams' would incorrectly pass the whole table as a single object.
# You can get specific functions from a module
Import-Module -Function Invoke-DbaQuery
# Powershell modules are just ordinary Posh files. You
# can write your own, and import them. The name of the
# module is the same as the name of the file.
# You can find out which functions and attributes
# are defined in a module.
Get-Command -module dbaTools
Get-Help dbaTools -Full
####################################################
## 6. Classes and Enums
####################################################
# We use the "class" statement to create a class
class Instrument {
[string]$Type
[string]$Family
}
$instrument = [Instrument]::new()
$instrument.Type = "String Instrument"
$instrument.Family = "Plucked String"
$instrument
<# Output:
Type Family
---- ------
String Instrument Plucked String
#>
####################################################
## 6.1 Inheritance
####################################################
# Inheritance allows new child classes to be defined that inherit
# methods and variables from their parent class.
class Guitar : Instrument
{
[string]$Brand
[string]$SubType
[string]$ModelType
[string]$ModelNumber
}
$myGuitar = [Guitar]::new()
$myGuitar.Brand = "Taylor"
$myGuitar.SubType = "Acoustic"
$myGuitar.ModelType = "Presentation"
$myGuitar.ModelNumber = "PS14ce Blackwood"
$myGuitar.GetType()
<#
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False Guitar Instrument
#>
####################################################
## 6.2 Enums
####################################################
# Enums define a set of named constants
# Note: they enforce type-safety and restrict inputs to a predefined list,
enum Color {
Red
Green
Blue
}
[Color]::Red # => Red
[Color]::Red.value__ # => 0
[Color]'Green' # => Green
# Flags enum - values can be combined
[Flags()] enum FilePermission {
Read = 1
Write = 2
Execute = 4
}
[FilePermission]::Read -bor [FilePermission]::Write # => Read, Write
####################################################
## 7. Advanced
####################################################
# The powershell pipeline allows things like High-Order Functions.
# Group-Object is a handy cmdlet that does incredible things.
# It works much like a GROUP BY in SQL.
<#
The following will get all the running processes,
group them by Name,
and tell us how many instances of each process we have running.
Tip: Chrome and svcHost are usually big numbers in this regard.
#>
Get-Process | Foreach-Object ProcessName | Group-Object
# Useful pipeline examples are iteration and filtering.
1..10 | ForEach-Object { "Loop number $PSITEM" }
1..10 | Where-Object { $PSITEM -gt 5 } | ConvertTo-Json
# ForEach-Object -Parallel (PowerShell 7+)
# Runs script blocks in parallel across pipeline input items.
$logNames = 'Security','Application','System','Windows PowerShell'
$logEntries = $logNames | ForEach-Object -Parallel {
Get-WinEvent -LogName $_ -MaxEvents 1000
} -ThrottleLimit 4 # Max 4 concurrent threads (default is 5)
$logEntries.Count
# Use the $using: scope modifier to pass variables into parallel blocks
$maxEvents = 500
1..5 | ForEach-Object -Parallel {
"Processing item $_ with max $using:maxEvents"
}
# A notable pitfall of the pipeline is its performance when
# compared with other options.
# Additionally, raw bytes are not passed through the pipeline,
# so passing an image causes some issues.
# See more on that in the link at the bottom.
<#
Asynchronous functions exist in the form of jobs.
Typically a procedural language,
Powershell can operate non-blocking functions when invoked as Jobs.
#>
# This function is known to be non-optimized, and therefore slow.
$installedApps = Get-CimInstance -ClassName Win32_Product
# If we had a script, it would hang at this func for a period of time.
$scriptBlock = {Get-CimInstance -ClassName Win32_Product}
Start-Job -ScriptBlock $scriptBlock
# This will start a background job that runs the command.
# You can then obtain the status of jobs and their returned results.
$allJobs = Get-Job
$jobResponse = Get-Job | Receive-Job
# Math is built in to powershell and has many functions.
$r=2
$pi=[math]::pi
$r2=[math]::pow( $r, 2 )
$area = $pi*$r2
$area
# To see all possibilities, check the members.
[System.Math] | Get-Member -Static -MemberType All
<#
This is a silly one:
You may one day be asked to create a func that could take $start and $end
and reverse anything in an array within the given range
based on an arbitrary array without mutating the original array.
Let's see one way to do that and introduce another data structure.
#>
$targetArray = 'a','b','c','d','e','f','g','h','i','j','k','l','m'
function Format-Range ($start, $end, $array) {
[System.Collections.ArrayList]$firstSectionArray = @()
[System.Collections.ArrayList]$secondSectionArray = @()
[System.Collections.Stack]$stack = @()
for ($index = 0; $index -lt $array.Count; $index++) {
if ($index -lt $start) {
$firstSectionArray.Add($array[$index]) > $null
}
elseif ($index -ge $start -and $index -le $end) {
$stack.Push($array[$index])
}
else {
$secondSectionArray.Add($array[$index]) > $null
}
}
$finalArray = $firstSectionArray + $stack.ToArray() + $secondSectionArray
return $finalArray
}
Format-Range 2 6 $targetArray
# => 'a','b','g','f','e','d','c','h','i','j','k','l','m'
# The previous method works, but uses extra memory by allocating new arrays.
# It's also kind of lengthy.
# Let's see how we can do this without allocating a new array.
# This is slightly faster as well.
function Format-Range ($start, $end) {
while ($start -lt $end)
{
$temp = $targetArray[$start]
$targetArray[$start] = $targetArray[$end]
$targetArray[$end] = $temp
$start++
$end--
}
return $targetArray
}
Format-Range 2 6 # => 'a','b','g','f','e','d','c','h','i','j','k','l','m'
####################################################
## 8. New Cmdlets & Features (PowerShell 6.x+)
####################################################
# Get-Error - detailed error view (PowerShell 7.0+)
# $ErrorView defaults to 'ConciseView' for cleaner error messages
$Error | Get-Error
Get-Error -Newest 3
# Get-Uptime - time since last boot (PowerShell 6+)
Get-Uptime
# Join-String - combine pipeline objects into a string (PowerShell 6.2+)
$joinedString = "one","two","three" | Join-String -Separator ", "
$joinedString # => "one, two, three"
$splitString = $joinedString.Split(", ")
$splitString # => one
# two
# three
# Test-Json - validate JSON strings (PowerShell 6.1+)
'{"name": "test"}' | Test-Json # => True
# In PowerShell 7.5+, Test-Json supports -IgnoreComments and -AllowTrailingCommas
'{"name": "test", /* comment */ }' | Test-Json -AllowTrailingCommas -IgnoreComments
# ConvertTo-CliXml / ConvertFrom-CliXml (PowerShell 7.5+)
# Serialize/deserialize objects to/from CliXml strings (without files)
$obj = [pscustomobject]@{ Name = "test"; Value = 42 }
$xml = $obj | ConvertTo-CliXml
$xml | ConvertFrom-CliXml # => recreated object
# ConvertFrom-Json -DateKind (PowerShell 7.5+)
# Control how date strings in JSON are parsed
'{"dt": "2024-01-15T10:30:00Z"}' | ConvertFrom-Json -DateKind Utc
# Where-Object -Not (PowerShell 6+)
Get-Service | Where-Object -Not DependentServices
# Remove-Alias (PowerShell 6+)
Remove-Alias -Name myAlias
# Markdown cmdlets (PowerShell 6.1+)
"**bold** and *italic*" | ConvertFrom-Markdown | Show-Markdown
# Remove-Service (PowerShell 6+)
# Remove-Service -Name "MyTestService"
# Get-Process no longer needs admin for -IncludeUserName (PowerShell 7.5+)
Get-Process -IncludeUserName
Powershell as a Tool:
Getting Help:
# Find commands
Get-Command about_* # alias: gcm
Get-Command -Verb Add
Get-Alias ps
Get-Alias -Definition Get-Process
Get-Help ps | less # alias: help
ps | Get-Member # alias: gm
Show-Command Get-WinEvent # Display GUI to fill in the parameters
Update-Help # No longer requires admin in PowerShell 7+
If you are uncertain about your environment:
Get-ExecutionPolicy -List
Set-ExecutionPolicy AllSigned
# Execution policies include:
# - Restricted: Scripts won't run.
# - RemoteSigned: Downloaded scripts run only if signed by a trusted publisher.
# - AllSigned: Scripts need to be signed by a trusted publisher.
# - Unrestricted: Run all scripts.
help about_Execution_Policies # for more info
# Current PowerShell version:
$PSVersionTable
# Check which edition you're running:
$PSVersionTable.PSEdition # => "Core" for PowerShell 7+, "Desktop" for 5.1
# Calling external commands, executables,
# and functions with the call operator.
# Exe paths with arguments passed or containing spaces can create issues.
C:\Program Files\dotnet\dotnet.exe
# The term 'C:\Program' is not recognized as a name of a cmdlet,
# function, script file, or executable program.
# Check the spelling of the name, or if a path was included,
# verify that the path is correct and try again
"C:\Program Files\dotnet\dotnet.exe"
C:\Program Files\dotnet\dotnet.exe # returns string rather than execute
&"C:\Program Files\dotnet\dotnet.exe --help" # fail
&"C:\Program Files\dotnet\dotnet.exe" --help # success
# Alternatively, you can use dot-sourcing here
."C:\Program Files\dotnet\dotnet.exe" --help # success
# the call operator (&) is similar to Invoke-Expression,
# but IEX runs in current scope.
# One usage of '&' would be to invoke a scriptblock inside of your script.
# Notice the variables are scoped
$i = 2
$scriptBlock = { $i=5; Write-Output $i }
& $scriptBlock # => 5
$i # => 2
invoke-expression ' $i=5; Write-Output $i ' # => 5
$i # => 5
# Alternatively, to preserve changes to public variables
# you can use "Dot-Sourcing". This will run in the current scope.
$x=1
&{$x=2};$x # => 1
.{$x=2};$x # => 2
# Remoting into computers is easy.
Enter-PSSession -ComputerName RemoteComputer
# PowerShell supports remoting over SSH on all platforms (PS 6+)
Enter-PSSession -HostName user@ssh.example.com:22
# Once remoted in, you can run commands as if you're local.
RemoteComputer\PS> Get-Process powershell
<#
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1096 44 156324 179068 29.92 11772 1 powershell
545 25 49512 49852 25348 0 powershell
#>
RemoteComputer\PS> Exit-PSSession
<#
Powershell is an incredible tool for Windows management and Automation.
Let's take the following scenario:
You have 10 servers.
You need to check whether a service is running on all of them.
You can RDP and log in, or PSSession to all of them, but why?
Check out the following
#>
$serverList = @(
'server1',
'server2',
'server3',
'server4',
'server5',
'server6',
'server7',
'server8',
'server9',
'server10'
)
[scriptblock]$script = {
Get-Service -DisplayName 'Task Scheduler'
}
foreach ($server in $serverList) {
$cmdSplat = @{
ComputerName = $server
JobName = 'checkService'
ScriptBlock = $script
AsJob = $true
ErrorAction = 'SilentlyContinue'
}
Invoke-Command @cmdSplat | Out-Null
}
<#
Here we've invoked jobs across many servers.
We can now Receive-Job and see if they're all running.
Now scale this up 100x as many servers :)
#>
Interesting Projects and Resources
cd that reads your mindGot a suggestion? A correction, perhaps? Open an Issue on the GitHub Repo, or make a pull request yourself!
Originally contributed by Wouter Van Schandevijl, and updated by 13 contributors.