Monthly Archive

Categories

PowerShell

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.

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.

Dynamic parameters

PowerShell has always been an extensible language meaning that you can add things on, change things and even remove things if required. One way that this extensibility surfaces is dynamic parameters.

 

A dynamic parameter is a parameter that is available when certain conditions are met. Many of the core cmdlets (about_Core_Commands) have dynamic parameters that are available dependent on the PowerShell provider currently in use.

 

A PowerShell provider exposes a data store in the same way as the file system

PS> Get-PSProvider | select Name, Drives

Name        Drives
----        ------
Registry    {HKLM, HKCU}
Alias       {Alias}
Environment {Env}
FileSystem  {C, D}
Function    {Function}
Variable    {Variable}
Certificate {Cert}

 

You can view the available certificates.

Get-ChildItem -Path Cert:\CurrentUser\ –Recurse

 

Now view the syntax of Get-ChildItem

PS> Get-Command Get-ChildItem -Syntax

Get-ChildItem [[-Path] <string[]>] [[-Filter] <string>] [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Depth <uint32>] [-Force] [-Name] [-UseTransaction] [-Attributes <FlagsExpression[FileAttributes]>] [-Directory] [-File] [-Hidden] [-ReadOnly] [-System] [<CommonParameters>]

Get-ChildItem [[-Filter] <string>] -LiteralPath <string[]> [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Depth <uint32>] [-Force] [-Name] [-UseTransaction] [-Attributes <FlagsExpression[FileAttributes]>] [-Directory] [-File] [-Hidden] [-ReadOnly] [-System] [<CommonParameters>]

 

Move into the cert: drive and have another look at Get-ChildItem

PS> Get-Command Get-ChildItem -Syntax

Get-ChildItem [[-Path] <string[]>] [[-Filter] <string>] [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Depth <uint32>] [-Force] [-Name] [-UseTransaction] [-CodeSigningCert] [-DocumentEncryptionCert] [-SSLServerAuthentication] [-DnsName <DnsNameRepresentation>] [-Eku <string[]>] [-ExpiringInDays <int>] [<CommonParameters>]

Get-ChildItem [[-Filter] <string>] -LiteralPath <string[]> [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Depth <uint32>] [-Force] [-Name] [-UseTransaction] [-CodeSigningCert] [-DocumentEncryptionCert] [-SSLServerAuthentication] [-DnsName <DnsNameRepresentation>] [-Eku <string[]>] [-ExpiringInDays <int>] [<CommonParameters>]

 

Comparing the 2 syntax lists you’ll see a number of differences. These are the dynamic parameters.

You can use –Eku for instance for filter the certificates

Get-ChildItem -Path Cert:\CurrentUser\ -Recurse -Eku 'Client'

 

If you try to run that command from any drive but cert: it’ll fail.

 

You can discover the dynamic parameters that are available for a particular provider by looking at the provider’s help

Get-Help certificate

 

When you’re working with PowerShell providers and drives remember to check what dynamic parameters are available – could save you some time and effort.

 

Note these dynamic parameters don’t appear to be available in PowerShell v6

Documentation can be wrong!

We rely on vendor documentation to help us solve problems. Documentation is produced by people and people make mistakes so Documentation can be wrong!

 

As an example:

The CIM class Win32_OperatingSystem has a Description property. According to the documentation the Description property is

“ Description of the Windows operating system. Some user interfaces for example, those that allow editing of this description, limit its length to 48 characters.”

 

if you take this to mean the description describes something about the OS you’d be wrong.

PS> Get-CimInstance Win32_OperatingSystem | Format-List Caption, Description

Caption : Microsoft Windows 10 Enterprise
Description : Richards Laptop

 

The caption property gives a description of the OS. The Description property is what you’ll find at

Control Panel –> System –> Advanced system settings –> Computer Name tab –> Computer description field

 

If you find something that doesn’t match with the documentation double check as you may find the documentation is wrong!

PowerShell sleep

PowerShell use tends to be very interactive. You run a command at the console and get some results. You run a script and get some results. How do you make PowerShell sleep?

There’s a few ways you can make PowerShell code sleep.

 

First there’s Start-Sleep

PS> for ($i=0; $i -le 10; $i++){
>> if ($i -eq 5) {
>> Get-Date
>> Start-Sleep -Seconds 30
>> Get-Date
>> }
>> }

30 January 2018 14:06:21
30 January 2018 14:06:51

I use Start-Sleep when starting a bunch of virtual machines to ensure one is fully up and running before attempting to start the next.

 

You can use the pause function to add a manually controlled delay

PS> for ($i=0; $i -le 10; $i++){
>> if ($i -eq 5) {
>> Get-Date
>> pause
>> Get-Date
>> }
>> }

30 January 2018 14:08:57
Press Enter to continue...:
30 January 2018 14:09:44

 

Pause is a function created when you start PowerShell

PS> Get-Command pause | select definition

Definition
----------
$null = Read-Host 'Press Enter to continue...'

so if you prefer you can use Read-Host

 

You could also use a workflow and suspend the job

PS> workflow w1 {
>> for ($i=0; $i -le 10; $i++){
>> if ($i -eq 5) {
>> Get-Date
>> Suspend-Workflow
>> Get-Date
>> }
>> }
>> }
PS> w1 -AsJob

Id Name PSJobTypeName   State HasMoreData  Location  Command
-- ---- -------------   ----- -----------  --------  -------
3  Job3 PSWorkflowJob Running        True  localhost      w1

PS> Get-Job

Id Name PSJobTypeName     State HasMoreData  Location Command
-- ---- -------------     ----- -----------  -------- -------
3  Job3 PSWorkflowJob Suspended        True localhost      w1


PS> Resume-Job -Id 3

Id Name PSJobTypeName   State HasMoreData  Location Command
-- ---- -------------   ----- -----------  -------- -------
3  Job3 PSWorkflowJob Running        True localhost      w1


PS> Get-Job

Id Name PSJobTypeName     State HasMoreData  Location Command
-- ---- -------------     ----- -----------  -------- -------
3  Job3 PSWorkflowJob Completed        True localhost      w1


PS> Receive-Job -Id 3

30 January 2018 18:39:25
30 January 2018 18:40:39

 

Remember that workflows aren’t available on PowerShell v6

PowerShell –f string

A PowerShell –f string is used to format the data inside a string. –f is usually referred to as the format operator.

 

The operation of the format operator is best explained by examples. At its simplest you create fields in the string using {} and the arguments to the right of the –f operator are assigned to those fields in numeric order:

PS> "{0} is  {1}" –f  'A', 'a'
A is  a

 

‘A’ is the first argument (so is assigned to field {0} and so on.

 

You can format the contents of the fields. The full syntax is

{index[,alignment][:format string]}

index is the zero based index of the arguments after the format operator as you’ve seen.

alignment takes a positive number and aligns the item to the right of a field of that length. A negative number produces a left alignment.

PS> "{0,5} is  {1,5}!" -f 'A', 'a'
A is      a!
PS> "{0,-5} is  {1,5}!" -f 'A', 'a'
A     is      a!
PS> "{0,5} is  {1,-5}!" -f 'A', 'a'
A is  a    !
PS> "{0,-5} is  {1,-5}!" -f 'A', 'a'
A     is  a    !

 

The format strings cover numeric and date time formatting

https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings

https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings

will get you started and provide links to other formatting information

 

A few more examples.

To control the total number of digits displayed

PS> "{0:D3} {1:D3} {2:D3}" -f 2, 20, 200
002 020 200

 

for hexadecimal – case matches case in string

PS> "{0:X4} {1:x8} " -f  1234, 1234
04D2 000004d2

 

Truncating the digits after decimal point

PS> "{0:F3} {1:F2} {2:F1}" -f 2.1234, 3.1234, 4.1234
2.123 3.12 4.1

 

Dates can be manipulated as well

PS> "{0:yyyy} {0:MM} {0:dd} {0:hh} {0:mm} {0:ss}" -f (Get-Date)
2018 01 28 02 24 03

Note that I’m referring to a single object for all of the fields

DSC update

The PowerShell team have posted an update on what’s happening with DSC. https://blogs.msdn.microsoft.com/powershell/2018/01/26/dsc-planning-update-january-2018/

 

The interesting thing is the decoupling of the Local Configuration Manager from Windows. A new LCM that can use resources written in multiple languages sounds good – DSC on Linux can finally have custom written resources.

 

LCM will be open source

 

No comments on pull server in the post though – ideally pull server should be open source as well

Iron Scripter prequels

One of the new things for Summit 2018 is the iron Scripter competition on the last afternoon. As a warm up for the competition we’re running a number of Iron Scripter prequels.

 

A puzzle will be published every week – first 2 are on powershell.org.

 

A forum exists to discuss the solutions in the context of the Iron Scripter factions.

 

Even if you aren’t going to Summit you can solve the puzzles and contribute to the solution.

 

Think of the puzzles as a Scripting Games lite. We provide a commentary on the puzzle including an example solution but we DON’T grade any submissions.

 

Join in and enjoy.

Just the date

Way back in this post https://richardspowershellblog.wordpress.com/2008/03/27/start-and-end-of-week/ I showed how to find the start of the week. I recently had a question about how to restrict the display to just the date.

 

PowerShell uses the .NET datetime class for working with dates so there will always be time information even if its set to 0. You can however restrict the display to just the date information

 

The ToShortDateString and ToLongDateString methods will give you just the date information but as a string

PS> $s = Get-Date -Hour 0 -Minute 0 -Second 0
PS> $d = $s.AddDays(-($s).DayOfWeek.value__)
PS> $d.ToShortDateString()
07/01/2018
PS> $d.ToLongDateString()
07 January 2018

 

Alternately you could create a custom format using the formatting strings found at

https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings

For example

PS> Get-Date -Date $d -Format "yyyy-MM-dd gg"
2018-01-07 A.D.

 

You could combine the code lines to give

PS> $s = Get-Date -Hour 0 -Minute 0 -Second 0
PS> Get-Date -Date ($s.AddDays(-($s).DayOfWeek.Value__)) -Format "yyyy-MM-dd gg"
2018-01-07 A.D.

 

If you do this often enough then create a function

Relevance of Scripts

I had a sudden thought today about the relevance of scripts – PowerShell Scripts -  today.

 

PowerShell v6 can’t run some of the modules that we’ve come to rely on – for instance the AD cmdlets. The Windows Compatibility Pack (see previous post) enables access to some of the underlying .NET classes that enable AD administration through PowerShell.

 

This means going back to the v1/v2 days and writing scripts – we may wrap them as advanced functions and modules these days but the basic code is the same – instead of using canned cmdlets.

 

My first two books

PowerShell in Practice - https://www.manning.com/books/powershell-in-practice

and

PowerShell and WMI - https://www.manning.com/books/powershell-and-wmi

 

supply lots of code examples for working with PowerShell Scripts including AD and WMI. If you haven’t got a copy they’ll help you get back to basics.