Monthly Archive

Categories

ScriptBlocks Decoded

My second talk at the PowerShell Summit 2018 was ScriptBlocks Decoded.

 

Starting from the basic explanation of what is a scriptblock and why they’re important it progressed through to proxy functions at the end.

 

The slides and code are available at https://github.com/RichardSiddaway/Summit2018ScriptBlocksDecoded

 

The recording of the session should be on the powershell.org youtube channel sometime in May

Troubleshooting #2

Last time I covered the PowerShell Troubleshooting pack. This time in Troubleshooting #2 I want to show you how to use Pester when troubleshooting.

 

When you’re troubleshooting you ideally want to follow a repeatable process so that you can do it again if necessary and teach others to do the same task.

By wrapping the troubleshooting tests as Pester tests you achieve these aims.

 

If you’ve not used Pester its a PowerShell module that was originally conceived for performing unit tests on your code. You can use Pester for testing your infrastructure is correctly built and configured as well.

 

I created a set of Pester tests for various scenarios where remoting has gone wrong. For example

Describe 'Listener' {
## tests listener available
It 'Listener is available' {
Test-Path -Path WSMan:\localhost\Listener\listener* |
Should Be $true
}
}

 

tests if the remoting listener is available

and

Describe 'Port' {
## tests listening on correct remoting port
It 'Remoting Port is 5985' {
Get-Item  "$path\Port" |
select -ExpandProperty Value |
Should Be '5985'
}
}

 

tests the port that remoting is using.

 

I wanted the troubleshooting script to stop when it hit a problem so I used a second script to execute the tests. Normally  Pester would execute all tests in a file.

$tests = 'WinRM', 'Listener', 'Listener Enabled', 'Transport', 'Address',
'Port', 'EndPoint Exists', 'EndPoint Enabled', 'Firewall Enabled', 'Firewall Allows',
'Remoting Enabled'

$data = foreach ($test in $tests) {
$result = $null
$result = Invoke-Pester -Script @{Path = '.\RemotingTests.ps1'} -PassThru -TestName "$test" -Show None #-EnableExit

$props = [ordered]@{
'Test' = $result.TestResult.Name
'Result' = $result.TestResult.Result
'Failue Message' = $result.TestResult.FailureMessage
}
New-Object -TypeName PSObject -Property $props

if ($result.FailedCount -gt 0) {break}
}

$data | Format-Table -AutoSize -Wrap

 

List the tests I want to run and foreach test run it. Output the results of the test and if the test failed break out of the loop and dump the results to screen.

 

if everything works you see something like this

PS>  .\Test-Remoting.ps1

Test                        Result Failue Message
----                        ------ --------------
WinRM should be running     Passed
Listener is available       Passed
Listener is Enabled         Passed
Transport is HTTP           Passed
Address is *                Passed
Remoting Port is 5985       Passed
End Point Exists            Passed
EndPoint is Enabled         Passed
Firewall rule is enabled    Passed
Firewall rule set to Allow  Passed
PowerShell Remoting Enabled Passed

 

If at any stage the test fails the testing process stops and you get a display showing the tests that passed and a message explaining the test that failed.  You can fix that and then rerun.

 

The code is available at

https://github.com/RichardSiddaway/Summit2018TroubleShootingRemoting

 

There are also a number of scripts that break remoting so you can test the methodology. For each breaking script there is the corresponding fix. The demo script I used in the talk is also available.

 

Its currently a proof of concept for the Summit talk but I think the approach has strong possibilities as troubleshooting methodology that could be applied to many troubleshooting scenarios.

 

Enjoy, use and adapt to meet your needs

Troubleshooting #1

How do you go about troubleshooting a problem?

 

There are two ends to the spectrum of solutions to troubleshooting a problem. At one end you have the click and pray brigade who will manically click round all the menus on all the tools they can think of using to try and find something that will fix the problem. I’ve watched people like this in action and while it looks like they’re working incredibly hard in reality they are achieving nothing  - in fact less than nothing because their manic activities get in the way of actually discovering a solution to the problem.

 

At the other end you have the people who have a process – usually manual – to work through solving the problem. In this case they methodically work through the process eliminating possible issues until they arrive at the cause of the problem. The classic problem solving routine for network connectivity is a classic example:

- ping the loop back adapter 127.0.0.1

- ping the local IP address

- ping the default gateway

- ping one or more routers

- ping destination address

You find out where the problem lies and can then address how to fix it.

 

One problem with the second approach is that it requires experience. Experience in administration and troubleshooting and  keeping calm and methodically working through the process when people are shouting at you for a solution. Junior admins usually don’t have this experience.

 

For a number of versions (I can’t remember when it first appeared) Windows PowerShell has carried a Troubleshooting module – its NOT available in Powershell v6.

There a number of troubleshooting packs in C:\Windows\Diagnostics\system\

Name
----
AERO
Apps
Audio
BITS
BlueScreen
Bluetooth
Device
DeviceCenter
HomeGroup
Internet
Keyboard
Networking
PCW
Performance
Power
Printer
Search
speech
UsbCore
Video
WindowsMediaPlayerConfiguration
WindowsMediaPlayerMediaLibrary
WindowsMediaPlayerPlayDVD
WindowsUpdate

 

You can view the individual packs – for instance

PS> Get-TroubleshootingPack -Path C:\Windows\Diagnostics\system\BITS | fl

Path              : C:\Windows\Diagnostics\system\BITS
Id                : BITSDiagnostic
Version           : 3.5
Publisher         : Microsoft Windows
Name              : Background Intelligent Transfer Service
Description       : Find and fix problems that may prevent background downloads from working
MinimumVersion    : 6.0
PrivacyLink       : https://go.microsoft.com/fwlink/?LinkId=534597
SupportsClient    : True
SupportsServer    : True
SupportsX86       : True
SupportsAmd64     : True
SupportsIA64      : True
RequiresElevation : True
Interactive       : True
RootCauses        : {Service registration is missing or corrupt, Check for missing or corrupt files, Some security
settings are missing or have been changed}
FoundRootCauses   :
Interactions      : {Check for missing or corrupt files, Restart your PC}
ExtensionPoint    : #document

 

And you can fire off the troubleshooting pack

PS> Get-TroubleshootingPack -Path C:\Windows\Diagnostics\system\BITS | Invoke-TroubleshootingPack
Initialising the diagnostic  -1

Check for missing or corrupt files
:
Checking security descriptor…  -1
Starting Bits service  -1

No problems were detected

 

In this case the BITS service was stopped and the troubleshooting pack restarted it to solve the problem.

 

The major issue with the TroubleShooting module is that there are only a very limited number of troubleshooting packs as you’ve seen. There’s no documentation that I’ve been able to find that deals with writing new packs.

 

One of my recent talks at the PowerShell Summit was about troubleshooting Powershell WinRM based remoting. I wanted to show how to automate troubleshooting as well as how to actually troubleshoot remoting issues.

 

I eventually had the idea to use Pester – the code testing module. I’ve used it to test code and infrastructure but I’d never seen anyone talk about using it as a general troubleshooting  tool.

 

Next time I’ll show you how to use Pester to create an expandable troubleshooting tool that can be adapted for almost any scenario.

CIM references and associations

Way back in 2011, when I were just a young lad, I wrote about WMI or CIM references and associations - https://wordpress.com/read/blogs/16267735/posts/1673

ASSOCIATORS show the end point of the link between CIM classes and REFERENCES shows the linking class.

I used the Win32_NetworkAdapter class in the example because it has a known association with Win32_NetworkAdapterConfiguration.

 

One thing I appear to have not made clear is that you have to use the key to the CIM class in the query for the reference or association. The key for a Win32_networkadapter class is DeviceID.

This became clear when a reader pointed out that this won’t work:

PS> Get-WmiObject -Query "REFERENCES OF {win32_printer.name = 'fax'}"
Get-WmiObject : Invalid object path
At line:1 char:1
+ Get-WmiObject -Query "REFERENCES OF {win32_printer.name = 'fax'}"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject], ManagementException
+ FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

 

But that

Get-WmiObject -Query "REFERENCES OF {Win32_Printer.DeviceID='Fax'}"

works. Again DeviceId is the key to the class.

 

Usually its better to go straight to the association and bypass the reference.

A better approach than writing WQL queries is to use the CIM cmdlets

$p = Get-CimInstance -ClassName Win32_Printer -Filter "Name='Fax'"

Get-CimAssociatedInstance -InputObject $p

will show you the data from the associated classes.

 

If you just want to see the associated class names

PS> Get-CimAssociatedInstance -InputObject $p | select CIMclass

CimClass
--------
root/cimv2:Win32_PrinterDriver
root/cimv2:Win32_PrinterConfiguration
root/cimv2:Win32_ComputerSystem
root/cimv2:CIM_DataFile

 

And if you just want the data for a particular associated class

PS> Get-CimAssociatedInstance -InputObject $p -ResultClassName Win32_PrinterDriver

Caption :
Description :
InstallDate :
Name : Microsoft Shared Fax Driver,3,Windows x64
Status :
CreationClassName : Win32_PrinterDriver
Started :
StartMode :
SystemCreationClassName : Win32_ComputerSystem
SystemName :
ConfigFile : C:\WINDOWS\system32\spool\DRIVERS\x64\3\FXSUI.DLL
DataFile : C:\WINDOWS\system32\spool\DRIVERS\x64\3\FXSUI.DLL
DefaultDataType :
DependentFiles : {C:\WINDOWS\system32\spool\DRIVERS\x64\3\FXSWZRD.DLL,
C:\WINDOWS\system32\spool\DRIVERS\x64\3\FXSTIFF.DLL,
C:\WINDOWS\system32\spool\DRIVERS\x64\3\FXSRES.DLL,
C:\WINDOWS\system32\spool\DRIVERS\x64\3\FXSAPI.DLL}
DriverPath : C:\WINDOWS\system32\spool\DRIVERS\x64\3\FXSDRV.DLL
FilePath :
HelpFile :
InfName :
MonitorName :
OEMUrl :
SupportedPlatform : Windows x64
Version : 3
PSComputerName :

Change a function definition

Functions in PowerShell are based on scriptblocks and as I showed in my session at the recent PowerShell Summit its possible to change a function definition.

Let’s start with a simple function:

PS> function f1 {
>> $x = 1
>> $y = 2
>> $x + $y
>> }
PS> f1
3

Our function just adds two numbers and outputs the result.

Digging into functions

PS> Get-Item -Path function:\f1 | Format-List *

PSPath : Microsoft.PowerShell.Core\Function::f1
PSDrive : Function
PSProvider : Microsoft.PowerShell.Core\Function
PSIsContainer : False
HelpUri :
ScriptBlock :
$x = 1
$y = 2
$x + $y

CmdletBinding : False
DefaultParameterSet :
Definition :
$x = 1
$y = 2
$x + $y

Options : None
Description :
Verb :
Noun :
HelpFile :
OutputType : {}
Name : f1
CommandType : Function
Source :
Version :
Visibility : Public
ModuleName :
Module :
RemotingCapability : PowerShell
Parameters : {}
ParameterSets : {}

You’ll see a scriptblock property that is the equivalent of the function definition

This means you can change the function definition by changing the scriptblock

PS> $function:f1 = {
>> $x = 2
>> $y = 3
>> $x * $y
>> }
PS> f1
6

The function has been changed to output the product of the two numbers instead of the sum.

Just one of the many things that scriptblocks can do for you

Code reviews and Iron Scripter

Many organisations use code reviews to help ensure a quality outcome. At the recent PowerShell Summit we ran a scripting competition – Iron Scripter. I’ve had a thought on how code reviews and Iron Scripter have something in common. Something that could lead to a better code review.

Iron Scripter involved the attendees splitting into 3 factions – each with its own philosophy:

Battle faction produce code that’s good enough to do the job then move onto the next problem.

Daybreak faction produce beautiful code that is easy to read and understand

Flawless faction produces code that “never” fails – it has all of the error handling and validation that you could wish for.

 

How does this help your code reviews?

Get people to adopt one of the above points of view (POV) either by choosing what comes naturally to them or by assigning roles. They then review the code from that POV:

Battle faction – does it get the job done

Daybreak faction – is it easy to understand and therefore maintain

Flawless faction – are errors handled

 

You can add other POV as desired and you don’t have to use the faction names. By getting people to review the code from differing POV you will have a better code review and therefore a better product.

Positional parameters

Positional parameters allow you go use a function of cmdlet without specifying the parameter names. The values you supply are assigned to the correct parameters based on their position.

If you look at the documentation for PowerShell you’ll see some confusion as to whether position 0 or position 1 is the first assigned position. Hopefully, these experiments will explain it all.

 

By default parameters are positional. So values are assigned to parameters based on the order of parameter definition.

function pa {
param (

[int] $x,

[int] $y
)

"`$x = $x"
"`$y = $y"

$x * $y

}

PS> pa -x 2 -y 3
$x = 2
$y = 3
6

PS> pa 2 -y 3
$x = 2
$y = 3
6

PS> pa 2 3
$x = 2
$y = 3
6

PS> pa -x 2 3
$x = 2
$y = 3
6

 

When you assign a position to one of more parameters those parameters without a positional assignment become non-positional and need to be explicitly defined

PS> pb -x 2 -y 3
$x = 2
$y = 3
6

PS> pb 2 -y 3
$x = 2
$y = 3
6

PS> pb 2 3
pb : A positional parameter cannot be found that accepts argument '3'.
At line:1 char:1
+ pb 2 3
+ ~~~~~~
+ CategoryInfo : InvalidArgument: (:) [pb], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,pb

PS> pb -x 2 3
pb : A positional parameter cannot be found that accepts argument '3'.
At line:1 char:1
+ pb -x 2 3
+ ~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [pb], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,pb

 

Alternatively

function pc {
param (

[Parameter(Position=1)]
[int] $x,

[int] $y
)

"`$x = $x"
"`$y = $y"

$x * $y

}

PS> pc -x 2 -y 3
$x = 2
$y = 3
6

PS> pc 2 -y 3
$x = 2
$y = 3
6

PS> pc 2 3
pc : A positional parameter cannot be found that accepts argument '3'.
At line:1 char:1
+ pc 2 3
+ ~~~~~~
+ CategoryInfo : InvalidArgument: (:) [pc], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,pc

PS> pc -x 2 3
pc : A positional parameter cannot be found that accepts argument '3'.
At line:1 char:1
+ pc -x 2 3
+ ~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [pc], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,pc

 

So far it looks as position 0 or position 1 can be used as the first assigned position. What happens if we use both:

function pd {
param (

[Parameter(Position=0)]
[int] $x,

[Parameter(Position=1)]
[int] $y
)

"`$x = $x"
"`$y = $y"

$x * $y

}

PS> pd -x 2 -y 3
$x = 2
$y = 3
6

PS> pd 2 -y 3
$x = 2
$y = 3
6

PS> pd 2 3
$x = 2
$y = 3
6

 

So position 0 takes precedence over position 1

What if we have gaps in the position order:

function pe {
param (

[Parameter(Position=0)]
[int] $x,

[Parameter(Position=2)]
[int] $y
)

"`$x = $x"
"`$y = $y"

$x * $y

}

PS> pe -x 2 -y 3
$x = 2
$y = 3
6

PS> pe 2 -y 3
$x = 2
$y = 3
6

PS> pe 2 3
$x = 2
$y = 3
6

 

Numerical position order is used.

In the next case position 1 is assigned to the first parameter

function pf {
param (

[Parameter(Position=1)]
[int] $x,

[Parameter(Position=2)]
[int] $y
)

"`$x = $x"
"`$y = $y"

$x * $y

}

PS> pf -x 2 -y 3
$x = 2
$y = 3
6

PS> pf 2 -y 3
$x = 2
$y = 3
6

PS> pf 2 3
$x = 2
$y = 3
6

 

Note the order of positions

function pg {
param (

[Parameter(Position=2)]
[int] $x,

[Parameter(Position=1)]
[int] $y
)

"`$x = $x"
"`$y = $y"

$x * $y

}

PS> pg -x 2 -y 3
$x = 2
$y = 3
6

PS> pg 2 -y 3
$x = 2
$y = 3
6

PS> pg 2 3
$x = 3
$y = 2
6

 

x is assigned the first positional parameter because y is explicitly defined.

In the next case no parameter is given position 0 or 1

function ph {
param (

[Parameter(Position=4)]
[int] $x,

[Parameter(Position=2)]
[int] $y
)

"`$x = $x"
"`$y = $y"

$x * $y

}

PS> ph -x 2 -y 3
$x = 2
$y = 3
6

PS> ph 2 -y 3
$x = 2
$y = 3
6

PS> ph 2 3
$x = 3
$y = 2
6

PS> ph -x 2 3
$x = 2
$y = 3
6

 

When y is defined then x gets the first available positional value. When both x and y aren’t defined y gets the first positional parameter.

 

From the above.

Positional parameters are assigned a numeric position.

Defined parameters explicitly overrides positional order

Values are assigned in order of position

If a parameter isn’t defined it gets a value based on its position

Position ranking can start at any number – 0 and 1 are the defaults.

Lowest ranked undefined parameter gets the first positional value

PowerShell join

I looked at PowerShell split a few posts back. This time I’ll look at the opposite action – PowerShell join.

Lets look at a simple string

PS> $str = 'PowerShell for Windows and Linux'

and split it

PS> $strs = -split $str
PS> $strs
PowerShell
for
Windows
and
Linux

 

You end up with an array of strings.

Now lets put it back together again

You can just use the operator

PS> -join $strs
PowerShellforWindowsandLinux

 

but there’s no delimiter (default) between the elements as they’re joined. You can define a delimiter

PS> $strs -join ' '
PowerShell for Windows and Linux
PS> $strs -join '_'
PowerShell_for_Windows_and_Linux

 

And that’s it for join.

Beware that you can use variable substitution to effect a join

PS> "$strs"
PowerShell for Windows and Linux

 

Also be aware that if you give the join operator a comma-separated list the join will fail

PS> -join 'PowerShell', 'for', 'Windows', 'and', 'Linux'
PowerShell
for
Windows
and
Linux

 

because join has a higher operator precedence than comma

You need to wrap the list in parentheses

PS> -join ('PowerShell', 'for', 'Windows', 'and', 'Linux')
PowerShellforWindowsandLinux

or use a variable as you saw earlier
PS> $x = 'PowerShell', 'for', 'Windows', 'and', 'Linux'
PS> -join $x
PowerShellforWindowsandLinux

 

Using a delimiter also works

PS> 'PowerShell', 'for', 'Windows', 'and', 'Linux' -join ' '
PowerShell for Windows and Linux
PS> ('PowerShell', 'for', 'Windows', 'and', 'Linux') -join ' '
PowerShell for Windows and Linux

Iron Scripter 2108

Iron Scripter 2018 is the scripting competition we’re holding at the PowerShell Summit on 12 April 2018.

 

The task for the main event is still under wraps but even if you can’t make the Summit you can benefit from the competition.

 

I’ve posted 10 prequel puzzles on powershell.org. Each puzzle revolves around a task you may need to perform. There is a commentary available for each puzzle (the commentary for puzzle 1o will be published on 1 April). If you’ve not looked at the puzzles there are some very interesting parts of PowerShell you need to dig into to solve them. The commentary discusses how I solved them.

 

Starting on Sunday 8 April we’ll be publishing 4 preludes to the iron Scripter event on ironscripter.us. These are shorter puzzles that may (or may not) help you solve the main event. We won’t be publishing any commentary on them but there is the iron Scripter forum on powershell.org

 

Even if you’re not attending Summit get in touch with someone who is and offer remote assistance for the competition itself. Its on 12 April 3-4pm PDT (11-midnight BST).

Windows server futures

A blog post - https://cloudblogs.microsoft.com/windowsserver/2018/03/29/windows-server-semi-annual-channel-update/ – has clarified Windows server futures.

 

Going forward the Semi-Annual Channel (SAC) releases – Server 1803 is due very soon  - will concentrate on containers (applications and hosts) and fast innovation application scenarios. Releases will be every 6 months with an 18 month lifetime.

 

The Long Term Servicing Channel (LTSC) is for general purpose file servers, ‘traditional’ applications (whatever that means), infrastructure roles, software defined datacenter and Hyper-converged infrastructure. Releases will be every 2-3 years with the standard 5+5 lifecycle.

 

SAC will be Server Core only. if you want a GUI you need LSTC.

 

Innovations from SAC releases that occur between LSTC releases will be rolled into the next LSTC.

 

This clarification is useful and the differences between SAC and LSTC now make sense.

 

I’d recommend that you only use the SAC releases if you need them as they do introduce more churn into the environment given their short lifecycle. I wouldn’t ignore them –keep any eye on the releases as they become available as there might be a feature that makes your life much easier.