Monthly Archive

Categories

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.

Quotes in CSVs

Up to and including PowerShell v6.2.x converting or exporting data to a csv has automatically put quotes round each field. In PowerShell v7 you can control quotes in CSVs

Using

Get-Service | ConvertTo-Csv

as an example.

 

The current behaviour is to put quotes round everything

"XboxNetApiSvc","System.ServiceProcess.ServiceController[]","False","False","False","Xbox Live Networking Service","Syst
em.ServiceProcess.ServiceController[]",".","XboxNetApiSvc","System.ServiceProcess.ServiceController[]",,"Stopped","Win32
ShareProcess","Manual",,

 

This is still the default behaviour in PowerShell v7 but there’s now a –usequotes parameter to control the quotes. Its default value is always to match previous behaviour.

 

Other options are AsNeeded and Never

AsNeeded consistently threw an error but Never seems to work

CDXML schema checks

PowerShell v3 introduced CDXML modules. Cmdlet Definition XML wraps a CIM class in XML to create a module. The methods of the CIM class can be used to generate additional cmdlets for the module. Many of the modules introduced with Windows 8 – including the networking and storage modules – are created in this manner though producing modules in this manner hasn’t caught on with this concept. The PowerShell 6.x releases didn’t check the XML of the CDXML modules against the CDXML schema as the required APIs weren’t available in .NET core 2.0. The APIs are in .NET Core 3.0 and the CDXML schema checks are built into PowerShell 7.

PowerShell Line continuation

There are a number of ways to signal PowerShell line continuation in your code. probably the most common is to have a pipeline symbol at the end of the line:

Get-Process |
Sort-Object -Property CPU -Descending -Top 5

 

because there’s nothing after the pipe symbol PowerShell assumes that the next line of code is actually a continuation and treats the 2 lines of code as a single pipeline. This is especially good if you have a very long pipeline with incorporating numerous cmdlets.

 

Since the days of PowerShell v1 many people have asked to be able to the pipe symbol at the beginning of the continuation line rather than at the end of the first line. The only way you could do that was to use the backtick ` symbol to signify line continuation:

Get-Process `
| Sort-Object -Property CPU -Descending -Top 5

 

This is an ugly approach as the back tick is a very small character its often overlooked when reading or working with the code which can lead to bugs.

 

PowerShell 7 allows you to use the pipe symbol at the start of a line as a continuation character without needing the backtick:

Get-Process
| Sort-Object -Property CPU -Descending -Top 5

 

Over the years many people, especially those new to PowerShell, have complained that there are sometimes too many different ways in PowerShell to achieve the result you want. To my mind this change of one of those unnecessary changes that just add complexity (of choice) for no real benefit. PowerShell is an open source project and if someone wants to add something like this then the opportunity is there. I’ll be sticking to the pipe symbol at the endo of the line approach as I don’t see any advantage to changing and I find the original approach easier to understand.

Test DNS servers

Had a recent comment about testing DNS servers which got me thinking how do I test DNS servers.

I decided I wanted to get the DNS server address from the client, ping the DNS server to test network connectivity and test if DNS was up and working. The current result is this:

function test-dnsserver {
[CmdletBinding()]

## get-dns server address
$addresses = Get-DnsClientServerAddress -AddressFamily IPv4 -InterfaceAlias LAN |
Select-Object -ExpandProperty ServerAddresses

## test servers
foreach ($address in $addresses){
$resolved = $false
$ping = Test-Connection -TargetName $address -Quiet -Count 1
$server = Resolve-DnsName -Name $address -Server $address

if ($server.NameHost) {
$serverName = $server.NameHost
$resolved = $true
}
else {
$serverName = 'DNS unreachable'
}

$props = [ordered]@{
DNSserverName = $serverName
DNSserverIP = $address
Pingable = $ping
Resolved = $resolved
}

New-Object -TypeName PSObject -Property $props
}
}

 

I always set my network interface alias to LAN so you may want to change that or even make it a parameter.

 

Use Get-DNSCleintServerAddress from the DNSclient module to get the DNS server addresses.

 

Loop through the addresses – you could make this a single pipeline if you wanted rather than using foreach loop.

 

Run a ping test using Test-Connection. the version of Test-Connection in PowerShell core is truly horrible as it prints a whole load of stuff to screen even in quiet mode. One of these days I need to write a ping type function that actually works like I want – similar to the Windows PowerShell Test-Connection.

 

Use Resolve-DNSname, again from the DNSclient module, to resolve the server address on the DNS server. If thsi works you should get the DNS server name back in the NameHost property even if you don’t have a reverses lookup zone! Change resolved to $true in this case.

 

The results give you the DNS server name, IP address, if its ping able and if it can perform DNS resolution. If you can’t ping it then either the servers down or you have network problems. If it can’t perform the resolution then DNS is down or it doesn’t have a record for itself. Further investigation is then required.

 

Test isn’t perfect but it does give you a reasonable idea of where problems might be.

Windows Terminal

Microsoft have released the first preview of the new Windows Terminal to the Windows store - https://devblogs.microsoft.com/commandline/windows-terminal-microsoft-store-preview-release/

 

Its currently only available for Windows 10 v18362 or later.

 

The terminal is a replacement for the very elderly Windows console – used for command prompt and PowerShell.

 

Some improvements over the old console include multiple tabs, Unicode and UTF-8 support and custom themes, styles and configurations.

 

Be interesting to see if its actually a good replacement or just makes life more complicated. I’ll post more after some experimentation.

PowerShell 7 roadmap

It was recently announced that the next version of PowerShell core will be 7.0 not 6.3. The PowerShell 7 roadmap is available at https://devblogs.microsoft.com/powershell/powershell-7-road-map/

 

The highlights are PowerShell 7 eventually replacing Windows PowerShell but I expect that’ll be 7.x rather than 7.0 as the mechanisms to update PowerShell aren’t in place. The Windows PowerShell modules that currently don’t work with PowerShell core will be updated to work directly in core – no more WindowsCompatibility module. Expect this to happen only on latest Windows 10 and windows server editions.

 

The PowerShell support life cycle changes to match .NET core

 

PowerShell 7 to use .NET Core 3.0 which will bring back some of the missing APIs including WPF and WinForms meaning Out-GridView will return on Windows

 

Three features are explicitly mentioned; – Secure Credentials management, Looging off the box and new version notifications.

 

Requested features are listed of which the most useful is probably Foreach-Object parallel as a (partial) replacement for workflows.

 

The opportunity is here for you to comment on RFCs and provide feedback to the PowerShell team. This is your chance to help shape PowerShell 7.

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).