Pipeline Power

Last week I came across a blog post that had a decent example using PowerShell and PowerCLI to get the disk location for all virtual machines. The posted code works and does display the information you might be after.

$myVMs = get-vm

foreach($vm in $myVMs){
$myDisks = @($vm | get-harddisk)
foreach ($disk in $myDisks) {
write-host $vm.Name, ( $disk|select -ExpandProperty Filename)

But I saw an teaching opportunity. Because the code works I can’t say it is “wrong”, but it really doesn’t adopt the PowerShell paradigm. The first issue is that using Write-Host only writes content to the console. There is no way with this command to do anything else with the results such as sorting, grouping or sending to a text file.

The other issue is the need to use ForEach. This is what we had to do in VBScript but in PowerShell we can take advantage of the pipeline.

get-vm | Select Name,@{Name="Disk";Expression= {$_ | get-harddisk | Select -ExpandProperty Filename }}

But now I can do something with this such as sorting by disk:

PS S:\> get-vm | Select Name,@{Name="Disk";Expression= {$_ | get-harddisk | Select -ExpandProperty Filename }} | sort Disk,Name

Name Disk
---- ----
Cluster Alpha {[datastore1] Cluster Alpha/Cluster ...
Globomantics Mail [datastore1] globomantics mail/Win2K...
MyCompany Exchange 2007 {[datastore1] MyCompany Exchange 200...
MyCompany XP {[datastore1] MyCompany XP/MyCompany...
MyCompany Windows 2008 [datastore1] MyCompany2008/Windows S...
MyCompanyDC 2K3R2 {[datastore1] MyCompanyDC 2K3R2/MyCo...
R2 Server Core -DEL [datastore1] Research Core DC/R2 Ser...
Cluster Bravo {[datastore2] Cluster Bravo/Cluster ...
MyCompany Vista {[datastore2] MyCompany Vista/Vista ...

Or if there are multiple disks, it is much easier to work with them. Write-Host can’t.

PS S:\> $vminfo=get-vm | Select Name,@{Name="Disk";Expression= {$_ | get-harddisk | Select -ExpandProperty Filename }}
PS S:\> $vminfo[1].disk
[datastore2] MyCompany Vista/Vista Baseline.vmdk
[datastore2] MyCompany Vista/MyCompany Vista.vmdk
PS S:\> $vminfo | Export-Clixml c:\work\vminfo.xml

The tricky part here I realize is pulling up a value from a nested object, in this case the Filename and adding it to the VM object. I totally get that this is not something a beginner would necessarily discover on their own, which is why I write stuff like this. But the big difference is that I know have an object written to the pipeline that I can do something with and I didn’t have to resort to keep track of what goes where in some foreach loops.

The other advantage, although not universal, is performance. Running the ForEach code against my 23 VMs took almost 6 seconds. My PowerShell one line took a tad over 3 seconds.

I don’t want you to think you can never use Write-Host or ForEach. Sometimes they make sense and may even perform better. But always ask yourself if you are thinking the PowerShell way and pushing objects through the pipeline or are you writing something that could be mistaken for VBScript.

By the way, I have posted most of this on the blog as a comment that is awaiting moderation. But I figured I would share it with my readers as well.