July 2014 - Posts

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.

Posted by RichardSiddaway | with no comments
Filed under:

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
Posted by Richard Siddaway's Blog
Filed under:

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
Posted by Richard Siddaway's Blog
Filed under:

msmvps.com is down

My mirror blog on msmvps.com is down – as is the rest of the site.  It is being worked on. In the meantime transfer here
Posted by Richard Siddaway's Blog
Filed under:

PowerShell Summit Europe 2014 - - update 5

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

Posted by RichardSiddaway | with no comments
Filed under: ,

Formatting disks - - the new way

Last time I showed how to format disks using the Win32_Volume CIM class. If you need to perform this activity on a Windows Server 2012/Windows 8 or later system you can use a couple of cmdlets from the Storage module

Get-Volume | where DriveLetter -ne 'C' | Format-Volume -FileSystem NTFS -Confirm:$false –WhatIf

 

If you’ve not looked at the Storage module before there is a lot of useful cmdlets.

Formatting disks–the old way

Saw a question on the forums about formatting all disks but the C: drive.  Assuming you ever need to do such a destructive activity you might want think about ths sort of approach

Get-CimInstance -ClassName Win32_Volume -Filter "DeviceId != 'C:'" |
foreach {
Invoke-CimMethod -whatif -InputObject $psitem -MethodName Format -Arguments @{
   ClusterSize = 4096
   EnableCompression = $false
   FileSystem = 'NTFS'
   QuickFormat = $true
}
}

 

Notice I’ve left the –whatif in for safety

More details on the Win32_Volume class at http://msdn.microsoft.com/en-us/library/aa394515%28v=vs.85%29.aspx

Posted by RichardSiddaway | with no comments
Filed under:

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

Posted by RichardSiddaway | with no comments

CIM or WMI - - accessing remote machines

I much prefer the CIM cmdlets for accessing remote machines. The WMI cmdlets use DCOM which is firewall unfriendly and can often be unavailable of a server – cue the dreaded RPC server is unavailable error messages.

By contrast the CIM cmdlets use WSMAN.

For one off access to a remote machine use the computername parameter

Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName RSSURFACEPRO2

 

If you want to access a machine multiple times in the session create a CIM session – analagous to a remoting session

 

$cs = New-CimSession -ComputerName RSSURFACEPRO2
Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $cs

 

By default a CIM session uses WSMAN

£> $cs


Id           : 1
Name         : CimSession1
InstanceId   : 30c2b530-4ff7-448e-b68d-1f1282890e6a
ComputerName : RSSURFACEPRO2
Protocol     : WSMAN

 

though you can configure them to use DCOM if need be

$opt = New-CimSessionOption -Protocol DCOM
$csd = New-CimSession -ComputerName RSSURFACEPRO2 -SessionOption $opt
Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $csd

 

When would you need to use DCOM – if you are accessing a machine with PowerShell 2 installed. The CIM cmdlets want to use WSMAN 3 and will error if you access a machine with WSMAN 2 installed however if you include a –Filter they will work

So

Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName computer1

will fail if computer1 is running WSMAN 2 (PowerShell 2)

However, if you change the command to include a filter

Get-CimInstance -ClassName Win32_OperatingSystem -Filter "Manufacturer LIKE 'Microsoft%'" -ComputerName computer1

 

Even if, as in this case, the filter doesn’t actually do anything

PowerShell Summit Europe 2014 - - update 4

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

Posted by RichardSiddaway | with no comments
Filed under: ,

Finding a CIM class

I was investigating something on my disks and started to look at the partitions:

£> Get-CimInstance -ClassName Win32_Partition
Get-CimInstance : Invalid class
At line:1 char:1
+ Get-CimInstance -ClassName Win32_Partition
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : MetadataError: (root\cimv2:Win32_Partition:String) [Get-CimInstance], CimException
    + FullyQualifiedErrorId : HRESULT 0x80041010,Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand

OK so my memory isn’t what it was and I got the class name wrong. How to find the correct class?

£> Get-CimClass *Partition*


   NameSpace: ROOT/cimv2

CimClassName
------------
CIM_DiskPartition
Win32_DiskPartition
Win32_SystemPartitions
CIM_LogicalDiskBasedOnPartition
Win32_LogicalDiskToPartition
CIM_RealizesDiskPartition
Win32_DiskDriveToDiskPartition
Win32_PerfFormattedData_HvStats_...
Win32_PerfRawData_HvStats_HyperV...
Win32_PerfFormattedData_HvStats_...
Win32_PerfRawData_HvStats_HyperV...
Win32_PerfFormattedData_VidPerfP...
Win32_PerfRawData_VidPerfProvide...

 

I’ve truncated the display horizontally as not interested in methods & properties at this point


So the class I want is Win32_DiskPartition.

Get-CimClass is one of the biggest benefits from PowerShell 3.0

CIM or WMI - - using methods

The CIM and WMI cmdlets both provide a way to use the methods on CIM classes namely Invoke-CimMethod and Invoke-WmiMethod. The cmdlets are very similar in operation.

$vol = Get-WmiObject -Class Win32_Volume -Filter "DriveLetter = 'D:'"

Invoke-WmiMethod -InputObject $vol -Name Chkdsk -ArgumentList $false, $true, $true, $false, $false, $false

 

The argumenst list isn’t very informative – unless you know the class, read the documentation or investigate with Get-CimClass

 

Using the CIM cmdlets is a bit more informative as to what is going on.

$vol = Get-CimInstance -ClassName Win32_Volume -Filter "DriveLetter = 'D:'"

Invoke-CimMethod -InputObject $vol -MethodName Chkdsk -Arguments @{FixErrors=$false; ForceDismount=$false; OkToRunAtB
ootUp = $false; RecoverBadSectors = $false; SkipFolderCycle = $true; VigorousIndexCheck = $true}

 

You present the arguments as a hash table – this means you can create the hash table and pass it to the method

$margs = @{
FixErrors=$false
ForceDismount=$false
OkToRunAtBootUp = $false
RecoverBadSectors = $false
SkipFolderCycle = $true
VigorousIndexCheck = $true
}
Invoke-CimMethod -InputObject $vol -MethodName Chkdsk -Arguments $margs

 

This also means that you can create a default set of values and manipulate them in your scripts very easily

 

Using Invoke-CimMethod involves more typing but I think that’s worth it for the clarity. Of course if you are going to be using the methods of class a lot then I’d recommend that you create a CDXML module from the class – but that’s a whole different set of articles.

Posted by RichardSiddaway | with no comments

Workflows 7: checkpointing workflows

Consider this workflow

workflow chkpt1 {

Get-Process

foreach ($x in 1..20){
$x
}

}

 

It will dump out the process information then output the numbers 1 to 20.  Not a particularly enlightening workflow but it forms  a nice basis for demonstrating checkpoints.

A checkpoint saves the state and data in the workflow. If the workflow is suspended or interrupted the work flow can be restarted from the most recent checkpoint rather than a complete restart from the beginning.

 

Change the workflow to give a long running activity

workflow chkpt1 {

Get-Process


foreach ($x in 1..1000){
$x
}

}

Let the process data be displayed and then stop execution once the numbers start displaying – this is easiest if you use ISE. If you want to restart the workflow you have to start it right from the beginning.

Add a checkpoint to the workflow. This can be achieved in a number of ways.

Use the Checkpoint-Workflow activity to the workflow.

workflow chkpt1 {

Get-Process
Checkpoint-Workflow

foreach ($x in 1..1000){
$x
}

}

 

or use the –PSPersist parameter

workflow chkpt1 {

Get-Process -PSPersist


foreach ($x in 1..1000){
$x
}

}

 

I prefer to use Checkpoint-Workflow as it is more obvious to me when I review the workflow.

 

if you want to checkpoint your workflows – you have to start them as a job

 

chkpt1 –AsJob

 

Then shut down ISE

Open another PowerShell session with elevated privileges. Use Get-Job to see the suspended job.

View the data in the job

Receive-Job -Id 5 –Keep

 

Restart the job

Resume-Job -Id 5

 

Once the job finishes view the data and you’ll see the process data and the list of numbers.

Use Checkpoint-Workflow as many time as necessary to protect your data in long running workflows. A checkpoint is a good idea any time that it would be expensive to restart the whole process.

Posted by RichardSiddaway | with no comments

and finally

If you’re old enough and seen UK TV you’ll recognise the title but this post is about using try – catch blocks.

Using try-catch this is a fairly normal construction

try {
Get-CimInstance -ClassName Win32_LogicalDisk  -ErrorAction Stop
}
catch {
Throw "something went wrong"
}

The number of commands within the try block should be minimised so that you stand a chance of knowing what you are going to catch.

if you introduce an error – for instance using the wrong classname

£> try {
Get-CimInstance -ClassName Win32_LogicalDrive  -ErrorAction Stop
}
catch {
Throw "something went wrong"
}
something went wrong
At line:5 char:2
+  Throw "something went wrong"
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (something went wrong:String) [], RuntimeException
    + FullyQualifiedErrorId : something went wrong

 

You can control what happens and even recover.

I don’t see the third element of a try-catch block being used much – that’s the finally block. The finally block will run irrespectively of whether the try block works or not. It’s your clean up block

Consider

£> try {
$cs = New-CimSession -ComputerName $env:COMPUTERNAME
Get-CimInstance -ClassName Win32_LogicalDrive  -CimSession $cs -ErrorAction Stop
}
catch {
Throw "something went wrong"
}
something went wrong
At line:6 char:2
+  Throw "something went wrong"
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (something went wrong:String) [], RuntimeException
    + FullyQualifiedErrorId : something went wrong
 

£> Get-CimSession


Id           : 1
Name         : CimSession1
InstanceId   : 68a4f534-d222-4e52-b6f9-ba65b1d57b3b
ComputerName : RSSURFACEPRO2
Protocol     : WSMAN

 

This try block fails but it leaves a CIM session hanging about. Not good practice. I know it will get cleaned up when you close the powershell session but that could be hours away. Better to clean up now.

£> try {
$cs = New-CimSession -ComputerName $env:COMPUTERNAME
Get-CimInstance -ClassName Win32_LogicalDrive  -CimSession $cs -ErrorAction Stop
}
catch {
Throw "something went wrong"
}
finally {
Remove-CimSession -CimSession $cs
}

Get-CimSession
something went wrong
At line:6 char:2
+  Throw "something went wrong"
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (something went wrong:String) [], RuntimeException
    + FullyQualifiedErrorId : something went wrong
 

no sign of a CIM session even if you try specifically.

 

£> Get-CimSession

£>

if you correct the code

try {
$cs = New-CimSession -ComputerName $env:COMPUTERNAME
Get-CimInstance -ClassName Win32_LogicalDisk  -CimSession $cs -ErrorAction Stop
}
catch {
Throw "something went wrong"
}
finally {
Remove-CimSession -CimSession $cs
}

Get-CimSession

 

You’ll find the the CIM session is still removed. A finally block will always run

When you are using try blocks think about your environment and use a finally block to clean up.

Posted by RichardSiddaway | with no comments
Filed under:

CIM or WMI?

Working with WMI became a whole easier when PowerShell came on the scene. If you ever spent hours typing all of the Echo commands that were required with VBScript to produce output you’ll be aware of what I mean.  There are still a few awkward areas in the WMI cmdlets. One of the most awkward is date/time handling.

Consider looking for the last boot up time of your favourite computer:

£> Get-WmiObject -Class Win32_OperatingSystem | select -ExpandProperty LastBootUpTime
20140702083855.487133+060

 

That breaks down as year (2014), month (07), day (02), hour (08), minute (38), second (55) and fraction of a second (.487133). The +060 denotes the minutes offset from GMT (UTC if you prefer) – I’m in the UK on daylight saving time.  So, once you know how to read it the answer is understandable but not easy to work with.

The PowerShell team introduced a method on Get-WmiObject that will convert the date to a more understandable format:

 

£> $os = Get-WmiObject -Class Win32_OperatingSystem
£> $os.ConvertToDateTime($os.LastBootUpTime)

02 July 2014 08:38:55

 

You can also use the method in select-object or the format cmdlets by using a calculated field:

£> Get-WmiObject -Class Win32_OperatingSystem | Format-List PSComputerName, Caption, @{N='BootTime'; E={$_.ConvertToDate
Time($_.LastBootUpTime)}}


PSComputerName : RSSURFACEPRO2
Caption        : Microsoft Windows 8.1 Pro
BootTime       : 02/07/2014 08:38:55

 

There is an easier way – use the CIM cmdlets:

£> Get-CimInstance -ClassName Win32_OperatingSystem | select -ExpandProperty LastBootUpTime

02 July 2014 08:38:55

 

The automatic date conversion is more than sufficient incentive for me to use Get-CimInstance in preference to Get-WmiObject.

Posted by RichardSiddaway | with no comments
Filed under:

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/

Posted by RichardSiddaway | with no comments
Filed under: ,