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)