Categories

PowerShell original

July 22 2014–deal of the day

Half off my book PowerShell and WMI. Use code dotd072214au at www.manning.com/siddaway2/ Also Half off PowerShell in Depth, Second Edition. Use code dotd072214au at www.manning.com/jones6/

Bad practice – – creating objects

Another in my occassional series on bad practices I’ve seen and recommend you avoid.  This time I want to look at creating objects. I recently saw some code that looked a bit like this:

$os = Get-CimInstance -ClassName Win32_OperatingSystem
$comp = Get-CimInstance -ClassName Win32_ComputerSystem
$bios =  Get-CimInstance -ClassName Win32_Bios

$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty -Name OperatingSystem -Value $os.Caption
$obj | Add-Member -MemberType NoteProperty -Name Manufacturer -Value $comp.Manufacturer
$obj | Add-Member -MemberType NoteProperty -Name Model -Value $comp.Model
$obj | Add-Member -MemberType NoteProperty -Name Bootup -Value $os.LastBootUpTime
$obj | Add-Member -MemberType NoteProperty -Name BiosType -Value $bios.BIOSVersion
$obj

This involves a lot more typing and effort than is required.

A much, much  simpler way is available:

$os = Get-CimInstance -ClassName Win32_OperatingSystem
$comp = Get-CimInstance -ClassName Win32_ComputerSystem
$bios =  Get-CimInstance -ClassName Win32_Bios

$obj = New-Object -TypeName PSObject -Property @{
OperatingSystem = $os.Caption
Manufacturer = $comp.Manufacturer
Model = $comp.Model
Bootup = $os.LastBootUpTime
BiosType = $bios.BIOSVersion
}
$obj

The Property parameter takes a hash table of property names and values and populates the object. Much less typing and more obvious, to my mind.

Some people complain that use the Property parameter means that you lose the order of the properties.

Who cares – its an object. Access the properties as you need them.

If for some reason you need to be able to dictate the order of the properties then use an ordered hash table:

$os = Get-CimInstance -ClassName Win32_OperatingSystem
$comp = Get-CimInstance -ClassName Win32_ComputerSystem
$bios =  Get-CimInstance -ClassName Win32_Bios

$props = [ordered]@{
OperatingSystem = $os.Caption
Manufacturer = $comp.Manufacturer
Model = $comp.Model
Bootup = $os.LastBootUpTime
BiosType = $bios.BIOSVersion
}

$obj = New-Object -TypeName PSObject -Property $props
$obj

But however you use the hash table use it in preference to Add-Member for these scenarios.

Does Add-Member have a place. Yes. Use when you want to add one or two properties to an existing object.

DSC Resource Kit Wave 5

The next wave of the DSC resource kit has arrived – see http://blogs.msdn.com/b/powershell/archive/2014/07/17/powershell-dsc-resource-kit-wave-5-arrives.aspx for details

Docker and DSC for Linux

Docker is a way to “virtualise” applications on Linux machines. With DSC for Linux you can manage Docker instances http://blogs.technet.com/b/privatecloud/archive/2014/07/17/configuring-docker-on-azure-with-powershell-dsc.aspx

PowerShell Summit Europe 2014 – – update 5

Registration is now open. Access through Events menu at powershell.org

Time to stock up your book collection?

Manning are offering 40% off everything – print & ebooks – www.manning.com. They have a very extensive set of PowerShell books – now would be the time to add to your collection

PowerShell Summit Europe 2014 – – update 4

Registration will open next Tuesday (15th July) – that’s less than 1 week

PowerShell Summit Europe 2014 – – update 3

Registration for the Summit opens in 14 days – 15th July

If you need some help selling the idea of attending to your boss cheeck out this post http://richardspowershellblog.wordpress.com/2014/06/21/powershell-summit-europe-2014-reasons-to-attend-1-7/

Expanding server names

I had a comment left on my recent post – “Bad practices – making scripts needlessly interactive” asking how to deal with the situation of N servers consecutively numbered e.g. server01 to server05. Taking the script from that post as an example:

 

[CmdletBinding()]
param (
[string[]]$computername
)

foreach ($computer in $computername){
  Restart-Computer -ComputerName $computer
}

 

the questioner wanted to be able to do something like

./reboot –computername server0[1-5]

which would have the same effect as typing

./reboot –computername server01, server02, server03, server04, server05

 

There are two approaches that come to mind. The first approach would be to put some code to perform the expansion into the script and use parameter sets to differentiate between a set of full names or a group of names to expand.  This would give you something like this:

[CmdletBinding()]
param (

[parameter(ParameterSetName="Names")]
[string[]]$computername,

[parameter(ParameterSetName="Group")]
[string]$computergroup

)

switch ($psCmdlet.ParameterSetName) {
"Names"  {
             foreach ($computer in $computername){
               Restart-Computer -ComputerName $computer
             }
          }
"Group"  {
              $groupdata = $computergroup.Replace("]", "") -split "\["
              $prefix = $groupdata[0]
              $nums = $groupdata[1] -split "-"
             
              $nums[0]..$nums[1] |
              foreach {
                Restart-Computer -ComputerName "$prefix$_"
              }
              
          }
default {Write-Error "Error!!! Should not be here" }
}

 

Two parameters are defined. The computername parameter takes one or more computer names. The computergroup parameter takes some kind of shorthand notation to describe a group of consecutively named machines such as server0[1-5]. Pameter sets are used to make the parameters mutually exclusive .

Processing is based on a switch determined by parameter set name.

For a set of computer names the processing is as previously.

For a shorthand notation the data is split to provide the prefix – ‘server0’  in this case.

The numbers are then split on the “-“ to effectively give a first and last which are used in a range operator and passed through foreach-object to the restart-computer cmdlet where the computer name is created.

This approach works but it has one major drawback. You would need to put the code to expand the server names into each and every script or function you wrote. That’s extra work we can avoid.

The essence of PowerShell cmdlets is that they are a small piece of code that perform a discrete job. If we follow that pattern we can split the expansion of the server names out into a reusable piece of code that then passes computer names to our other scripts or functions. I would make the expand code a function so that you can load it through a module or your profile.

function expand-servername {
[CmdletBinding()]
param (

[string]$computergroup

)
$computers = @()

$groupdata = $computergroup.Replace("]", "") -split "\["
$prefix = $groupdata[0]
$nums = $groupdata[1] -split "-"
             
$nums[0]..$nums[1] |
foreach {
$computers += "$prefix$_"
}

Write-Output $computers
}

 

using the function generates the server names:

£> . .\expand-servername.ps1
£> expand-servername -computergroup server0[1-5]
server01
server02
server03
server04
server05

 

You can use the reboot script like this:

.\reboot.ps1 -computername (expand-servername -computergroup server0[1-5])

 

If I was doing this for my production environment I would make the reboot script into an advanced function that accepted pipeline input. The reboot and expand-servername functions would be part of a module that could auto load. Alternatively. make expand-servername part of a module that you auto load. You could expand the options in expand-servername to accomodate multiple patterns of names.

PowerShell.org – TechSession webinairs

PowerShell.org are starting a series of technical webinairs. Details from

http://powershell.org/wp/techsession-webinars/