Monthly Archive

Categories

Monthly Archives: February 2018

PowerShell while

PowerShell has a number of looping structures – do; while; for; foreach. This is how the PowerShell while loop works

The while statement has the form:

while (<condition>){<statement list>}

 

The while loop is probably the simplest of the PowerShell loops. For example:

$x = 0
while ($x -lt 5){
Write-Host "`$x is $x"
$x++
}

 

gives:

$x is 0
$x is 1
$x is 2
$x is 3
$x is 4

 

As long as the condition is true the statement list is executed

The condition is evaluated BEFORE the loop is executed meaning that it may never run

PS> $x = 10
while ($x -lt 5){
Write-Host "`$x is $x"
$x++
}

PS>

The value of $x is greater than 4 so the loop never executes.

Get-Date format

A common question revolves around Get-Date format. In other words how can you format the output of Get-Date.

Standard output is

PS> Get-Date

27 February 2018 16:02:13

 

You can use –DisplayHint to control what’s displayed

PS> Get-Date -DisplayHint Date

27 February 2018

PS> Get-Date -DisplayHint Time

16:03:09

PS> Get-Date -DisplayHint DateTime

27 February 2018 16:03:17

 

Which is fine if you just want the date or the time

You’ve also got some methods on the DateTime object that can help

PS> (Get-Date).ToShortDateString()
27/02/2018
PS> (Get-Date).ToShortTimeString()
16:05
PS> (Get-Date).ToLongDateString()
27 February 2018
PS> (Get-Date).ToLongTimeString()
16:05:38
PS> (Get-Date).ToFileTime()
131642211467063624
PS> (Get-Date).ToFileTimeUtc()
131642211545205423
PS> (Get-Date).ToUniversalTime()

27 February 2018 16:07:01

 

Universal Time is more properly known as Greenwich Mean Time

If you want a bit more control you can use the –Format parameter. A description of the format specifiers is available at https://msdn.microsoft.com/en-GB/Library/system.globalization.datetimeformatinfo(VS.85).aspx

(Get-Date).GetDateTimeFormats()

will give a very long list of the possible formats. Unfortunately it just displays the results not the format specifier.

 

There are some preset formats – for example:

PS> Get-Date -Format g
27/02/2018 16:14
PS> Get-Date -Format r
Tue, 27 Feb 2018 16:14:58 GMT
PS> Get-Date -Format s
2018-02-27T16:15:03

 

Or you can customise the format

PS> Get-Date -Format "ddMMyyyyhhmmss"
27022018041748
PS> Get-Date -Format "dd/MM/yyyy hh:mm:ss"
27/02/2018 04:18:03
PS> Get-Date -Format "dd/MM/yyyy HH:mm:ss"
27/02/2018 16:18:08

 

Read the article at the link for the full list of options. NOTE – the options ARE case sensitive

 

Alternatively, you could use the –Uformat parameter to use Unix formatting. This is explained in the NOTES section of the Get-Date help file.

Some examples

PS> Get-Date -UFormat "%d/%m/%Y %r"
27/02/2018 04:23:25 PM
PS> Get-Date -UFormat "%d/%m/%Y %R"
27/02/2018 16:23

 

Between the display hints, the methods, –Format and –UFormat you should be able to get the date into the format you need.

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 - https://powershell.org/2018/01/21/iron-scripter-prequel-puzzle-1-a-solution/

 

Puzzle 2: change text output to objects and create format file - https://powershell.org/2018/01/28/iron-scripter-prequel-puzzle-2-a-commentary/

 

Puzzle 3: web feeds - https://powershell.org/2018/02/04/iron-scripter-2018-prequel-puzzle-3-a-commentary/

 

Puzzle 4: legacy utilities - https://powershell.org/2018/02/11/iron-scripter-2018-prequel-puzzle-4-a-commentary/

 

Puzzle 5: working with performance counters - https://powershell.org/2018/02/18/iron-scripter-2018-prequel-puzzle-5-a-commentary/

 

Puzzle 6: determining uptime - https://powershell.org/2018/02/25/iron-scripter-prequel-puzzle-6-commentary/

 

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.

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 - https://www.manning.com/books/windows-powershell-in-action-third-edition

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$i.zip"

$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 ArchiveN.zip

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
-------- -------
File1.txt Archive1.zip
File2.txt Archive3.zip
File3.txt Archive1.zip
File4.txt Archive2.zip
File5.txt Archive1.zip
File6.txt Archive3.zip

 

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.

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 your test will return false.

 

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.

ComputerName

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.

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++) {
$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++) {
$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--) {
$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}
$i
}
Write-Host "I now equals $i"

which outputs

0
1
2
3
4
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}
$i
}

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++) {
$i
}

 

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

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

outputs 0,2,4,6,8

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.

Putting on the style

PowerShell is all about getting things done but how you do things can be as important as what you do. I’ll explain what I mean so you be able to be putting on the style.

 

While PowerShell is used by a number of developers its predominantly an administrators tool. Most administrators aren’t taught to code so they pick things up as they go along -  including coding styles.

 

Back when PowerShell first appeared Jeffrey Snover talked about administrators having an ad hoc development methodology. They’d use single cmdlets to get things done. Then progress to using multiple cmdlets in a pipeline. They’d then realise that if they saved that pipeline in a script they wouldn’t have to retype it every time they wanted to use it and so save so time and errors.

 

What happens once you’ve got those first scripts?

 

You’ll realise that scripting enables you to generate tools that make your life easier and that others can also use. By then you’ve probably discovered the PowerShell community and seen what others are doing – so you learn a bit more about coding.

 

You adopt some standards – no aliases in scripts etc., etc. And you think about creating modules of advanced functions.

 

At this point you need to think about your coding style. I’m deliberately separating coding standards from coding style. Standards are what you do – style is how you do it.

 

We’ve used this style concept as the basis of the Iron Scripter competition at PowerShell Summit 2018. To help people divide themselves into teams we have three factions defined - http://ironscripter.us/factions/. The factions split on coding styles as much as anything. You can regard the differences between the factions as philosophical discussions if you prefer.

 

The factions can be summarised as:

  • Daybreak Faction - beautiful code
  • Flawless Faction - flawless code
  • Battle Faction - good enough to get the job done

 

Battle faction is the easiest to understand. You have a solution to your problem. It works and gives the correct result in a reasonable time frame. Job done. Next task. if it breaks at some time in the future you’ll fix it then but in the meantime there’s a stack of other problems to solve.

 

This is the way many administrators work, at least to begin with. Working code doesn’t mean that the code is easy to maintain. If it breaks in the future you may not be the one to fix it – so the code needs to be readable and understandable. Daybreak faction with their view that code should be beautiful take this to the extreme.

 

Ideally, your code shouldn’t break. The idea of flawless code is at the heart of Flawless faction who believe in doing everything possible to ensure that the code will run. Beauty has its place but beautiful code that doesn’t work is wasted time and effort.

 

The three factions are stereotypes and extremes but there is an extremely valid point to them. Imagine an equilateral triangle that has a faction at each point.  Your code will sit somewhere in that triangle with varying influences of battle, flawless and daybreak factions.

 

Does all your code sit at the same place in the triangle? Sometimes good enough will do and you can move on to the next task. Sometimes the code just has to run so you need a lot of flawless influence.

 

All the code you produce doesn’t have to have the same influences but knowing when to be putting on the style – and more importantly which style – could arguably make you a better coder and make your life easier.