Iron Scripter puzzles

We keep innovating around the content of the PowerShell Summit to ensure it remains fresh and relevant to our attendees. This year we’re introducing the Iron Scripter competition. As a run up to the main competition I’ve written a number of challenges. The first half of the Iron Scripter puzzles are available.

The puzzle is published on a Sunday with a commentary the following Sunday. I’ve listed the first six commentaries:

Puzzle 1: fix code to get monitor information and create objects –

Puzzle 2: change text output to objects and create format file –

Puzzle 3: web feeds –

Puzzle 4: legacy utilities –

Puzzle 5: working with performance counters –

Puzzle 6: determining uptime –

The commentary documents supply the puzzle and then the commentary. If you’ve not looked at them they are designed to have some serious learning points.

You don’t have to be attending the Summit to work through the puzzles.

Posted in Powershell | Leave a comment

PowerShell Scope

PowerShell Scope has an important impact on the way your code runs. When you run a script or a function in PowerShell it runs it in its own scope. This means that all variables, functions, classes, aliases etc are removed from memory at the end of the script.

Here’s an example

create a class

class test {
[int]$P1 = 1
[int]$p2 = 2


Now save the code as test.ps1.
Run the code in the console

PS> .\test.ps1
PS> [test]::new()
Unable to find type [test].
At line:1 char:1
+ [test]::new()
+ ~~~~~~
+ CategoryInfo : InvalidOperation: (test:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound

The reason you’re not seeing [test] is that a script runs in its own scope and all variables, functions classes etc are removed at the end of the script’s execution.

You need to dot source the script so that the class remains in memory. You do that by putting a . in front of the script name like this

PS> . .\test.ps1
PS> [test]::new()

P1 p2
— —
1 2

Anything defined in the console is visible to scripts you run but not vice versa.

We spent a lot of time discussing Scope in PowerShell in Action –

Posted in Powershell | Leave a comment

Controlled zip

Powershell v5 introduced the Compress- and Expand-Archive cmdlets which enabled you to manage compressed archives. I had a question about how you could control adding files to archives using a CSV file. This is how you do a controlled zip.

Start by creating a set of test data.

1..100 |
foreach {
   $file =  “File$psitem.txt”
   Get-Process | Out-File -FilePath $file

  $i = Get-Random -Minimum 1 -Maximum 4
   $zip = “Archive$”

  $props = @{
     FileName = $file
     Archive = $zip

  New-Object -TypeName PSObject -Property $props
} | Export-Csv -Path FilesToArchive.CSV –NoTypeInformation

I created a 100 files – name of the form FileN.txt and into each piped the output of Get-Process just so they weren’t empty.

I wanted 3 zip files – named

I used Get-Random to assign the zip file.

Create an object to with the file and archive and output to CSV

The CSV looks like this:

FileName    Archive    
——–    ——-    

To perform the zip

Import-Csv .\FilesToArchive.CSV |
foreach {
   Compress-Archive -Path $_.FileName -DestinationPath $_.Archive -Update

Read the CSV file and for each file add it to the appropriate archive. The –Update parameter on Compress-Archive is what allows you to add files to an existing archive.

Posted in PowerShell v5 | Leave a comment

PowerShell if

The PowerShell if statement enables you to branch your code depending in the results of one or more conditional tests. The tests can be anything you need but must produce a boolean – true/false – result.   Also 0 is treated as $false and a positive  non-zero is $true. A negative non-zero generates an error.

The syntax fro an if statement is

if (<test>){<statement list>}
elseif (<test>){<statement list>}
else {<statement list>}

You can have as many elseif sections as required. Note that the tests and statement lists are independent in each section.

As an example of an if statement in use:

$x = 7

if ($x -gt 9){“`$x more than 9 : $x”}
elseif ($x -gt 6){“`$x more than 6 : $x”}
elseif ($x -gt 3){“`$x more than 3 : $x”}
else {“`$x less than 3 : $x”}

Very often you’ll not be using elseif

$x = 7
if ($x -gt 5){“`$x more than 5 : $x”}
else {“`$x less than 5 : $x”}

If $x = 5 you’ll get a slightly misleading message so may be better to do this

$x = 5
if ($x -ge 5){“`$x more or equal to 5 : $x”}
else {“`$x less than 5 : $x”}

I often see code like this for testing boolean values

$x = $true
if ($x -eq $true){“`$x is true”}
else {“`$x is false”}

You don’t need to explicitly test in this case

$x = $true
if ($x){“`$x is true”}
else {“`$x is false”}

The variable will be true or false so just need the variable

$x = $null
if ($x){“`$x is true”}
else {“`$x is false”}

If a variable is $null then you’ll test false returned.

You should always try to test a positive rather than a negative. So

$x = $true
if ($x){“`$x is true”}
else {“`$x is false”}

rather than

$x = $true
if (-not $x){“`$x is false”}
else {“`$x is true”}

Double or triple or more negatives will make your head explode.

You can also perform multiple tests simultaneously

$x = 7
if (($x -gt 8) -or ($x -eq 7)) {“`$x is high : $x”}
else {“`$x is low : $x”}

$x = 7
if (($x -lt 10) -and ($x -ge 7)) {“`$x is high : $x”}
else {“`$x is low : $x”}

In both cases the result is:

$x is high : 7

You don’t need the () round each test but I find it helps when debugging as the code is more readable.

For the –or scenario EITHER test must evaluate to $true and for the –and scenario BOTH scenarios must evaluate to $true

The else statement is the default if the if and elseif tests all fail.

If you find your self using a number of elseif statements a switch is most likely a better code structure.

Posted in Powershell | Leave a comment


If you want to find the name of the local computer you use $env:COMPUTERNAME.

Except that doesn’t exist in Linux PowerShell v6 – you have to use $env:HOSTNAME

PowerShell 1 Consistency 0

I can live with having $env:HOSTNAME because I bet that’s what Linux users would look for. It would be nice to also have $env:COMPUTERNAME for Windows users starting out with Linux.

It gets better.

If you create a SSH remoting session using PowerShell v6 to a Linux system – $env:HOSTNAME ISN’T exposed. You have to use the hostname legacy utility.

If you create a SSH remoting session using PowerShell v6 to a Windows system – $env:COMPUTERNAM IS exposed.

PowerShell 2 Consistency 0

Could be worse I suppose.

Posted in PowerShell v6 | Leave a comment

PowerShell for loop

PowerShell has a number of looping mechanisms – do-while; do-until; foreach; while and for loops. In this post I’ll show you how to use the PowerShell for loop.

A for loop iterates a predefined  number 0f times. A basic for loop looks like this:

for ($i=0; $i -lt 10; $i++) {

In the () you have 3 statements – they can be pipelines rather than simple assignments though most people just use assignments.

$i=0;  – means set the counting variable $i to zero as the starting point

$i -lt 10; – means loop while the counting variable is less than 10

$i++ – means increment the counting variable by 1 after each loop

If you run the code you’ll see the numbers 0-9 displayed.

$i is traditionally used as the loop counting variable but it doesn’t have to be $i. It can be any arbitrary variable

for ($somevar=0; $somevar -lt 10; $somevar++) {

The reason for using $i is traditional. It traces back to one of the early programming languages – Fortran – in which variables starting with i,j,k,l,m or n were integers by default. The variable i became the counter variable because of its position in the alphabet. The tradition has progressed down through programming languages since that day.

The loop count can be decremented

for ($i=0; $i -gt -10; $i--) {

This loop will output 0 to –9

If you want to break out of a for loop use break

for ($i=0; $i -lt 10; $i++) {
if ($i -eq 5){break}

Write-Host "I now equals $i"

which outputs

I now equals 5

Alternatively, to force the loop to skip further processing and move to the next iteration

for ($i=0; $i -lt 10; $i++) {
if ($i -eq 5){continue}

which outputs 0-4 then 6-9

The for loop is a basic loop that’s best used when you want to iterate over a set of code a number of times.

You can use variables when assigning the start and endpoints

$x = 1
$y = 10
for ($i=$x; $i -lt $y; $i++) {

Also, the loop counter doesn’t have to change by 1 each time

for ($i=0; $i -lt 10; $i+=2) {

outputs 0,2,4,6,8

Posted in Powershell Basics | Leave a comment

PowerShell v6 and PowerShell Direct

Not seen this reported anywhere so thought I post.

PowerShell v6 went to GA in January 2018. PowerShell Direct is a feature of Windows 10/Windows Server 2016. By accident I found that PowerShell v6 and PowerShell Direct work together.

PowerShell v6 is based on .NET core which is basically a subset of the full .NET CLR (that powers Windows PowerShell )

PowerShell Direct is a technology by which a PowerShell v5.1 session on a Windows 10/Windows Server 2016 Hyper-V host can establish a remoting session to a Windows 10/Windows Server 2016 virtual machine over the VM bus rather than using WSMAN.

By default PowerShell v6 can’t access the Hyper-V module but if you add the PowerShell v5.1 module path to the PowerShell v6 module path:

$env:PSModulePath = ‘C:\Windows\System32\WindowsPowerShell\v1.0\Modules\;’ + $env:PSModulePath

You can then create a credential

$cred = Get-Credential manticore\richard

This is required because you’re not relying on Kerberos to authenticate as you do in standard remoting within the domain.

You can then create your session:

$s = New-PSSession -VMName W10PRV01 -Credential $cred

And use it

Invoke-Command -Session $s -ScriptBlock {Get-Service}

The session has a ComputerType of VirtualMachine

PS>  $s | fl

ComputerType           : VirtualMachine
ComputerName           : W10PRV01
ContainerId            :
VMName                 : W10PRV01
VMId                   : 374d0569-3b22-441d-84dc-802aed67dea9
ConfigurationName      :
InstanceId             : c6e6b1c1-8ed5-409f-be4c-0a1262342cb7
Id                     : 1
Name                   : WinRM1
Availability           : Available
ApplicationPrivateData : {DebugMode, DebugStop, UnhandledBreakpointMode, PSVersionTable…}
Runspace               : System.Management.Automation.RemoteRunspace
State                  : Opened
IdleTimeout            : -1
OutputBufferingMode    :
DisconnectedOn         :
ExpiresOn              :

You can also enter the session

PS>  Enter-PSSession $s
[W10PRV01]: PS C:\Users\Richard.MANTICORE\Documents>

I’ve not tried all the Hyper-V cmdlets but the Get* cmdlets that I have tried all work.

Posted in Hyper-V, PowerShell v6 | 2 Comments