# PowerShell

PowerShell isn’t just the interactive `powershell.exe` and `powershell_ise.exe` binaries, Powershell itself is actually `System.Management.Automation.dll` which is a dependency of various hosts.

The following is a table that represents the different versions of Powershell supported over time

| Version              | Release Date | OS Support                        |
| -------------------- | ------------ | --------------------------------- |
| PowerShell V1        | 2006         | Windows Server 2008               |
| PowerShell V2        | 2009         | Windows 7, Windows Server 2008 R2 |
| PowerShell V3 / WMF3 | 2013         | Windows 8, Windows Server 2012    |
| PowerShell V4        | 2013         | Windows 8.1                       |
| PowerShell V5        | 2015         | Windows 10, Windows Server 2016   |
| PowerShell Core      | 2016         | Nano Server, Windows 10 IoT       |
| PowerShell V6 (Core) | 2017         | Windows, macOS, \*UNIX            |

To determine what PowerShell versions we have installed on a system we use

```powershell
(Get-ItemProperty HKLM:\SOFTWARE\Microsoft\PowerShell\*\PowerShellEngine -Name PowerShellVersion).PowerShellVersion
```

Execution policy is **NOT** a security protection as it can be disabled with something as simple as

```powershell
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
```

or

```
powershell.exe -exec bypass
```

PowerShell files can come in different formats:

* `.ps1`: a single PowerShell script, completely self-contained and can be loaded into memory in one go
* `.psm1`: a PowerShell module file, this allows to do things like exporting only specific functions / variables and it's often used to give a better structure to more complex PowerShell code
* `.psd1`: a PowerShell module manifest that specifies information about the module itself and what variables / functions are exported
* `.ps1xml`: a object formatting file for a PowerShell module that allows for granular control of how custom objects are displayed

#### PowerShell Profile Locations

| Profile                           | Location                                                                                      |
| --------------------------------- | --------------------------------------------------------------------------------------------- |
| AllUsersAllHosts                  | `%windir%\System32\WindowsPowerShell\v1.0\profile.ps1`                                        |
| AllUsersAllHosts (WoW64)          | `%windir%\SysWOW64\WindowsPowerShell\v1.0\profile.ps1`                                        |
| AllUsersCurrentHost               | `%windir%\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1`                   |
| AllUsersCurrentHost (ISE)         | `%windir%\System32\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_profile.ps1`                |
| AllUsersCurrentHost (WoW64)       | `%windir%\SysWOW64\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1`                   |
| AllUsersCurrentHost (ISE - Wow64) | `%windir%\SysWOW64\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_profile.ps1`                |
| CurrentUserAllHosts               | `%homedrive%\%homepath%\[My ]Documents\WindowsPowerShell\profile.ps1`                         |
| CurrentUserCurrentHost            | `%homedrive%\%homepath%\[My ]Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1`    |
| CurrentUserCurrentHost (ISE)      | `%homedrive%\%homepath%\[My ]Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1` |

#### Exporting/Importing PowerShell Objects

To import / export PowerShell objects we use the `Import-Clixml` and `Export-Clixml` functions

```powershell
Get-Process | Export-Clixml processes.xml
$processes = Import-Clixml .\processes.xml
```

#### Variables & Operators

Variables are defined with `$` followed by any combination of numbers and (case-insensitive) letters but we can also use `New-Variable` to specify non-printable characters as well.

```powershell
New-Variable -Name ([Char] 7) -Value 'something'
```

To list all variables in the current scope we can use `Get-ChildItem Variable:\` and to cast a variable to another data type we use `[Type]$variableName`.

The following are common operators used with variables and their attributes

* Math: `+`, `-`, `*`, `/`, `%`
* Assignment: `=`, `+=`, `-=`, `*=`, `/=`, `%=`
* Comparison: `-eq`, `-ne`, `-gt`, `-lt`, `-le`, `-ge`, regex operations
* Logical: `-and`, `-or`, `-xor`, `-not` or `!`
* Redirection: `>`, `>>`, `2>`, `2>>`, `2>&1`
* Type: `-is`, `-isnot`, `-as`

It's possible to get more information about operators with `Get-Help about_Operators`.

#### Arrays & Hash Tables

There are many ways to create arrays

| Creation method   | Code                                    |
| ----------------- | --------------------------------------- |
| Implicit creation | `$array = 4,6,1,60,23,53`               |
| Explicit creation | `$array = @(4,6,”s”,60,”yes”,5.3)`      |
| Ranged creation   | `$array = 1..100`                       |
| Strongly typed    | `[int32[]]$array = 1500,1600,1700,1800` |

and there are several operations we can perform on an array object

* count the number of elements: `$array.Count`
* indexing: `$array[0]`
* reversing an array: `$array[-1..-$array.Lenght]`
* append a value to an array: `$array += $value`

Since arrays are immutable we have to perform extra steps to append or remove elements from one

```powershell
$arrayList = New-Object System.Collections.ArrayList
$arrayList.Add($value)
# or
$arrayList.Remove($value)
$arrayList.ToArray()
```

HashTables in PowerShell are the equivalent of dictionaries

```
$hashtable = @{ Number = 1; Color = "Purple"}
$hashtable["Number"] # prints 1
```

Common operations we can perform on hash tables are

* return the keys of the hash table: `$hashtable.keys`
* return the values of the hash table: `$hashtable.values`
* add a key or value

```powershell
$hashtable.Add("Key", "Value")
# or
$hashtable = $hashtable + @{Key="Value}
```

* remove a key: `$hashtable.Remove("Key")`

Hash tables can also be used as parameters for function calls

```powershell
$Args = @{ Path = "source.txt"; Destination = "destination.txt"; WhatIf = $true }
Copy-Item @Args
```

#### Strings

Double-quoted strings - both single and multi-line - expand sub-expressions and variables while single-quoted strings don't.

* Case-insensitive comparison (returns 0 if strings are the same): `$a.CompareTo($b)`
* Case-sensitive comparison: `[string]::Compare($a, $b, $True)`
* Case-sensitive prefix / suffix: `$a.StartsWith("prefix")`, `$a.EndsWith("suffix")`
* Turn string lowercase / uppercase: `$a.ToLower()`, `$a.ToUpper()`
* Check for substring (case-sensitive): `$a.Contains("substring")`
* Replace a string or substring: `$a.Replace("old string", "new string")`
* Substring: `$a.SubString(index)` returns `a[index..$a.Length-1]` or `$a.SubString(start, end)` returns `$a[start..end]`
* Split a string: `$a.Split("separator")` returns an array of the string split by the separator
* Pad a string: `$a.PadLeft(1)`, `$a.PadRight(1)` pads the string to the specified length
* Convert string to a byte array: `$a.ToByteArray()`

#### Regular Expressions

Strings can also be used to match regular expressions with operators like `-match`, `-notmatch` (case-insensitive) or `-cmatch`, `-cnotmatch` (case-sensitive).

{% hint style="info" %}
The `-match` operator will populate the `$Matches` variable with all the matches if used on a single-line variable.
{% endhint %}

To match and replace part of a string we use `-replace` and `-creplace`

```powershell
"date: 11/11/2011" -replace "\d{2}/\d{2}/\d{4}", (get-date -f "DD/MM/YYYY")
```

#### Logic & Loops

If / else / if-else statements

```powershell
if ($var -lt 0) {
	# something
}
elseif ($var -lt 10) {
	# something
}
else {
	# something
}
```

Switch statements

```powershell
$output = switch -wildcard ($var) {
	"var a" { "first output" }
	"var b" { "second output" }
	default { "default output" }
}
```

Try / catch / finally statements

```powershell
try {
	$client = new-object System.Net.WebClient
	$client.DownloadFile("http://domain.com/a")
}
catch [System.Net.WebException],[System.IO.IOException] {
	"Unable to download file"
}
catch {
	"Could not resolve error"
}
```

For-each statements

```powershell
ForEach($number in $numbers) {
	# do something
}
```

For-each-object statement

```powershell
Get-Process | % {$_.Name}
```

While / do-while statement

```powershell
while ($var -ne 100) {
	# do something
}
```

#### Filtering

When piping output from other commands it's possible to filter it

* Find an object with specific properties:

```powershell
Get-DomainUser | ? {$_.lastlogon -gt [DateTime]::TOday.AddDays(-1)}
```

* Execute a scriptblock on each object:

```powershell
Get-DomainUser -Domain domain.com | %{ if ($_.scriptpath) { $_.scriptpath.split("\\")[2] } }
```

* Find property comparisons

```powershell
$_ -eq number
$_ -Like *string*
$_ -match 'regex'
```

We can also sort objects by a set property with `Sort-Object property`, group objects with `GroupObject property` or select only some objects with

```powershell
Select-Object -Property firstProperty,secondProperty
select -expand property
select -First 1
select -Last 1
```
