Skip to content

Latest commit

 

History

History
1142 lines (839 loc) · 19.9 KB

Powershell.md

File metadata and controls

1142 lines (839 loc) · 19.9 KB

Powershell

Console

Profile

Changing Tab Completion (shows and lets choose parameter):

Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete

Show help for Parameter

Set-PSReadlineOption -ShowToolTips

Deactivate beep on error:

Set-PSReadlineOption -BellStyle None

Bash style completion:

Set-PSReadlineOption -EditMode Emacs

Don't write certain phrases to history:

Set-PSReadLineOption -AddToHistoryHandler {
  param([string]$line)

  $sensitive = "password|asplaintext|token|key|secret|credential"
  return ($line -notmatch $sensitive)
}

predictive IntelliSense

Set-PSReadLineOption -PredictionSource History

vscode

Settings

use correct casing:

powershell.codeFormatting.useCorrectCasing
get-item
Get-Item

format on save:

"[powershell]": {
  "editor.formatOnSave": true
}

History

Search console history [CTRL]+[R]:

C:\>bck-i-search: <String>

'#' searcher:

C:\>#<optional String>+[TAB]

Variables

Set-Variable

Set dynamic Variablename

$ProcessName = 'explorer'
$PropertyName = 'Id'
Get-Process -Name $ProcessName |
  Select-Object -Property $PropertyName |
  Set-Variable ('{0}{1}' -f $ProcessName, $PropertyName)
$ExplorerId
  Id
  --
7936

OutVariable

$Null = Get-Service -OutVariable GetService |
  Sort-Object -Property Status -Descending -OutVariable GetServiceByStatus |
  Format-Table -AutoSize -OutVariable GetServiceByStatusAutosize

$GetService
$GetServiceByStatus
$GetServiceByStatusAutosize

Remoting

Enabling with WinRM:

([wmiclass]"\\$ComputerName\root\cimv2:win32_process").Create('powershell Enable-PSRemoting -Force')

Enabling with psexec:

psexec.exe \\computername -h -s powershell.exe Enable-PSRemoting -Force

AD

Get an orphaned computer instantly back on the domain or fixes its account:

Reset-ComputerMachinePassword -Server $Computername

Fixing domain join issues without a reboot:

Test-ComputerSecureChannel -Server $Computername -Repair

Networking

Test network connection (ping), result is bool:

Test-Connection -ComputerName $Computername -Count 3 -Quiet

Ping sweep

(((0..255).ForEach( { "192.168.0.$_" }) | ForEach-Object {
      (New-Object Net.NetworkInformation.Ping).SendPingAsync($_, 250)
    }).Result.Where{ $_.Status -eq 'Success' }).Address.IPAddressToString

Test network connection (TCP Port), result is bool (telnet alternative):

(Test-NetConnection -ComputerName $Computername -Port $TCPPort).TcpTestSucceeded

Create, start and stop TCP Listener:

$Listener = [System.Net.Sockets.TcpListener]$TCPPort
$Listener.Start()
$Listener.Stop()

Get IP network configuration (ipconfig -all alternative):

Get-NetIPConfiguration -All

Change the network category of a connection profile form public to private:

Get-NetConnectionProfile |
  Where-Object -Property NetworkCategory -eq 'Public' |
  Set-NetConnectionProfile -NetworkCategory Private

Perform a DNS name query resolution:

Resolve-DnsName -Name $Computername

Perform a DNS name query resolution (query type is mail routing):

Resolve-DnsName -Name $Computername -Type MX

Error Handling

foreach ($Item in $ItemColl)
{
  try
  {
    $Item = Get-Item -ErrorAction Stop
  }
  catch
  {
    $Item = 'Error'
  }
  finally
  {
    Write-Output $Item
  }
}

Exception Response

Invoke-RestMethod:

Write-Output "StatusCode:" $_.Exception.Response.StatusCode.value__
Write-Output "StatusDescription:" $_.Exception.Response.StatusDescription

Credential

Save credential

Get-Credential | Export-Clixml -Path ('{0}\[email protected]' -f ${env:\userprofile})

Load credential

$Credential = Import-Clixml -Path ('{0}\[email protected]' -f ${env:\userprofile})

SecureString

Create SecureString, write it to file

ConvertTo-SecureString –String 'F987sdh4dfd3DPiSM4DLGJ-wiWePUJkl77Mo' -AsPlainText -Force |
Export-Clixml -Path ('{0}\[email protected]' -f ${env:\userprofile})

Get String From SecureString

$SecureString = Import-Clixml -Path ('{0}\[email protected]' -f ${env:\userprofile})
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString)
[Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)

Parameter

Validation

Get Validators

[psobject].Assembly.GetType('System.Management.Automation.TypeAccelerators')::Get | Sort-Object -Property Value

IP Address

[ValidateScript({$_ -match [IPAddress]$_ })]

MAC Address

[ValidatePattern('(([0-9A-Fa-f]{2}[-:]){5}[0-9A-Fa-f]{2})|(([0-9A-Fa-f]{4}\.){2}[0-9A-Fa-f]{4})')]

Not Null or Empty

[ValidateNotNullOrEmpty()]

Path

[ValidateScript({
    if(-Not ($_ | Test-Path) )
    {
      throw "File or folder does not exist" 
    }
    if(-Not ($_ | Test-Path -PathType Leaf) )
    {
      throw "The Path argument must be a file. Folder paths are not allowed."
    }
    return $true
  })]
[System.IO.FileInfo]$Path

Accept Arrays

[string[]]
$MultipleItems = 'apple', 'orange', 'banana'

Default Parameter Values

$PSDefaultParameterValues = @{
  '*:Computername' = 'Computer01'
}

Pester

Check for Type [array]

,@(1, 4, 5) | Should -HaveType [Array]
Should -HaveType ([Array]) -ActualValue @(1, 4, 5)

Plaster

Create new Module

Invoke-Plaster -TemplatePath C:\Path\To\PlasterTemplate -DestinationPath  C:\Path\To\NewModule -Verbose

Modules

Find

Find-Module -Name remote

Find Path

(Get-Module -Name ((Get-Command Get-Process).ModuleName) | Select-Object -Property ).ModuleBase

Publish

Publish-Module -Repository PSRepository -Path C:\Path\To\Module

Get

Get-Module -Name Mappings -ListAvailable

Uninstall

Uninstall-Module -Name ModuleName -Force

Install

Install-Module -Name ModuleName -Repository RepositoryName -Scope AllUsers

Check for outdated Modules:

Get-InstalledModule |
  Select-Object Name, @{
  n = 'Installed'; e = {$_.Version}
}, @{n = 'Available'; e = {
    (Find-Module -Name $_.Name).Version}
} |
  Where-Object {$_.Available -gt $_.Installed}

Data Types

String

Concatenating

join

-join('aaa', 'bbb')

f operator

'{0}{1}' -f 'aaa', 'bbb'

extract a part of the string

substring

$FirstTwoChars = ('abcdefg').Substring(0, 2)

Left and right part from delimiter

$Name = 'aaaaaaaaaa;bbbbbbbbb'
$Pos = $Name.IndexOf(';')
$LeftPart = $Name.Substring(0, $Pos)
$RightPart = $Name.Substring($Pos + 1)

Title Casing

(Get-Culture).TextInfo.ToTitleCase("maX MusTerMann")

Here String

Create
$HereString = @'
     sadsada
   asdsadas
dasssd
 fsf  ff
'@
Join
$HereStringA = @"
asdasd
dgfg
    dfg
"@
$HereStringB = @'
asdfsdassdfdfd
        ds gfdg
  dfgd
'@
$HereStringA, $HereStringB -join "`n"

DateType

Parse

[datetime]::ParseExact("2020-04-16T14:10:23-05:00","yyyy-MM-ddTHH:mm:ss-05:00",$Null)

Array

Get collection even with only one item in array

@($OneItemColl)

Lists

Unlike eg. an array it's not fixed size and you can add and remove items from it to your hearts content with several types of Add and Remove methods. Also has methods for sorting and more.

Create list

New-Object 'System.Collections.Generic.List[System.Object]'
[System.Collections.Generic.List[System.Object]]::new()

Objects

Create object

Using New-Object and hashtables

$properties = @{
  firstname = 'Peter'
  lastname  = 'Miller'
}
New-Object psobject -Property $properties

Convert Hashtables to [PSCustomObject]

[pscustomobject]@{
  firstname = 'Peter'
  lastname  = 'Miller'
}

Write-Output vs Return

Return Write-Output
only $Variable all Outputs

'+=' Operator

Avoid, instead use

$Obj1 = foreach ($item in $items)
{
  $item
}

$Obj2 = 1..10000 | Foreach-Object { $_ }

$Obj3 = foreach($element in (1..10000)) { $element }

Use if first collect subroutine

$Obj = @()
foreach ($item in $items)
{
  $Obj += $item
}
Write-Output $Obj

Property

Add

 $Coll = Get-Coll |
     Add-Member -MemberType NoteProperty -Name $PropertyName -Value $PropertyValue -PassThru

Rename (Avoid Alias)

Get-ADComputer -Filter  | Select-Object -Property @{name = 'Computername'; expression = {$_.name}}

Order by multiple

Get-Service | Sort-Object -Property @{
  Expression = "Status"; Descending = $True
}, @{
    Expression = "DisplayName"; Descending = $False
  }

Calculated

Get-ChildItem -Path $Path -Filter '.txt' |
  Sort-Object -Property @{
      Expression = {$_.LastWriteTime - $_.CreationTime}; Ascending = $False
      } |
  Format-Table LastWriteTime, CreationTime

Methods

where method:

(Get-Service).where{$_.Status -eq 'running'}

foreach method:

(Get-Service).foreach{$_.DisplayName}

Hashtable

Ordered

$OrderedHashTable = [ordered]@{
  a = 1
  b = 2
  c = 3
  d = 4
}

Add items

$HashTable = @{}
Get-ChildItem -Path $env:windir -Filter .exe | ForEach-Object {

  $HashTable.Add($_.Name.ToString() , $_.Length.ToString())
}
$HashTable

Arrays of hashtables

$peopleArray = @(
  @{
    name = 'Kevin'
    age  = 37
    city = 'Irvine'
  }
  @{
    name = 'Alex'
    age  = 9
    city = 'Irvine'
  }
)
$peopleArray | ConvertTo-Json

PSBoundParameters

Proxy functions with PSBoundParameters:

function Get-ProxyWMIObject 
{
  [cmdletbinding()]
  param(
    $Class,
    $ComputerName
  )
  Get-WmiObject @PSBoundParameters
}

Get-ProxyWMIObject  -Class WIN32_BIOS

Help

Get-Help -ShowWindow -Name Get-Process
Get-Help -Name Get-Process -Detailed
Get-Help -Name Get-Process -Examples
(Get-Help -Name remote).where{$_.Category -eq 'Function'}

Useful Cmdlets

Group-Object

-AsHashtable

Turns a collection of data into a hashtable of that data that you can index into. This will add each row into a hashtable and use the specified property as the key to access it.

$Employee = @"
LastName,FirstName,Location
Schmidt,Hans,Leipzig
Schmidt,Thomas,Berlin
Mueller,Thomas,Leipzig
Mueller,Klaus,Berlin
Meier,Hans,Leipzig
"@ | ConvertFrom-Csv | Group-Object -AsHashtable -Property LastName
$Employee.Mueller
LastName FirstName Location
-------- --------- --------
Mueller  Thomas    Leipzig
Mueller  Klaus     Berlin

Show-Object

Provides a graphical interface to let you explore and navigate an object.

Get-Process | Show-Object

Useful Modules

PowerShellCookbook (Show-Object)

ImportExcel

quick replacement for Out-Gridview, Excel has to be installed

Get-Process | Export-Excel -Now -WarningAction SilentlyContinue

Statements

#Requires

#Requires -Version N[.n] #Requires -PSEdition [ Core | Desktop ] #Requires –PSSnapin PSSnapin-Name [-Version N[.n]] #Requires -Modules { Module-Name | Hashtable } #Requires –ShellId ShellId #Requires -RunAsAdministrator

CmdletBinding

advanced function

[CmdletBinding(DefaultParameterSetName = 'Set1', SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]

DefaultParameterSetName

SupportsShouldProcess

WhatIf, Confirm Support

if ($PSCmdlet.ShouldProcess('ID: {0}' -f $ID))
{
  Remove-Something -ID $ID
}

ConfirmImpact

Whenever the ConfirmImpact is equal or higher than the $ConfirmPreference (Default 'High'), a confirmation is required.

Formatting

Line break

Line too long

Get-WmiObject -Class 'Win32_LogicalDisk' -Filter 'DriveType=3' -Computername 'localhost'

Backticks

hard to read trailing spaces break code

Get-WmiObject -Class 'Win32_LogicalDisk' `
-Filter 'DriveType=3' `
-ComputerName 'localhost'

Splatting

$GetWmiObjectParams = @{
  Class        = 'Win32_LogicalDisk'
  Filter       = 'DriveType=3'
  Computername = 'localhost'
}
Get-WmiObject @GetWmiObjectParams

Other

line break after almost any comma, pipe character, or semicolon

Regular expressions, regex

Powershell: The many ways to use regex

Named matches (capture group name)

$Text = '192.168.0.1 computer01.domain.com 001122334455'
$Pattern = '^(?<IPv4>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Computername>(\w|\d|\.)+)\s+(?<MAC>\d{12})'
if ($Text -match $Pattern)
{
  $matches
}

Part of an OU

$Text = 'cn=JSmith,ou=Management,ou=users,ou=London,dc=company,dc=com'
$Pattern = '^.+ou=(?<Location>(\w|\d|\s)+)(?!.+ou){1}'
$Location = ([regex]::match($Text, $Pattern).captures.groups).Where{$_.Name -eq 'Location'}.Value

Filename and Path

$Text = 'C:\Temp\Subdir01\Test.txt'
$Pattern = '(?<Path>.+)\\(?<Filename>(?:.(?!\\))+$)'
$Filename = ([regex]::match($Text, $Pattern).captures.groups).Where{$_.Name -eq 'Filename'}.Value
$Path = ([regex]::match($Text, $Pattern).captures.groups).Where{$_.Name -eq 'Path'}.Value

create object

$TextColl = '192.168.0.1 computer01.domain.com 001122334455', '192.168.0.2 computer02.domain.com 001122334466'
$Pattern = '^(?<IPv4>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Computername>(\w|\d|\.)+)\s+(?<MAC>\d{12})'
$Result = foreach ($Text in $TextColl)
{
  if ($Text -match $Pattern)
  {
    New-Object -TypeName PsObject -Property $matches
  }
}
$Result

between quotation marks

$Pattern = '\"(?<BetweenQuotationMarks>.)\"'

abc until first space

$Pattern = '(?<ABCUntilFirstpace>abc[^\s])'

.net Regex

get methods

[regex]::new($pattern) | Get-Member

match string

[regex]::match($string,"^(\w{3})(\w{2,3})-.$")

Diverse

remove UTF8 BOM

[System.IO.File]::WriteAllLines($FilePath, (Get-Content -Path $FilePath))
(Get-Content -Path $FilePath) -replace '\xEF\xBB\xBF', '' |
  Set-Content -Path $FilePath

create csv w/o double quotes

Get-ChildItem | ConvertTo-Csv -NoTypeInformation |
  ForEach-Object { $_.Replace('"', '') } | Out-File output.csv

SQL NULL value

if ($Property.Value -is [System.DBNull])
{
  $Value = $Null
}

DateTime

ISO 8601:

(Get-Date).ToString('s')
2018-09-14T13:08:14

ISO 8601 + UTC offset:

(Get-Date).ToString('o')
2018-09-14T13:19:38.7241894+02:00

FileDate(Time):

Get-Date -Format FileDateTime
20200111T1721060231
Get-Date -Format FileDate
20200111

Disable Powershell V2

Disable-WindowsOptionalFeature -Online –FeatureName MicrosoftWindowsPowerShellV2Root –norestart

Select and show Command

Get-Command | Out-GridView -PassThru | Get-Help -ShowWindow
  Show-command $(Get-Command | Out-GridView -PassThru).Name

Trigger Windows Update

(New-Object -ComObject 'Microsoft.Update.AutoUpdate').DetectNow()

Generate Password

Generates an 8-char PW including 3 special characters

[system.web.security.membership]::GeneratePassword(8,3)
=^l.dN!h

current Path

$($executionContext.SessionState.Path.CurrentLocation)
Path
----
C:\GitHub

check for administrator

[System.Security.Principal.WindowsPrincipal]::new([System.Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole("Administrators")
false

escape wildcard pattern

$BeginWith = [WildcardPattern]::Escape('Mich')
'Michael', 'Michelle', 'Jonathan' -like "$BeginWith"
Michael
Michelle

progress bar

$itemColl = Get-ChildItem -Path ('{0}' -f [IO.Path]::GetTempPath()) -File
$step = 10
$i = 0
foreach ($item in $itemColl)
{
  If ($i%$step -eq 0)
  {
    Write-Host ('[{0}/{1}]' -f $i, ($itemColl).Count)
    $item | Do-Something
  }
  $i++
}
[10/96]
[20/96]
[30/96]
[40/96]
[50/96]
[60/96]
[70/96]
[80/96]
[90/96]

get default values of parameters in function

(Get-Command Get-UMSDevice).ScriptBlock.Ast.Body.paramblock.Parameters |
  Select-Object -Property Name, DefaultValue
Name              DefaultValue
----              ------------
$Computername
$TCPPort          8443
$ApiVersion       3
$SecurityProtocol 'Tls12'
$WebSession
$Filter           'short'
$Id

enter credentials via command line

as opposed to GUI

Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds" ConsolePrompting True

encode-urls

https://acme.com/Shared Documents/

[System.Web.HttpUtility]::UrlEncode('https://acme.com/Shared Documents/')
https%3a%2f%2facme.com%2fShared+Documents%2f

decode-urls

[System.Web.HttpUtility]::UrlDecode('https%3a%2f%2facme.com%2fShared+Documents%2f')
https://acme.com/Shared Documents/