wiki.getshifting.com

--- Sjoerd Hooft's InFormation Technology ---

User Tools

Site Tools


cheatsheet-powershell

Cheatsheet PowerShell

Summary: Powershell hints, tips, oneliners and best practices.
Date: 8 December 2024

PowerShell Help

Get-Help with -Parameter is a quick way to examine the data type a given parameter expects:
PS> Get-Help Get-ChildItem -Parameter Path
 
-Path <String[]>
    Specifies a path to one or more locations. Wildcards are permitted. The default location is the current directory (`.`).
 
    Required?                    false
    Position?                    0
    Default value                Current directory
    Accept pipeline input?       True (ByPropertyName, ByValue)
    Accept wildcard characters?  false

PowerShell History

View history of powershell commands and invoke them using “r #”
Get-History
<# Output #>
r 1


See all saved commands in vscode
code (Get-PSReadLineOption).HistorySavePath

PowerShell If

If statement
if (($alllogs -eq "True") -AND ($result)){
    Write-Host "Both statements are true"
}elseif (($buildreason -eq "IndividualCI") -or ($buildreason -eq "BatchedCI") -or ($buildreason -eq "PullRequest")){
    Write-Host "At least one of the statements is true"
}else {
    Write-Host "None of the statements is true"
}

PowerShell Count

Count number of strings / objects
# string
$een = "een"
$een.Count
1
 
# Array
$twee = @("een","twee")
$twee.count
2


Length
# string
$een = "een"
$een.Length
3
 
# Array
$twee = @("een","twee")
$twee.Length
2
$twee[0].Length
3


Measure-Object - Sometimes counting when there is only one item can fail, use measure-object instead.
$een = "een"
($een | Measure-Object).Count
1
$twee = @("een","twee")
($twee | Measure-Object).Count
2

PowerShell Dates

# Dates
$timestamp = Get-Date -format "yyyyMMdd-HH.mm" # 20211011-14.27
$readdate = Get-Date -format "d MMM yyyy" # 11 Oct 2021
$weekdate = Get-Date -uformat %V # Week number # 41
$monthdate = Get-Date -format "MMMM-yyyy" # May-2021


add or subtract days
[datetime]$endDate = (Get-Date).AddDays(+90)
[datetime]$endDate = (Get-Date).AddDays(-90)

Powershell Error Handling

Error handling explained


Try Catch Error Finally with LineNumber for troubleshooting

  1. function Get-ScriptLineNumber {
  2. return $MyInvocation.ScriptLineNumber }
  3. try{
  4. Write-Host "Do something that you want to catch the error of"
  5. }catch{
  6. # Catch the error
  7. Write-Host "Full Error: $_"
  8. # Or error out after all
  9. Throw "Failed on: $actionname; Status: Failed; Line: $(Get-ScriptLineNumber); Error: $($_.Exception.Message)"
  10. }finally {
  11. <#Do this after the try block regardless of whether an exception occurred or not#>
  12. # For example, clean up remote ps sessions
  13. }


Exception.options
try{
    # Do something that you want to catch the error of
    Get-ChildItem \\prox-hyperv -ErrorAction Stop
 
    # [System.Net.DNS]::GetHostByName("server99")
 
    # Connect-AzAccount
    # Get-AzResourceGroup -Name "exampleGroup" -ErrorAction Stop
}catch [System.Management.Automation.ItemNotFoundException] {
    Write-Host "Cannot find item."
    Write-Host -NoNewLine "Exception Message        : "
    Write-Host -Foreground Red -Background Black "$($_.Exception.Message)"
}catch{
    # Catch the error
    Write-Host -Foreground Red -Background Black "Error found. Please try again, or if the error persists, contact support for evaluation. Please provide the following diagnostics:"
    Write-Host -NoNewLine "Exception Message        : "
    Write-Host -Foreground Red -Background Black "$($_.Exception.Message)"
 
    # Exceptions can contain inner exceptions. This is often the case when the code you are calling catches an exception and throws a different exception. They will place the original exception inside the new exception.
    Write-Host -NoNewLine "Inner Exception Message  : "
    Write-Host -Foreground Red -Background Black "$($_.Exception.InnerException.Message)"
 
    # The FullyQualifiedErrorId is the .Message property of the exception object along with the the fully-qualified name of the class where the exception originated.
    Write-Host -NoNewLine "Fully Qualified Error Id : "
    Write-Host -Foreground Red -Background Black "$($_.FullyQualifiedErrorId)"
 
    # Output provides the error type you need to catch this specific error type
    Write-Host -NoNewLine "Exception type           : "
    Write-Host -Foreground Red -Background Black "$($_.Exception.GetType().fullname)"
 
    # Shows the  powershell command. Does not work for .Net exceptions
    Write-Host -NoNewLine "Command                  : "
    Write-Host -Foreground Red -Background Black "$($_.InvocationInfo.MyCommand)"
 
    # Position of the error in the scriptblock or file
    Write-Host -NoNewLine "Position                 : "
    Write-Host -Foreground Red -Background Black "At line: $($_.InvocationInfo.ScriptLineNumber) char:$($_.InvocationInfo.OffsetInLine)"
 
    # Shows the faulty line. Also works for .Net exceptions
    Write-Host -NoNewLine "Full line                : "
    Write-Host -Foreground Red -Background Black "$($_.InvocationInfo.Line)"
 
    # Shows the full error. Useful for troubleshooting. Provides output so use careful when returning output.
    Write-Host "Full Error:"
    Get-Error
 
    # Shows the full error for AZ cmdlets. Useful for troubleshooting. Provides output so use careful when returning output. Usually also works with non AZ cmdlets.
    Write-Host "Full Azure Error:"
    Resolve-AzError -Last
}finally {
    #Do this after the try block regardless of whether an exception occurred or not. For example, clean up remote ps sessions, clear variables or cleanup files.
}

PowerShell Input and Output

Enable logging of script
  1. ### Script Variables ###
  2. $scriptname = [System.IO.Path]::GetFilenameWithoutExtension($MyInvocation.MyCommand.Path.ToString())
  3. $scriptlocation = Split-Path $myinvocation.mycommand.path
  4. $date = (Get-Date).ToString('yyyyMMdd-HHmm')
  5.  
  6. ### Start transcript for full logging capabilities ###
  7. start-transcript -path "$scriptlocation\$scriptname-$date-logtranscript.txt"
  8.  
  9. ### Stop transcript
  10. Stop-transcript


Script output and Write-Host options
  1. # Inside a function use return or write-output
  2. return "value"
  3. Write-Output $value
  4. # Output an object without adding it to the function output
  5. Write-Host "color" -ForegroundColor Red -BackgroundColor green -noNewLine
  6. $object | Out-Host


Input password
$password = Read-Host "Enter password" -asSecureString


Use padright to fill a line with one symbol
Write-Host ''.PadRight(78, '=')
# Or with a variable
$wide = 32
Write-Host ''.PadRight($wide, '=')


Preference Variables

About Preference Variables

Verbose Preference
# Default
$VerbosePreference = 'SilentlyContinue'
# Override: -Verbose
 
# Verbose output
$VerbosePreference = 'Continue'
# Override: -Verbose:$false


Debug Preference
# Default
$DebugPreference = 'SilentlyContinue'
# Debug output
$DebugPreference = 'Continue'


Whatif Preference
# Default
$WhatIfPreference = $false
# Override: -WhatIf
 
# When WhatIf is enabled, the cmdlet reports the expected effect of the command, but doesn't execute the command.
$WhatIfPreference = $true
# Override: -WhatIf:$false

PowerShell Variables

Set system (environment) variable
$env:var="value"


Get all system (environment) variables, sorted by name
get-childitem -path env:* | Sort Name


Variables in function
Declare a variable with script scope to use it in a function
$script:orgUrl = "https://dev.azure.com/getshiftingcom"
$script:apiversion = "api-version=6.0"
function SetBuildTag {
  # Load Azure DevOps API settings
  AzureDevOpsAPISettings
  foreach ($tag in $tags){
      $tagurl = "$orgUrl/$env:System_Teamproject/_apis/build/builds/$env:BUILD_BUILDID/tags/$($tag)?$apiversion"
  }
}


set variable with a dash/hyphen
New-Variable -Name "MODULE-SiteName" -Value "Incomingapi"


set system variable with a dash/hyphen
[Environment]::SetEnvironmentVariable('MODULE-SiteName','Incomingapi')


Set system variables with a dash/hyphen
${env:test-app_user} = "svc-test-dev"
$user = ${env:test-app_user}

Remote PowerShell

Set variables
  1. $AdminCredentials = Get-Credential
  2. $remotePort = 5986
  3. $pso = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
  4. $Culture = "en-US"
  5. $pso.Culture = $Culture
  6. $pso.UICulture = $Culture
  7. $sessionParams = @{}
  8. $sessionParams.Add('UseSSL', $true)
  9. $sessionParams.Add('Port', $remotePort)
  10. $sessionParams.Add('Credential', $AdminCredentials)


Start session to restart computer
$session = New-PSSession -ComputerName webserverdmz.domain -EnableNetworkAccess -SessionOption $pso @sessionParams
Invoke-Command -Session $session -ScriptBlock {Restart-Computer}
Remove-PSSession $session

PowerShell Function Template

See here for guidelines.
function Use-AzureDevOpsApi{ #Use Get-Verb for a list of approved verbs
    <#
    .SYNOPSIS
        Short description
 
    .DESCRIPTION
        Long description
 
    .PARAMETER Pat
        Explain the parameter Pat. Repeat for additional parameters.
 
    .OUTPUTS
        Explain the powershell output, if any
 
    .EXAMPLE
        Pipeline example:
        - task: PowerShell@2
          displayName: "Use Default SystemAccessToken"
          env:
            SYSTEM_ACCESSTOKEN: $(System.AccessToken)
          inputs:
            pwsh: true
            targetType: 'inline'
            script: |
                # Load Azure DevOps API settings
                Use-AzureDevOpsApi
 
        Script example:
        Use-AzureDevOpsApi -Pat $Pat
 
    .LINK
        System access token: https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#systemaccesstoken
 
    .NOTES
        Author : Sjoerd Hooft
        Version: 2022-08-03
 
    #>
    [CmdletBinding()]
    param (
        [Parameter()]
        [ValidateLength(52,52)] # See for more parameter validation https://poshcode.gitbook.io/powershell-practice-and-style/style-guide/function-structure
        [ValidateSet("dev", "tst", "acc")]
        [string]
        $Pat
    )
 
    begin {
        Write-Host "##[section]Function: $($MyInvocation.MyCommand)"
        Write-Verbose "Input: $($PSBoundParameters | Out-String)"
 
        # Set Verbose switch based on PSBoundParameters
        $CmdLetOutput = @{Verbose = $($PSBoundParameters.Verbose)}
        # Collect information
    }
 
    process {
        # Do stuff
    }
 
    end {
        # Cleanup
    }
}

PowerShell Function Validates

  • Not null or empty: [ValidateNotNullOrEmpty()]
  • Latest or YYYY-MM: [ValidatePattern("latest|20\d{2}[-]\d{2}")]
  • Email: [ValidatePattern('(.+@getshifting\.com)$')]
  • Specific server name: [ValidatePattern("web(dev|tst|acc)dmz0[12]")]
  • Environment: [ValidateSet("dev", "tst", "acc", "prd")]
  • Number between: [ValidateRange(8,100)]
  • Length range: [ValidateLength(8,12)]
  • Length exactly: [ValidateLength(8,8)]

PowerShell Modules

Check Azure powershell module depending on PS version
  1. Write-Host "Check required PowerShell Modules `n"
  2. # The "new" PS module for Azure requires PowerShell 7
  3. if ($($PSVersionTable.PSVersion).Major -eq 7){
  4. if (Get-Module -ListAvailable -Name AZ) {
  5. Write-Host "PowerShell module Azure exists"
  6. } else {
  7. Write-Host "PowerShell module Azure does not exist. Start installation. "
  8. Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force
  9. }
  10. }else{
  11. Throw "Powershell needs to be version 7"
  12. }


PowerShell DNS Module
if (Get-Module -ListAvailable -Name DnsServer) {
    Write-Host "PowerShell module DnsServer exists"
} else {
    Write-Host "Module DNSServer does not exist. Starting installation"
    Import-Module ServerManager
    Add-WindowsFeature -Name "RSAT-DNS-Server"
}


PowerShell AD Module
# Because ActiveDirectory is a 5.1 module you need to add -All
if (Get-Module -ListAvailable -All -Name ActiveDirectory) {
    Write-Host "PowerShell module ActiveDirectory exists"
} else {
    Write-Host "Module ActiveDirectory does not exist. Starting installation"
    Import-Module ServerManager
    Add-WindowsFeature -Name "RSAT-AD-PowerShell" –IncludeAllSubFeature
}


PowerShell Pester module, remove shipped version and install latest version (Needs PS 7)
  1. if (Get-Module -ListAvailable -Name Pester) {
  2. # Check for version 3.4 and if available remove it
  3. Get-Module -ListAvailable -Name Pester
  4. if (((((Get-Module -ListAvailable -Name Pester).Version).Major) -eq 3) -AND ((((Get-Module -ListAvailable -Name Pester).Version).Minor) -eq 4)) {
  5. Write-Host "PowerShell Module Pester is version 3.4 which is shipped along with Win10/Windows 2016. Start removal:"
  6. $module = "C:\Program Files\WindowsPowerShell\Modules\Pester"
  7. takeown /F $module /A /R
  8. icacls $module /reset
  9. icacls $module /grant "*S-1-5-32-544:F" /inheritance:d /T
  10. Remove-Item -Path $module -Recurse -Force -Confirm:$false
  11. }
  12. # Verifieer of Pester al aanwezig met minimaal versie 5
  13. if ((((Get-Module -ListAvailable -Name Pester).Version).Major) -ne 5) {
  14. Write-Host "PowerShell module Pester is not up to date"
  15. Install-Module -Name Pester -Force -Scope CurrentUser
  16. }else{
  17. Write-Host "PowerShell Module Pester is available with minimal version of 5:"
  18. Get-Module -ListAvailable -Name Pester
  19. }
  20. } else {
  21. Write-Host "PowerShell module Pester does not exist. Start installation. "
  22. Install-Module -Name Pester -Force -Scope CurrentUser
  23. }


IIS Administration Module, install extra version, see the version and the available commandlets. Note that this needs to be done in PowerShell 5 Administrative Shell
Install-Module -Name IISAdministration -Scope AllUsers -Force #Installs latest version next to it (1.1.0.0)
Import-Module IISAdministration -RequiredVersion 1.1.0.0
$mod = Get-Module -Name IISAdministration
$mod.Version
$mod.ExportedCmdlets | Format-Table


Import PS Module
Import-Module Pester


Get commands within a module
Get-Command -module Pester


Get module for a command
Get-Command Get-Service


Remove / Unload a PowerShell module
Remove-Module Pester

PowerShell Requires

#Requires -Version 7.0
#Requires -PSEdition Core
# Requires for modules is slow because it also imports them
# Requires does not work for non-PS Core modules like DNS and ActiveDirectory
#Requires -Modules @{ ModuleName="Pester"; ModuleVersion="5.0" }
#Requires -Modules Az
#Requires -Modules AzureRmStorageTable

PowerShell ForEach

With custom PS Object and export to csv
  1. $myCol = @()
  2. foreach ($vm in (get-vmhost esxprd101.intranet | get-vm )){
  3. $VMInfo = "" | Select-Object VMName,VMHostName,NICCount
  4. $VMInfo.VMName = $vmview.Name
  5. $VMInfo.VMHostName = $vmview.Guest.HostName
  6. $VMInfo.NICCount = $vmview.Guest.Net.Count
  7. $myCol += $VMInfo
  8. }
  9. $myCol |Export-csv -NoTypeInformation $csvfile

PowerShell Comparison

Overview
# logical and comparison
# -and, -or, -not , ! : Connect expressions
# -eq, -ne : Equal, not equal
# -gt / -lt, -ge / -le : Greater/less than, greater or equal / less or equal
# -replace
# -match / -notmatch : Regular expression match
# -like / -notlike : wilcard matching
# -contains / -notcontains : check for value in array: $array -contains $value
# -in / -notin : reverse syntax from contains: $value in $array

PowerShell Location

Change directory (alias cd)
Set-Location c:\


Change directory and back
PS C:\Users\sjoer> Push-Location c:\
PS C:\> Pop-Location
PS C:\Users\sjoer>

Azure PowerShell

NOTE: Module AZ only works in PowerShell 7


TLS version
# Set TLS version to use TLS 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

Login to Azure

Login and context
Connect-AzAccount
Get-AzContext -ListAvailable
Set-AzContext -Name $dtapEnvironment -Subscription $azureSubscriptionName


Get TenantId
$tenantId = (Get-AzContext).Tenant.Id

KeyVault

Get secret from KeyVault
$adadminpass = (Get-AzKeyVaultSecret -VaultName kv-operations-global -Name "adAdminPass").SecretValue


Set secret to KeyVault
$userpasswordsecure = ConvertTo-SecureString -String $userpassword -AsPlainText -Force
Set-AzKeyVaultSecret -VaultName kv-operations-global -Name $username -SecretValue $userpasswordsecure

Resourcegroup Creation Time

((Invoke-AzRestMethod -Path "/subscriptions/$subId/resourcegroups?api-version=2020-06-01&`$expand=createdTime" -Method GET).Content | ConvertFrom-Json).value | Select-Object name,createdTime
cheatsheet-powershell.txt · Last modified: by 127.0.0.1