Monthly Archive

Categories

Monthly Archives: May 2018

Splitting into pairs

During my session on scriptblocks at Summit 2018 I showed how to split a string into pairs of values

Start with this string

PS> $str = 'Jack,Jill,Bill,Ben,Eric,Ernie,Cagney,Lacey'

 

You want the string split in the commas – but every other comma so you get pairs if values separated by a comma.

You need to set a counter
PS> $count=@(0)
PS> $count
0

 

Then use a scriptblock to control the split
PS> $str -split {$_ -eq ',' -AND ++$count[0] % 2 -eq 0}
Jack,Jill
Bill,Ben
Eric,Ernie
Cagney,Lacey
PS> $count
7

 

You need to use an array because the scriptblock executes in its own context and a scalar value wouldn’t be updated as you need.

Try

PS> $count2 = 0
PS> $str -split {$_ -eq ',' -AND ++$count2 % 2 -eq 0}
Jack,Jill,Bill,Ben,Eric,Ernie,Cagney,Lacey
PS> $count2
0

and nothing happens.

Summit 2018 sessions

The code and slides from my Summit 2018 sessions can be found at

https://github.com/RichardSiddaway/Summit2018TroubleShootingRemoting

and

https://github.com/RichardSiddaway/Summit2018ScriptBlocksDecoded

PowerShell functionality

PowerShell functionality – in the form of modules – comes from a number of different sources.

These are:

Powershell itself including the Microsoft.PowerShell.* modules and CIM cmdlets

Windows Team – this includes the modules such as NetAdapter, NetTCPIP, Storage that were first introduced with Windows 8 and have been part of all subsequent Windows client and server versions. These modules are often CDXML (CIM based) which means they won’t be found, and can’t be installed, on versions of Windows earlier than Windows 8

Windows feature installs – these modules are installed when you install a windows feature or role such as DNS, AD, IIS or Hyper-V. Many of then are also available as part the RSAT tools. Their presence on your system is dependent on what features are installed.

Modules you have written – totally unique to you

Modules you’ve down loaded from the PowerShell gallery or other repository. Again the presence of a particular module depends on what you’ve downloaded.

Modules from third party vendors – such as NetApp or VMWare. Their presence depends on what you’ve bought and installed.

If you can’t find a cmdlet or module on your system check that its available for that version of windows then check to see where it comes from. You may, depending on the source, be able to download and/or install it.

Hyper-V book

The Month of Lunches Hyper-V book I was working on was cancelled by the publisher.

The good news is that it’s most likely going to be resurrected with another publisher and will hopefully be available later this year.

More to follow when the details are finalised

WMI and CIM accelerators

In PowerShell an accelerator is a shortcut to a .NET type. The WMI accelerators have been around since PowerShell v1. The WMI accelerators were heavily used in v1 fill some of the gaps in cmdlet coverage. The CIM accelerators appeared in PowerShell v3 (I think – only discovered them recently!). This is how you use the WMI and CIM accelerators.

 

There are three WMI accelerators

wmiclass is shortcut for System.Management.ManagementClass
wmi is shortcut for System.Management.ManagementObject
wmisearcher is shortcut for System.Management.ManagementObjectSearcher

 

And four CIM accelerators are

ciminstance is shortcut for Microsoft.Management.Infrastructure.CimInstance
cimclass is shortcut for Microsoft.Management.Infrastructure.CimClass
cimtype is shortcut for Microsoft.Management.Infrastructure.CimType
cimconverter is shortcut for Microsoft.Management.Infrastructure.CimConverter

plus

CimSession which is a shortcut for Microsoft.Management.Infrastructure.CimSession. Use this to set a parameter type.

 

Notice that there isn’t a direct correspondence between the WMI and CIM accelerators.

PowerShell v6 only has the CIM accelerators

 

The WMI accelerators are used like this:

WMICLASS

This can be used for creating new instances of CIM classes

PS> $p = [wmiclass]'Win32_Process'
PS> $p.Create("notepad.exe")

This is easily replicated using the CIM cmdlets

PS> Invoke-CimMethod -ClassName Win32_Process -MethodName create -Arguments @{CommandLine='notepad.exe'}

 

WMI

The [wmi] accelerator is used to find an instance BUT you have to use the class key!

PS> [wmi]"root\cimv2:Win32_Process.Handle='7264'"

NOTE the handle has to be the one reported by CIM NOT the one reported by Get-Process!

Its much easier to use Get-CimInstance and filter on the name

Get-CimInstance -ClassName Win32_Process -Filter "Name='pwsh.exe'"

 

WMISEARCHER

This is used to find CIM instances:

PS> $query = [wmisearcher]"SELECT * FROM Win32_Process WHERE Name='pwsh.exe'"
PS> $query.Get()

Its easier to use Get-CIMinstance these days

PS> Get-CimInstance -ClassName Win32_Process -Filter "Name='pwsh.exe'"

 

Looking at the CIM accelerators

CIMINSTANCE

This one doesn’t seem to be usable for anything but a type decorator on a parameter.

 

CIMCLASS

Using Get-CimClass

PS> Get-CimClass -ClassName Win32_process | fl

CimSuperClassName : CIM_Process
CimSuperClass : ROOT/cimv2:CIM_Process
CimClassProperties : {Caption, Description, InstallDate, Name...}
CimClassQualifiers : {Locale, UUID, CreateBy, DeleteBy...}
CimClassMethods : {Create, Terminate, GetOwner, GetOwnerSid...}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
CimClassName : Win32_Process

The output is of type Microsoft.Management.Infrastructure.CimClass for which cimclass is an accelerator BUT there doesn’t seem to be a way to use the accelerator to access a class. I think this one is only usable as a type on a parameter for a function where you want to pass in a CIM class object.

 

CIMTYPE

Microsoft.Management.Infrastructure.CimType is an enum that contains the CIM (and WMI) datatypes:

PS> [cimtype]::Boolean
Boolean
PS> [cimtype]::UInt32
UInt32

The full set of CIM data types is

PS> [enum]::GetNames([cimtype])
Unknown
Boolean
UInt8
SInt8
UInt16
SInt16
UInt32
SInt32
UInt64
SInt64
Real32
Real64
Char16
DateTime
String
Reference
Instance
BooleanArray
UInt8Array
SInt8Array
UInt16Array
SInt16Array
UInt32Array
SInt32Array
UInt64Array
SInt64Array
Real32Array
Real64Array
Char16Array
DateTimeArray
StringArray
ReferenceArray
InstanceArray

 

CIMCONVERTOR

Some of the CIM data types shown above don’t directly correspond to .NET types you’re used to from PowerShell. You can use [cimconvertor] which is shortcut for Microsoft.Management.Infrastructure.CimConverter to discover the corresponding .NET or CIM data type

.NET to CIM

PS> [cimconverter]::GetCimType([int32])
SInt32
PS> [cimconverter]::GetCimType([double])
Real64

CIM to .NET

PS> [cimconverter]::GetDotNetType([cimtype]::SInt32)

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType

PS> [cimconverter]::GetDotNetType([cimtype]::Instance)

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True CimInstance System.Object

 

For the most part I think the WMI and CIM accelerators are best ignored. Use the CIM cmdlets instead. The cimtype and cimconverter accelerators are useful when developing code to check on types between CIM and .NET

PowerShell parameter sets

PowerShell parameter sets allow you to control which parameters can be used together. If a parameter isn’t in the parameter set you’re trying to use you’ll get an error message.

PS> Get-VM -Name XYZ -id (New-Guid)
 Get-VM : Parameter set cannot be resolved using the specified named parameters.
 At line:1 char:1
 + Get-VM -Name XYZ -id (New-Guid)
 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + CategoryInfo : InvalidArgument: (:) [Get-VM], ParameterBindingException
 + FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.HyperV.PowerShell.Commands.GetVM

 

You can also use parameter sets to help control the processing within your function as this example shows:

function Convert-Temperature {
param (
[Parameter(ParameterSetName='Celsius')]
[double]$degreeC,

[Parameter(ParameterSetName='Fahrenheit')]
[double]$degreeF,

[Parameter(ParameterSetName='Kelvin')]
[double]$degreeK
)

$temps = New-Object -TypeName psobject -Property @{
'Temperature-Celsius' = 0.0
'Temperature-Fahrenheit' = 0.0
'Temperature-Kelvin' = 0.0
}

switch ($psCmdlet.ParameterSetName) {
"Celsius" {
$temps.'Temperature-Celsius' = $degreeC
$temps.'Temperature-Fahrenheit' = [math]::Round((32 + ($degreeC * 1.8)), 2)
$temps.'Temperature-Kelvin' = $degreeC + 273.15
}

"Fahrenheit" {
$temps.'Temperature-Celsius' = [math]::Round((($degreeF - 32) / 1.8), 2)
$temps.'Temperature-Fahrenheit' = $degreeF
$temps.'Temperature-Kelvin' = $temps.'Temperature-Celsius' + 273.15
}

"Kelvin" {
$temps.'Temperature-Celsius' = $degreeK - 273.15
$temps.'Temperature-Fahrenheit' = [math]::Round((32 + ($temps.'Temperature-Celsius' * 1.8)), 2)
$temps.'Temperature-Kelvin' = $degreeK
}

default {Write-Error -Message "Error!!! Should not be here" }
}

$temps
}

 

Each of the input parameters is in its own parameter set meaning that they are mutually exclusive – you can only use one of them!

The switch statement uses the active parameter set name to decide how to perform the relevant conversions – the output object contains the temperature in Celsius, Fahrenheit and Kelvin

PowerShell version

Discovering the PowerShell version you’re using can be an interesting task.

The automatic variable $PSVersionTable was introduced in PowerShell v2. On my Windows 10 version 1803 machine for Windows PowerShell I get

PS> $PSVersionTable

Name Value
---- -----
PSVersion 5.1.17134.48
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.17134.48
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1

 

$PSVersionTable is a hashtable so the order in which items are displayed may vary. The contents of the hashtable have changed over time as well.

 

The important parts of $PsversionTable include:

The version of PowerShell itself

PS> $PSVersionTable.PSVersion

Major Minor Build Revision
----- ----- ----- --------
5 1 17134 48

PS> $PSVersionTable.PSVersion.Major
5

This a simple test for version.

 

The edition is also important

PS> $PSVersionTable.PSEdition
Desktop

Desktop means its full CLR – in other words Windows PowerShell

 

The WSMAN version is also important

PS> $PSVersionTable.WSManStackVersion

Major Minor Build Revision
----- ----- ----- --------
3 0 -1 -1

You need v3 to use CIM sessions.

 

With PowerShell v6 you get a few more options

PS> $PSVersionTable

Name Value
---- -----
PSVersion 6.0.1
PSEdition Core
GitCommitId v6.0.1
OS Microsoft Windows 10.0.17134
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0

 

Notice PSEdition is set to Core.

The OS and Platform data are v6 only

The PSRemotingProtocolVersion, SerializationVersion and WSManStackVersion are the same for Windows PowerShell v5.1 and PowerShell v6.

 

PowerShell v6 runs on Windows, Linux and macOS. You can test which OS you’re on from $PSVersionTable or more simply using the following automatic variables:

PS> ls variable:\is*

Name Value
---- -----
IsLinux False
IsMacOS False
IsWindows True
IsCoreCLR True

Using these you could create branching logic to perform a task by calling the appropriate command based on the underlying operating system.

Format Data

PowerShell will format the output of objects that it knows about. For instance Get-Process shows different sets of properties depending on whether you choose a table or list view. The format data is stored in files named *.format.ps1xml.

In Windows PowerShell the format files are found in C:\Windows\System32\WindowsPowerShell\v1.0. In PowerShell v6 the format files are stored with the appropriate module.

 

If you want to create formatting for a new object, or change the formatting for an existing object the best place to start is to export the format data for an existing object

Get-FormatData -TypeName System.Diagnostics.Process | Export-FormatData -Path f5.txt

gets the format data for the object used in Get-Process.

The drawback is that is presented a long string

<?xml version="1.0" encoding="utf-8"?><Configuration><ViewDefinitions><View><Name>process</Name><ViewSelectedBy><TypeNam
e>System.Diagnostics.Process</TypeName></ViewSelectedBy><TableControl><TableHeaders><TableColumnHeader><Label>Handles</L
abel><Width>7</Width><Alignment>Right</Alignment></TableColumnHeader><TableColumnHeader><Label>NPM(K)</Label><Width>7</W
idth><Alignment>Right</Alignment></TableColumnHeader><TableColumnHeader><Label>PM(K)</Label><Width>8</Width><Alignment>R
ight</Alignment></TableColumnHeader><TableColumnHeader><Label>WS(K)</Label><Width>10</Width><Alignment>Right</Alignment>
</TableColumnHeader><TableColumnHeader><Label>CPU(s)</Label><Width>10</Width><Alignment>Right</Alignment></TableColumnHe
ader><TableColumnHeader><Width>6</Width><Alignment>Right</Alignment></TableColumnHeader><TableColumnHeader><Width>3</Wid
th><Alignment>Right</Alignment></TableColumnHeader><TableColumnHeader /></TableHeaders><TableRowEntries><TableRowEntry><
TableColumnItems><TableColumnItem><PropertyName>HandleCount</PropertyName></TableColumnItem><TableColumnItem><ScriptBloc
k>;</ScriptBlock></TableColumnItem><TableColumnItem><ScriptBlock>;</ScriptBlock></TableColumnItem><TableColumnItem><Scri
ptBlock>;</ScriptBlock></TableColumnItem><TableColumnItem><ScriptBlock>;</ScriptBlock></TableColumnItem><TableColumnItem
><PropertyName>Id</PropertyName></TableColumnItem><TableColumnItem><PropertyName>SI</PropertyName></TableColumnItem><Tab
leColumnItem><PropertyName>ProcessName</PropertyName></TableColumnItem></TableColumnItems></TableRowEntry></TableRowEntr
ies></TableControl></View><View><Name>process</Name><ViewSelectedBy><TypeName>System.Diagnostics.Process</TypeName></Vie
wSelectedBy><WideControl><WideEntries><WideEntry><WideItem><PropertyName>ProcessName</PropertyName></WideItem></WideEntr
y></WideEntries></WideControl></View></ViewDefinitions></Configuration>

 

And you have to manage the pretty printing yourself.

In PowerShell v6.1 the output is automatically pretty printed

Get-FormatData -TypeName System.Diagnostics.Process | Export-FormatData -Path f6.txt

<?xml version="1.0" encoding="utf-8"?>
<Configuration>
<ViewDefinitions>
<View>
<Name>process</Name>
<ViewSelectedBy>
<TypeName>System.Diagnostics.Process</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader>
<Label>NPM(K)</Label>
<Width>7</Width>
<Alignment>Right</Alignment>
</TableColumnHeader>
<TableColumnHeader>
<Label>PM(M)</Label>
<Width>8</Width>
<Alignment>Right</Alignment>
</TableColumnHeader>
<TableColumnHeader>
<Label>WS(M)</Label>
<Width>10</Width>
<Alignment>Right</Alignment>
</TableColumnHeader>
<TableColumnHeader>
<Label>CPU(s)</Label>
<Width>10</Width>
<Alignment>Right</Alignment>
</TableColumnHeader>
<TableColumnHeader>
<Width>7</Width>
<Alignment>Right</Alignment>
</TableColumnHeader>
<TableColumnHeader>
<Width>3</Width>
<Alignment>Right</Alignment>
</TableColumnHeader>
<TableColumnHeader />
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem>
<ScriptBlock>;</ScriptBlock>
</TableColumnItem>
<TableColumnItem>
<ScriptBlock>;</ScriptBlock>
</TableColumnItem>
<TableColumnItem>
<ScriptBlock>;</ScriptBlock>
</TableColumnItem>
<TableColumnItem>
<ScriptBlock>;</ScriptBlock>
</TableColumnItem>
<TableColumnItem>
<PropertyName>Id</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>SI</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>ProcessName</PropertyName>
</TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
<View>
<Name>process</Name>
<ViewSelectedBy>
<TypeName>System.Diagnostics.Process</TypeName>
</ViewSelectedBy>
<WideControl>
<WideEntries>
<WideEntry>
<WideItem>
<PropertyName>ProcessName</PropertyName>
</WideItem>
</WideEntry>
</WideEntries>
</WideControl>
</View>
</ViewDefinitions>
</Configuration>

 

which makes it much easier to work with

where –not

PowerShell v6.1 preview 2 has added the where –not option.

The option adds another parameter to the syntax

where-object <property name> <operator like parameter> <value>

 

Couple of examples of using the syntax

Get-Process | where CPU -gt 12

Get-Service | where Status -like 'Stop*'

 

Use –Not when you’re looking for properties that aren’t set. For example

Get-Service | where -Not DependentServices

or

Get-Process | where -Not StartTime

Calculating standard deviation

Calculating a standard deviation isn’t a difficult exercise but PowerShell v6.1 offers an easier method.

 

In Windows PowerShell v5.1 and PowerShell v6.0 the Measure-Object cmdlet has this syntax

PS> Get-Command Measure-Object -Syntax

Measure-Object [[-Property] <string[]>] [-InputObject <psobject>] [-Sum] [-Average] [-Maximum] [-Minimum] [<CommonParameters>]

Measure-Object [[-Property] <string[]>] [-InputObject <psobject>] [-Line] [-Word] [-Character] [-IgnoreWhiteSpace] [<CommonParameters>]

 

In PowerShell v6.1.0-preview.2 this changes to

PS> Get-Command Measure-Object -Syntax

Measure-Object [[-Property] <string[]>] [-InputObject <psobject>] [-StandardDeviation] [-Sum] [-Average] [-Maximum] [-Minimum] [<CommonParameters>]

Measure-Object [[-Property] <string[]>] [-InputObject <psobject>] [-Line] [-Word] [-Character] [-IgnoreWhiteSpace] [<CommonParameters>]

 

You’ve now got a standard deviation option.

How do you use it?

PS> 1..517 | Measure-Object -Sum -Average -StandardDeviation

Count : 517
Average : 259
Sum : 133903
Maximum :
Minimum :
StandardDeviation : 149.389312424506
Property :

 

There’s probably a few other calculations that would be useful to add to Measure-Object