diff --git a/powershell.html.markdown b/powershell.html.markdown
index 6efd984c..f2f2be61 100644
--- a/powershell.html.markdown
+++ b/powershell.html.markdown
@@ -3,6 +3,7 @@ category: tool
tool: powershell
contributors:
- ["Wouter Van Schandevijl", "https://github.com/laoujin"]
+ - ["Andrew Ryan Davis", "https://github.com/AndrewDavis1191"]
filename: LearnPowershell.ps1
---
@@ -13,116 +14,347 @@ 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.
+rather than plain text. After years of evolving, it resembles Python a bit.
[Read more here.](https://docs.microsoft.com/powershell/scripting/overview)
-If you are uncertain about your environment:
-
+Powershell as a Language:
```powershell
-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
-```
-
-Getting help:
-
-```powershell
-# 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-EventLog # Display GUI to fill in the parameters
-
-Update-Help # Run as admin
-```
-
-The tutorial starts here:
-
-```powershell
-# As you already figured, comments start with #
+# Single line comments start with a number symbol.
<#
Multi-line comments
like so
#>
-# Simple hello world example:
-echo Hello world!
-# echo is an alias for Write-Output (=cmdlet)
-# Most cmdlets and functions follow the Verb-Noun naming convention
+####################################################
+## 1. Primitive Datatypes and Operators
+####################################################
-# Each command starts on a new line, or after a semicolon:
-echo 'This is the first line'; echo 'This is the second line'
+# Numbers
+3 # => 3
-# Declaring a variable looks like this:
-$aString="Some string"
-# Or like this:
-$aNumber = 5 -as [double]
-$aList = 1,2,3,4,5
-# Reverse an array *Note this is a mutation on the existing array
-[array]::Reverse($aList)
-$anEmptyList = @()
-$aString = $aList -join '--' # yes, -split exists also
-$aHashtable = @{name1='val1'; name2='val2'}
+# Math
+1 + 1 # => 2
+8 - 1 # => 7
+10 * 2 # => 20
+35 / 5 # => 7.0
-# Using variables:
-echo $aString
-echo "Interpolation: $aString"
-echo "$aString has length of $($aString.Length)"
-echo '$aString'
-echo @"
-This is a Here-String
-$aString
-"@
-# Note that ' (single quote) won't expand the variables!
-# Here-Strings also work with single quote
+# 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 # => 2
-# Builtin variables:
-# There are some useful builtin variables, like
-echo "Booleans: $TRUE and $FALSE"
-echo "Empty value: $NULL"
-echo "Last program's return value: $?"
-echo "Exit code of last run Windows-based program: $LastExitCode"
-echo "The last token in the last line received by the session: $$"
-echo "The first token: $^"
-echo "Script's PID: $PID"
-echo "Full path of current script directory: $PSScriptRoot"
-echo 'Full path of current script: ' + $MyInvocation.MyCommand.Path
-echo "FUll path of current directory: $Pwd"
-echo "Bound arguments in a function, script or code block: $PSBoundParameters"
-echo "Unbound arguments: $($Args -join ', ')."
-# More builtins: `help about_Automatic_Variables`
+# Modulo operation
+7 % 3 # => 1
-# Find the datatype of variables or properties you're working with
-$true.GetType()
-$aHashtable.name2.GetType()
+# Exponentiation requires longform or the built-in [Math] class
+[Math]::Pow(2,3) # => 8
-# Inline another file (dot operator)
-. .\otherScriptName.ps1
+# 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 them to booleans for evaluation
+# but their non-cast value is returned
+# Don't mix up with bool(ints) and bitwise and/or (&,|)
+[bool](0) # => False
+[bool](4) # => True
+[bool](-6) # => True
+0 -and 2 # => 0
+-5 -or 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.
+[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 # => True, a and b values are equal
+[System.Collections.Hashtable]$b = @{} # => Point a 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."
+
+# $null is not an object
+$null # => None
+
+# $null, 0, and empty strings and arrays all evaluate to False.
+# All other values are True
+function test ($value) {
+ if ($value) {Write-Output 'True'}
+ else {Write-Output 'False'}
+}
+test ($null) # => False
+test (0) # => False
+test ("") # => False
+test [] # => True
+test ({}) # => True
+test @() # => False
+
+####################################################
+## 2. Variables and Collections
+####################################################
+
+# Powershell uses the "Write-Output" function to print
+Write-Output "I'm Powershell. Nice to meet you!" # => I'm Powershell. 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
+
+# The default array object in Powershell is an immutable array
+$defaultArray = "thing","thing2","thing3"
+# you are unable to add or remove objects
+$defaultArray.Add("thing4") # => Exception "Collection was of a fixed size."
+# To have a mutable array, you will need to use the .NET ArrayList class
+
+# ArrayLists store sequences
+[System.Collections.ArrayList]$array = @()
+# You can start with a prefilled ArrayList
+[System.Collections.ArrayList]$otherArray = @(4, 5, 6)
+
+# Add stuff to the end of a list with add (Note: it produces output, so 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 the end with index of count of objects-1 as arrays are indexed starting 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
+
+# You can look at ranges with slice syntax.
+# The start index is included, the end index is not
+# (It's a closed/open range for you mathy types.)
+$array[1..3] # Return array from index 1 to 3 => [2, 4]
+$array[2..-1] # Return array starting from index 2 => [4, 3]
+$array[0..3] # Return array from beginning until index 3 => [1, 2, 4]
+$array[0..2] # Return array selecting every second entry => [1, 4]
+$array.Reverse() # mutates array to reverse order => [3, 4, 2, 1]
+# Use any combination of these to make advanced slices
+
+# Remove arbitrary elements from a array with "del"
+$array.Remove($array[2]) # $array is now [1, 2, 3]
+
+# Insert an element at a specific index
+$array.Insert(1, 2) # $array is now [1, 2, 3] again
+
+# 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]
+
+# Concatenate arrays with "AddRange()"
+$array.AddRange($otherArray) # Now $array is [1, 2, 3, 4, 5, 6]
+
+# Check for existence in a array with "in"
+1 -in $array # => True
+
+# Examine the length with "Count" (Note: Length method on arrayList = each items length)
+$array.Count # => 6
-### Control Flow
-# We have the usual if structure:
-if ($Age -is [string]) {
- echo 'But.. $Age cannot be a string!'
-} elseif ($Age -lt 12 -and $Age -gt 0) {
- echo 'Child (Less than 12. Greater than 0)'
-} else {
- echo 'Adult'
+# 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 a TypeError
+
+# 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
+2 -in $tuple # => False
+
+
+# Hashtables store mappings from keys to values, similar to Dictionaries
+$emptyHash = @{}
+# Here is a prefilled dictionary
+$filledHash = @{"one"= 1
+ "two"= 2
+ "three"= 3}
+
+# Look up values with []
+$filledHash["one"] # => 1
+
+# Get all keys as an iterable with ".Keys".
+# items maintain the order at which they are inserted into the dictionary.
+$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
+
+# Looking up a non-existing key returns $null
+$filledHash["four"] # $null
+
+# Adding to a dictionary
+$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, run again and it does nothing
+
+# Remove keys from a dictionary with del
+$filledHash.Remove("one") # Removes the key "one" from filled dict
+
+
+
+####################################################
+## 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
@@ -137,85 +369,52 @@ switch($val) {
default { "Others" }
}
-# The classic for
-for($i = 1; $i -le 10; $i++) {
- "Loop number $i"
+# Handle exceptions with a try/catch block
+try {
+ # Use "throw" to raise an error
+ throw "This is an error"
}
-# Or shorter
-1..10 | % { "Loop number $_" }
-
-# PowerShell also offers
-foreach ($var in 'val1','val2','val3') { echo $var }
-# while () {}
-# do {} while ()
-# do {} until ()
-
-# Exception handling
-try {} catch {} finally {}
-try {} catch [System.NullReferenceException] {
- echo $_.Exception | Format-List -Force
+catch {
+ Write-Output $Error.ExceptionMessage
+}
+finally {
+ Write-Output "We can clean up resources here"
}
-### Providers
-# List files and directories in the current directory
-ls # or `dir`
-cd ~ # goto home
-Get-Alias ls # -> Get-ChildItem
-# Uh!? These cmdlets have generic names because unlike other scripting
-# languages, PowerShell does not only operate in the current directory.
-cd HKCU: # go to the HKEY_CURRENT_USER registry hive
+# Writing to a file
+$contents = @{"aa"= 12
+ "bb"= 21}
+$contents | Export-CSV "$env:HOMEDRIVE\file.csv" # writes to a file
-# Get all providers in your session
-Get-PSProvider
+$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
-### Pipeline
-# Cmdlets have parameters that control their execution:
-Get-ChildItem -Filter *.txt -Name # Get just the name of all txt files
-# Only need to type as much of a parameter name until it is no longer ambiguous
-ls -fi *.txt -n # -f is not possible because -Force also exists
-# Use `Get-Help Get-ChildItem -Full` for a complete overview
-# Results of the previous cmdlet can be passed to the next as input.
-# `$_` is the current object in the pipeline object.
-ls | Where-Object { $_.Name -match 'c' } | Export-CSV export.txt
-ls | ? { $_.Name -match 'c' } | ConvertTo-HTML | Out-File export.html
+####################################################
+## 4. Functions
+####################################################
-# If you get confused in the pipeline use `Get-Member` for an overview
-# of the available methods and properties of the pipelined objects:
-ls | Get-Member
-Get-Date | gm
-
-# ` is the line continuation character. Or end the line with a |
-Get-Process | Sort-Object ID -Descending | Select-Object -First 10 Name,ID,VM `
- | Stop-Process -WhatIf
-
-Get-EventLog Application -After (Get-Date).AddHours(-2) | Format-List
-
-# Use % as a shorthand for ForEach-Object
-('a','b','c') | ForEach-Object `
- -Begin { "Starting"; $counter = 0 } `
- -Process { "Processing $_"; $counter++ } `
- -End { "Finishing: $counter" }
-
-# Get-Process as a table with three columns
-# The third column is the value of the VM property in MB and 2 decimal places
-# Computed columns can be written more verbose as:
-# `@{name='lbl';expression={$_}`
-ps | Format-Table ID,Name,@{n='VM(MB)';e={'{0:n2}' -f ($_.VM / 1MB)}} -autoSize
-
-
-### Functions
-# The [string] attribute is optional.
-# Function names should follow Verb-Noun convention
-function Get-Foo([string]$name) {
- echo "Hey $name, have a function"
+# Use "function" to create new functions
+# Keep the Verb-Noun naming convention for functions
+function Add-Numbers {
+ $args[0] + $args[1]
}
-# Calling your function
-Get-Foo "Say my name"
+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
<#
@@ -232,111 +431,268 @@ 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-Verbose 'Creating new website(s)' }
- PROCESS { echo "name: $siteName, port: $port" }
- END { Write-Verbose 'Website(s) created' }
+ [CmdletBinding()]
+ param (
+ [Parameter(ValueFromPipeline=$true, Mandatory=$true)]
+ [Alias('name')]
+ [string]$siteName,
+ [ValidateSet(3000,5000,8000)]
+ [int]$port = 3000
+ )
+ BEGIN { Write-Verbose 'Creating new website(s)' }
+ PROCESS { echo "name: $siteName, port: $port" }
+ END { Write-Verbose 'Website(s) created' }
}
-### It's all .NET
-# A PS string is in fact a .NET System.String
-# All .NET methods and properties are thus available
-'string'.ToUpper().Replace('G', 'ggg')
-# Or more powershellish
-'string'.ToUpper() -replace 'G', 'ggg'
+####################################################
+## 5. Modules
+####################################################
-# Unsure how that .NET method is called again?
-'string' | gm
+# You can import modules and install modules
+# The Install-Module is similar to pip or npm, pulls from Powershell Gallery
+Install-Module dbaTools
+Import-Module dbaTools
-# Syntax for calling static .NET methods
-[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
-
-# Note that .NET functions MUST be called with parentheses
-# while PS functions CANNOT be called with parentheses.
-# If you do call a cmdlet/PS function with parentheses,
-# it is the same as passing a single parameter list
-$writer = New-Object System.IO.StreamWriter($path, $true)
-$writer.Write([Environment]::NewLine)
-$writer.Dispose()
-
-### IO
-# Reading a value from input:
-$Name = Read-Host "What's your name?"
-echo "Hello, $Name!"
-[int]$Age = Read-Host "What's your age?"
-
-# Test-Path, Split-Path, Join-Path, Resolve-Path
-# Get-Content filename # returns a string[]
-# Set-Content, Add-Content, Clear-Content
-Get-Command ConvertTo-*,ConvertFrom-*
-
-
-### Useful stuff
-# Refresh your PATH
-$env:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") +
- ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
-
-# Find Python in path
-$env:PATH.Split(";") | Where-Object { $_ -like "*python*"}
-
-# Change working directory without having to remember previous path
-Push-Location c:\temp # change working directory to c:\temp
-Pop-Location # change back to previous working directory
-# Aliases are: pushd and popd
-
-# Unblock a directory after download
-Get-ChildItem -Recurse | Unblock-File
-
-# You can also pass arguments to a Function with a hash table
-# This is called Splatting
-# Normal Command
-Export-Csv -InputObject $csv -Path 'c:\mypath' -Encoding UTF8 -NoTypeInformation
-# With Splatting
-$csvArguments = @{
- InputObject = $csv
- Path = 'c:\mypath'
- Encoding = 'UTF8'
- NoTypeInformation = $true
+$query = "SELECT * FROM dbo.sometable"
+$queryParams = @{
+ SqlInstance = 'testInstance'
+ Database = 'testDatabase'
+ Query = $query
}
-Export-Csv @csvArguments
+Invoke-DbaQuery @queryParams
-# Open Windows Explorer in working directory
-Invoke-Item .
-# Or the alias
-ii .
+# You can get specific functions from a module
+Import-Module -Function Invoke-DbaQuery
-# Any key to exit
-$host.UI.RawUI.ReadKey()
-return
-# Create a shortcut
-$WshShell = New-Object -comObject WScript.Shell
-$Shortcut = $WshShell.CreateShortcut($link)
-$Shortcut.TargetPath = $file
-$Shortcut.WorkingDirectory = Split-Path $file
-$Shortcut.Save()
+# 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
+####################################################
+
+# 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
+#>
+
+
+####################################################
+## 7. Advanced
+####################################################
+
+# The powershell pipeline allows us to do things like High-Order Functions
+
+# Group Object is a handy command that does incredible things for us
+# It works much like a GROUP BY in SQL would
+
+<#
+ 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 ProcessName | Group-Object
+
+<#
+ Asynchronous functions exist in the form of jobs
+ Typically a procedural language
+ Powershell can operate many non-blocking functions when invoked as Jobs
+#>
+
+# This function is commonly 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
+ Let's see one way to do that
+#>
+
+$testArray = 'a','b','c','d','e','f','g','h','i','j','k','l','m','n'
+
+function Reverse-Range ($start, $end) {
+[System.Collections.ArrayList]$newArray = @()
+[System.Collections.ArrayList]$secondArray = @()
+[System.Collections.Stack]$stack = @()
+ for ($i = 0; $i -lt $testArray.Length; $i++) {
+ if ($i -lt $start) {
+ $newArray.Add($testArray[$i]) > $null
+ }
+ elseif ($i -ge $start -and $i -le $end) {
+ $stack.push($testArray[$i])
+ }
+ elseif ($i -gt $end) {
+ $secondArray.Add($testArray[$i]) > $null
+ }
+ }
+ $endArray = $newArray + $stack.ToArray() + $secondArray
+ Write-Output $endArray
+}
+```
+Powershell as a Tool:
+
+Getting Help:
+```Powershell
+# 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-EventLog # Display GUI to fill in the parameters
+
+Update-Help # Run as admin
```
+If you are uncertain about your environment:
+```Powershell
+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
-Configuring your shell
+# Current PowerShell version:
+$PSVersionTable
+```
-```powershell
-# $Profile is the full path for your `Microsoft.PowerShell_profile.ps1`
-# All code there will be executed when the PS session starts
-if (-not (Test-Path $Profile)) {
- New-Item -Type file -Path $Profile -Force
- notepad $Profile
+```Powershell
+# Remoting into computers is easy
+Enter-PSSession -ComputerName RemoteComputer
+# 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'
}
-# More info: `help about_profiles`
-# For a more useful shell, be sure to check the project PSReadLine below
+
+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
@@ -350,10 +706,3 @@ Interesting Projects
* [Pester](https://github.com/pester/Pester) BDD Testing Framework
* [Jump-Location](https://github.com/tkellogg/Jump-Location) Powershell `cd` that reads your mind
* [PowerShell Community Extensions](https://github.com/Pscx/Pscx)
-
-Not covered
-
-* WMI: Windows Management Intrumentation (Get-CimInstance)
-* Multitasking: Start-Job -scriptBlock {...},
-* Code Signing
-* Remoting (Enter-PSSession/Exit-PSSession; Invoke-Command)