Monthly Archive

Categories

PowerShell v6

PowerShell v6.2.2

PowerShell v6.2.2 has just been released.

 

One breaking change – the Enter-PSHostProcess is disabled when system is in lock down mode.

 

The jumplist is now created in STA to avoid potential CLR crash

 

Other changes are around the build process.

 

These changes shouldn’t have impact on your day to day scripts

 

PowerShell v6.1.5 has also been released with the same changes.

 

PowerShell v6.2.0 was released 28 March 2019 so you can expect the v6.1.x family to go out of support at the end of September 2019 assuming the lifecycle is followed. I recommend upgrading to v6.2.2 as soon as you can if you’re still using v6.1.x

Load format file in a module

Staying with the test-connection function I thought I’d show how to turn the function and format file into a module. This includes how to load the format file in a module.

 

Create a folder TestConnection

 

Copy the format file and the script file that contains the test-connection function into the folder.

 

Rename the script file to TestConnection.psm1

 

Create a module manifest file:

PS> New-ModuleManifest -Path TestConnection.psd1 –RootModule testconnection.psm1 -FormatsToProcess RSPing.Format.ps1xml -CompatiblePSEditions Core

The manifest file should look like this:

#
# Module manifest for module 'TestConnection'
#
# Generated by: Richard
#
# Generated on: 30/06/2019
#

@{

# Script module or binary module file associated with this manifest.
RootModule = 'testconnection.psm1'

# Version number of this module.
ModuleVersion = '0.0.1'

# Supported PSEditions
CompatiblePSEditions = 'Core'

# ID used to uniquely identify this module
GUID = 'e260cd41-8023-4a38-8dab-ac8ffbf20163'

# Author of this module
Author = 'Richard'

# Company or vendor of this module
CompanyName = 'Unknown'

# Copyright statement for this module
Copyright = '(c) Richard. All rights reserved.'

# Description of the functionality provided by this module
# Description = ''

# Minimum version of the PowerShell engine required by this module
# PowerShellVersion = ''

# Name of the PowerShell host required by this module
# PowerShellHostName = ''

# Minimum version of the PowerShell host required by this module
# PowerShellHostVersion = ''

# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
# DotNetFrameworkVersion = ''

# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
# CLRVersion = ''

# Processor architecture (None, X86, Amd64) required by this module
# ProcessorArchitecture = ''

# Modules that must be imported into the global environment prior to importing this module
# RequiredModules = @()

# Assemblies that must be loaded prior to importing this module
# RequiredAssemblies = @()

# Script files (.ps1) that are run in the caller's environment prior to importing this module.
# ScriptsToProcess = @()

# Type files (.ps1xml) to be loaded when importing this module
# TypesToProcess = @()

# Format files (.ps1xml) to be loaded when importing this module
FormatsToProcess = 'RSPing.Format.ps1xml'

# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
# NestedModules = @()

# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = '*'

# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = '*'

# Variables to export from this module
VariablesToExport = '*'

# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = '*'

 

To load the module into your PowerShell session:

PS> Import-Module -Name C:\Scripts\Modules\TestConnection\TestConnection.psd1

 

Substitute the path to your module.

Even simpler than updating the format data manually!

Test-Connection formatting

Last time I showed how to write a function that replaces the current PowerShell 6/7 Test-Connection. This time I’ll show you how to do the Test-Connection formatting.

Using just the function you get something like this:

PS> test-connection -computername 127.0.0.1

Success : True
Source : W10PROIP
Destination : 127.0.0.1
IPV4Address : 10.10.54.5
Bytes : 32
Time : 0

etc

 

All of the properties on the object are displayed and because there are more than four properties PowerShell defaults to a list display. What we want is to drop the Success property and display the remaining fields as a table – exactly like Windows PowerShell v5.1. I’ve deliberately dropped the IPv6 data because I don’t use IPV6 for anything.

Start by exporting the format data from Windows PowerShell

PS> Get-FormatData -TypeName 'System.Management.ManagementObject#root\cimv2\Win32_PingStatus' | Export-FormatData –Path C:\test\ping.xml

 

You have to use the full name for the type which you can find via

Test-Connection 127.0.0.1 | get-member

 

If you open the xml file you’ll see something like this:

<?xml version="1.0" encoding="utf-8"?>
<Configuration>
<ViewDefinitions><View>
<Name>System.Management.ManagementObject#root\cimv2\Win32_PingStatus</Name>
<ViewSelectedBy>
<TypeName>System.Management.ManagementObject#root\cimv2\Win32_PingStatus</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader><Label>Source</Label><Width>13</Width></TableColumnHeader>
<TableColumnHeader><Label>Destination</Label><Width>15</Width></TableColumnHeader>
<TableColumnHeader><Label>IPV4Address</Label><Width>16</Width></TableColumnHeader>
<TableColumnHeader><Label>IPV6Address</Label><Width>40</Width></TableColumnHeader>
<TableColumnHeader><Label>Bytes</Label><Width>8</Width></TableColumnHeader>
<TableColumnHeader><Label>Time(ms)</Label><Width>9</Width></TableColumnHeader>
</TableHeaders>
<TableRowEntries><TableRowEntry><TableColumnItems>
<TableColumnItem><PropertyName>__Server</PropertyName>
</TableColumnItem><TableColumnItem><PropertyName>Address</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>IPV4Address</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>IPV6Address</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>BufferSize</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>ResponseTime</PropertyName></TableColumnItem>
</TableColumnItems></TableRowEntry></TableRowEntries>
</TableControl>
</View></ViewDefinitions>
</Configuration>

 

Initially the XML will be all on one line. You’ll need to use a pretty printer of manually edit the XML to get into a more readable format. I’ve deliberately put a few entries on the same line where they should probably be separated on their own lines.

 

The test-connection function sets the object type to RSping – you can change that if you want.

 

The XML needs the type changing in the name and typename fields. You also need to change the tableColumnHeader and the TableColumnItem fields so they look like this:

<?xml version="1.0" encoding="utf-8"?>
<Configuration>
<ViewDefinitions>
<View>
<Name>RSPing</Name>
<ViewSelectedBy>
<TypeName>RSPing</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader><Label>Source</Label><Width>13</Width></TableColumnHeader>
<TableColumnHeader><Label>Destination</Label><Width>15</Width></TableColumnHeader>
<TableColumnHeader><Label>IPV4Address</Label><Width>16</Width></TableColumnHeader>
<TableColumnHeader><Label>Bytes</Label><Width>8</Width></TableColumnHeader>
<TableColumnHeader><Label>Time(ms)</Label><Width>9</Width></TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem><PropertyName>Source</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Destination</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>IPV4Address</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Bytes</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Time</PropertyName></TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>

 

Save the format data as RSPing.Format.ps1xml

 

To use the format data you need to load it

Update-FormatData -PrependPath .\RSPing.Format.ps1xml

 

By prepending the format data your format data will be used before any system formatting.

 

Now when you use test-connection you’ll get the formatting you want.

 

The new formatting data is session specific. It stays in the PowerShell session until you close it. If you want the formatting data in all sessions either create a module or dot source the test-connection function and load the formatting data in your profile.

PowerShell 6/7 Test-Connection

The PowerShell 6/7 Test-Connection implementation as of PowerShell v6.2.1 and PowerShell v7.0 preview 1 is in my opinion a horrible example of how not to create output. the cmdlet shows each ping and then wraps all of the results in the Replies property so you need to do something like this:

Test-Connection 127.0.0.1 | select -ExpandProperty Replies

 

And on top of all this there’s also a progress bar displayed!

What I really want is a version of test-connection in PowerShell 6/7 that works like test connection does in Windows PowerShell v5.1.

 

There’s a lot of discussion going round in circles about Test-Connection but in the mean time I decided to write my own. I’m only concerned with Windows systems so I can use a CIM class. If I wanted it to work cross-platform I’d need to use a based dot net solution.

function test-connection {
[CmdletBinding()]
param (
[string]$computername,
[int]$count=4,
[switch]$quiet
)

$filter = "Address='$computername'"

$pingresults = 1..$count | ForEach-Object {
$ping = Get-CimInstance -ClassName Win32_PingStatus -Filter $filter
$props = [ordered]@{
Success = if ($ping.StatusCode -eq 0) {$true}else {$false}
Source = $env:COMPUTERNAME
Destination = $ping.Address
IPV4Address = $ping.IPV4Address
Bytes = $ping.BufferSize
Time = $ping.ResponseTime
}

$result = New-Object -TypeName PSobject -Property $props
$result.PSTypeNames[0] = "RSPing"

$result
}

if ($quiet) {
foreach ($pingresult in $pingresults){
$pingresult.Success
}
}
else {
$pingresults
}
}

 

I’ve given the function the name test-connection. If you have a function and a cmdlet with the same name the function takes precedence.

 

The function has three parameters – computername that can take a computer name or an IP address; count = number of pings defaults to 4 and a quiet switch that will return a boolean to indicate success of failure.

 

The work is done by Win32_PingStatus CIM class with a filter that uses the computername for the Address property.

 

The relevant properties are collected into the output object whose type is set to RSPing.

 

If the quiet switch is selected the Success property for each object is displayed otherwise the whole object is output.

 

I don’t want the success property in the full display so I’m going to write a format file to manage the display which I’ll show you next time.

 

PowerShell doesn’t always supply the functionality in the way that you want it but there’s nothing to stop you creating your own version of the command.

Start Jobs with &

You usually start background jobs using Start-Job. Now you can start jobs with &. As of PowerShell v6.0 putting & at the end of a pipeline causes the pipeline to be run as a PowerShell job, for instance this pipeline:

PS> Get-Process | Sort-Object -Property CPU -Top 5

 

Is run as a background job by adding & to the end of the pipeline:

PS> Get-Process | Sort-Object -Property CPU -Top 5 &

 

When a pipeline is backgrounded, a job object is returned. Once the pipeline is running as a job, all of the standard job cmdlets can be used to manage the job. Variables (ignoring process-specific variables) used in the pipeline are automatically copied to the job so

Copy-Item -Path $path -Destination $dest &

 

just works without the need for the $using scope modifier or an argument list.

I recommend this technique for interactive working but that you use Start-Job in your scripts for more control (such as naming the job).

Install-Module and Update-Module Scope parameter

I’ve written a couple of times this year on the problems around the PowerShellGet module and the Scope (Allusers or CurrentUsers). The situation is a lot easier now that Install-Module and Update-Module Scope parameter is available.

Install-Module has always had a Scope parameter.

Update-Module has a Scope parameter as of PowerShell v6.2.1 and Powershell v7.0 preview 1.

 

I’ve changed mu profile to define the Scope parameter for both cmdlets as a default parameter:

$PSDefaultParameterValues = @{'Install-Module:Scope'='AllUsers'; 'Update-Module:Scope'='AllUsers'}

 

The rules for Install-Module default scopes seem to have changed with them now being:

When no Scope is defined, the default is set based on the PowerShellGet version.

In PowerShellGet versions 2.0.0 and above, the default is CurrentUser, which does not require elevation for install.

In PowerShellGet 1.x versions, the default is AllUsers, which requires elevation for install.

 

The documentation hasn’t caught up to Update-Module but past experience suggests it follows the same rules.

 

This is a good change as it gives back control of where the user puts the module, Too much software these days forces the developer’s assumptions on the user with no option to override. Its good to get the control back

Stable sort

In Windows PowerShell if you do something like this:

PS> (1..20 | Sort-Object -Property {$_ % 3}) -join ' '
9 6 12 15 3 18 19 16 13 10 4 1 7 20 17 2 8 11 5 14

 

The results come back in an unexpected order. This is not a stable sort as the results are sorted by their modulus result but lose the order within each group.

If you want a stable sort – where the results come back from a calculation like this in the order in they were received you need to add the –Stable switch. BUT that was added to PowerShell v6.1 and later

PS> (1..20 | Sort-Object -Property {$_ % 3} -Stable ) -join ' '
3 6 9 12 15 18 1 4 7 10 13 16 19 2 5 8 11 14 17 20

 

The results are sorted by modulus result and correctly within each group of modulus results

You could also use the Top or Bottom parameters:

PS> (1..20 | Sort-Object -Property {$_ % 3} -Top 20 ) -join ' '
3 6 9 12 15 18 1 4 7 10 13 16 19 2 5 8 11 14 17 20
PS> (1..20 | Sort-Object -Property {$_ % 3} -Bottom 20 ) -join ' '
3 6 9 12 15 18 1 4 7 10 13 16 19 2 5 8 11 14 17 20

 

If you alter the values presented to Top and Bottom you’ll get a subset of the results

PS> (1..20 | Sort-Object -Property {$_ % 3} -Top 10 ) -join ' '
3 6 9 12 15 18 1 4 7 10
PS> (1..20 | Sort-Object -Property {$_ % 3} -Bottom 10 ) -join ' '
13 16 19 2 5 8 11 14 17 20

 

Sort-Object in PowerShell Core has an interesting set of additions compared to Windows PowerShell.

OpenSSH installation

OpenSSH installation has got a lot simpler in Windows 10 1809; Windows Server 2019 and Windows Server 1809.

 

OpenSSH is available as an optional feature. The client is preinstalled when you install the operating system. You just need to install the server:

Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

 

Installing the optional feature creates the required firewall rule which is good.

 

You still need to make changes to the sshd_config file to enable password and pubkey authentication. The subsystem isn’t configured for PowerShell.

 

There’s a bug in OpenSSH so that subsystem paths with spaces aren’t parsed so you need to create a symbolic link for the PowerShell v6 folder.

 

if you want to use key-pair authentication the OpenSSHUtils module on the PowerShell gallery has bugs so you need to manually set the permissions on the authorized_keys file.

 

SSH remoting has a lot to offer but incorrect documentation, the work needed to install and configure it and buggy software will stop people using it. On the plus side OpenSSH is getting easier to install and configure but its still a long way from good.

PowerShell Core v6.2.1

PowerShell Core v6.2.1 has been released - https://github.com/PowerShell/PowerShell/releases

as has v6.1.4

 

The new versions are to primarily fix the Security Vulnerability CVE-2019-0733 - https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0733

 

v6.2.1 also enables tab completion for functions

What’s new in PowerShell v6.2

The What’s new in PowerShell v6.2 is available at - https://docs.microsoft.com/en-gb/powershell/scripting/whats-new/what-s-new-in-powershell-core-62?view=powershell-6 – together with the already existing documents for PowerShell v6.0 and v6.1.

 

Its worth reading through the three documents – one each for v6.0, v6.1 and v6.2 to see the whole range of changes in PowerShell core.