The joys of software – PowerShell remoting edition

I was happily scripting in PowerShell and the script appeared to work. It remoted to a server, got that state of a few services, if they were running, how much memory they used, etc.

It used – very simplified

$Session = New-PSSession -ComputerName $server
Invoke-Command -Session $Session -ScriptBlock {#Remote code}

So after testing on a single server, I let it lose on all the production servers.  It worked for about 40% of them – the rest did not give a hoot about the script content.

Why?  No idea at the time.  So – what was the differences between the servers? They all had PowerShell 7.x installed, but some where 2016+ while others where 2012R2.  

Changing the remote script to simply retrieve the most current PS version config that the WinRM would provide. 

function Get-PSConfigs { 
    param (
        [String] $server
    )
    Write-Host "--- $server --------------------------------------------------------------"
    $Session = New-PSSession -ComputerName $server
    if ($Session) {
        try {
            Invoke-Command -Session $Session -ArgumentList $server -ScriptBlock {
                Param ([string]$LocName)
                Get-PSSessionConfiguration | sort PSVersion -Descending | Select-Object -first 1
            }
        }
        finally {
            Remove-PSSession -Id $Session.Id
        }
    }
}

This revealed that all the servers that failed only offered PS version 4, and of those that worked, they only reported PS version 5.1.  Why!?  PS 7 was installed! Why didn’t the script run on PS 7?

So, after RTFM a lot, and experimenting a little, it turned out that to enable a configuration for remoting to PS 7, you have to start PWSH 7 with Administrator rights and run “Enable-PSRemoting”.

Only then will you have a PowerShell.7 configuration that you can use with New-PSSession.

function Get-PSSevenResults { 
    param (
        [string] $server
    )
    $Session = New-PSSession -ComputerName $server -ConfigurationName PowerShell.7 
    if ($Session) {
        try {
            Invoke-Command -Session $Session -ArgumentList $server -ScriptBlock {
                Param ([string]$ServerName)
                # remote executed code here
            }
        }
        finally {
            Remove-PSSession -Id $Session.Id
        }
    } 
}

Various error checking/handling removed for clarity

Fun fact – RDPing to 80+ servers, finding pwsh 7, starting it as admin, and running Enable-PSRemoting, is not really much fun at all.

PowerShell snippet: Get-Choice

You know when you want the user to make a choice, and you want him to type it out and get it right?

class Choice {    
    [string] $Key;
    [string] $Option;
    Choice($Key, $Option) {
        $this.Key = $Key
        $this.Option = $Option
    }
}
<# 
 .Synopsis
  Read userinput and validate against choices
 .Description
  Read userinput and validate against choices. Must match one of the keys. Not case sensitive.
 .Parameter Prompt
  Text to show when waiting for input
 .Parameter Choices
  List of Choice elements
 .Parameter NoHints
  Don't show list of hints for Choices
 .Example
  $MyChoices = @(
      [Choice]::new('yes', 'Let me continue'), 
      [Choice]::new('no', 'I want to stop')
  )
  $key = Get-Choice -Prompt "Do you want to continue?" -Choices $MyChoices
#>
function Get-Choice {
    #    [OutputType([string])]
    param (
        [string]$Prompt,
        [Choice[]]$Choices,
        [switch]$NoHints
    )
    $Hints = ""
    $Comma = ""
    $Opts = ""
    $Sep = ""
    $Choices | ForEach-Object {
        $Hints = $Hints + $Comma + '[' + $_.Key + '] ' + $_.Option
        $Comma = ', '
        $Opts = $Opts + $Sep + $_.Key 
        $Sep = '|'
    }
    if (-not $NoHints) {
        Write-Host $Hints
    }
    $pattern = $Opts.ToLower()
    $pattern = "¤¤$pattern¤¤" -replace "\|", "¤¤|¤¤"
    $ok = $false
    while (-not $ok) {
        [string]$userinput = (Read-Host -Prompt "$Prompt ($Opts)").ToLower()
        $userinput = ("¤¤$userinput¤¤" | Select-String -Pattern $pattern) -replace "¤¤" , ""
        $ok = ("$userinput" -ne "")
    }
    return $userinput
} # Get-Choice

PowerShell snippet: Show-TextFile

You know those times that you need to show a log file or some text file?

<# 
 .Synopsis
  Open NotePad with the specified filename
 .Description
  Open NotePad with the specified filename
 .Parameter FileName
  Name of text file to open
 .Parameter ScrollToEnd
  Scroll to the bottom of the opened file
 .Parameter Wait
  Wait for Notepad to close before continuing script
 .Parameter Kill 
  Kill Notepad after Timeout -Requires Wait
 .Parameter TimeOut 
  Timeout period in seconds - default = 300 - Requires Wait
 .Example
  Show-TextFile -FileName 'C:\Some path\folder\filename.txt' -ScrollToEnd -Wait -Kill -TimeOut 30 
#>
function Show-TextFile {
    param (
        [string]$FileName,
        [switch]$ScrollToEnd,
        [switch]$Wait,
        [switch]$Kill,
        [int]$TimeOut = 300
    )
    if (Test-Path -Path $FileName) {
        $proc = Start-Process -FilePath 'NotePad.exe' -ArgumentList "$FileName" -NoNewWindow -PassThru
        if ($ScrollToEnd) { 
            # Use VBScript to send Ctrl-End keystroke to Notepad
            $wshell = New-Object -ComObject wscript.shell
            $title = Split-Path -path $FileName -Leaf
            Start-Sleep -seconds 0.5
            $wshell.AppActivate($title) | Out-Null
            $wshell.SendKeys('^{END}') | Out-Null
        }
        if ($Wait) {
            Write-Host "To continue, close Notepad for '$FileName'"
            if (-not (Wait-Process -InputObject $proc -Timeout $TimeOut -ErrorAction SilentlyContinue)) {
                if (-not $Proc.HasExited) {
                    if ($Kill) {
                        Write-Host "Closed Notepad for $FileName"
                        Stop-Process -InputObject $proc -Force
                    } }
            }
        }
    }
    else {
        Write-Warning "Show-TextFile -FileName not found: $FileName"
    }
} # Show-TextFile

Did you know that you can enable VT100 terminal emulation in PowerShell as well as the Cmd window?

Did you know that you can enable VT100 terminal emulation in PowerShell as well as the Cmd window?

This will allow you to do adb shell to your Android device, and allows you to run “full screen” vi editing on any text file on the device.

https://stackoverflow.com/questions/51680709/colored-text-output-in-powershell-console-using-ansi-vt100-codes

PowerShell Core 6.0 is a new edition of PowerShell that is cross-platform (Windows, macOS, and Linux), open-source,…

PowerShell Core 6.0 is a new edition of PowerShell that is cross-platform (Windows, macOS, and Linux), open-source, and built for heterogeneous environments and the hybrid cloud.

https://blogs.msdn.microsoft.com/powershell/2018/01/10/powershell-core-6-0-generally-available-ga-and-supported/

PowerShell Core is officially supported on the following platforms:

Windows 7, 8.1, and 10
Windows Server 2008 R2, 2012 R2, 2016
Windows Server Semi-Annual Channel
Ubuntu 14.04, 16.04, and 17.04
Debian 8.7+, and 9
CentOS 7
Red Hat Enterprise Linux 7
OpenSUSE 42.2
Fedora 25, 26
macOS 10.12+

Contributed, but not officially supported:

Arch Linux
Kali Linux
AppImage (works on multiple Linux platforms)

Experimental (unsupported) releases:

Windows on ARM32/ARM64
Raspbian (Stretch)

https://blogs.msdn.microsoft.com/powershell/2018/01/10/powershell-core-6-0-generally-available-ga-and-supported/