DiskInfo oneliner

Hi, I’m trying to create a oneliner gathering Diskinformation. Although I basically succeed in what I am trying to do, i.e. I get the information I want formatted in the way I want, but it’s also throwing me errors. Here’s the code:

$DiskInfo = @(
gwmi cim_LogicalDisk | where {$_.DriveType -eq '3'} -PipelineVariable Ldisk | 
% {gwmi cim_LogicalDiskBasedOnPartition | Where {$_.Dependent -eq $Ldisk.__Path}-PipelineVariable L2P | 
gwmi Cim_DiskPartition | where {$_.__Path -eq $L2P.Antecedent} -PipelineVariable Part | 
gwmi cim_DiskDrive | where {$_.Index -eq $Part.DiskIndex} -PipelineVariable Disk | 
select 	@{n = "Disk"; 				e = {$_.index -as [int]} },
		@{n = "Model"; e = {$_.Model -as [string]} },
		@{n = "Firmware"; e = {$_.FirmwareRevision -as [string]} },
		@{n = "SerialNumber"; e = {$_.SerialNumber -as [string]} },
		@{n = "DiskSize(GB)"; e = {"{0:N1}" -f( $Disk.Size / 1024MB)} }, 
		@{n = "Partitions"; e = {$_.Partitions -as [int] } },
		@{n = "Partition"; e = {$Part.index -as [int]} }, 
		@{n = "BootPartition"; e = {$Part.BootPartition -as [string]} }, 
		@{n = "PartionSize(GB)"; e = {"{0:N1}" -f ($Part.size /1024MB)} }, 
		@{n = "Blocks"; e = {$Part.NumberOfBlocks -as [int]} }, 
		@{n = "BlockSize"; e = {$Part.BlockSize -as [int]} }, 
		@{n = "LDiskName"; e = {$Ldisk.Caption -as [string]} }, 
		@{n = "FileSystem"; e = {$Ldisk.Filesystem -as [string]} }, 
		@{n = "LDiskSize"; e = {"{0:N1}" -f ($Ldisk.size /1024MB)}}, 
		@{n = "LDiskFree"; e = {"{0:N1}" -f ($Ldisk.FreeSpace /1024MB)}}

What am I doing wrong?
Any help would be great.

A couple of things:
First, PLEASE format your code.
Second, replace the PIPE for a SEMI-COLON on the second line in your code.

From this:

gwmi cim_LogicalDisk | where {$_.DriveType -eq '3'} -PipelineVariable Ldisk |

To This:

gwmi cim_LogicalDisk | where {$_.DriveType -eq '3'} -PipelineVariable Ldisk ;

Break it down piece by piece. It seems to break down here (I don’t think you can pipe to gwmi):

gwmi cim_LogicalDisk | where {$_.DriveType -eq '3'} -PipelineVariable Ldisk |
% {gwmi cim_LogicalDiskBasedOnPartition | Where {$_.Dependent -eq $Ldisk.__Path}-PipelineVariable L2P |
gwmi Cim_DiskPartition }

NumberOfBlocks   : 250051662
BootPartition    : True
Name             : Disk #0, Partition #0
PrimaryPartition : True
Size             : 128026450944
Index            : 0

gwmi : The input object cannot be bound to any parameters for the command either because the command does not take
pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
At line:3 char:1
+ gwmi Cim_DiskPartition }
+ ~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (\\TIMMONS\root\...eviceID=\"C:\"":PSObject) [Get-WmiObject], Parameter
    + FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Commands.GetWmiObjectCommand

Thank you Ivan for helping me out here. I formatted the code.
The semicolon replacement didn’t do the trick.

Thank you JS,
What happens is, the code runs all the way to the last Select property but then jumps back to enumerating Disks and thereafter enumerating partitions throwing errors as it goes. After that it goes back to the % and it starts all over again with the next object in Ldisk.
All the way down all the PipelineVariables are neatly filled with the desired data. Every command is tested on itself and works flawlesly even if I break and copy and past the code to the command line and run it from there.
The question is why does it jump back from the last Selection definition to enumerating disks and partitions.

I got it to work like this:

$DiskInfo = @( 
gwmi cim_LogicalDisk | where {$_.DriveType -eq '3'} -PipelineVariable Ldisk | 
	% {gwmi cim_LogicalDiskBasedOnPartition | Where {$_.Dependent -eq $Ldisk.__Path}-PipelineVariable L2P | 
		% {gwmi Cim_DiskPartition | where {$_.__Path -eq $L2P.Antecedent} -PipelineVariable Part | 
			% {gwmi cim_DiskDrive | where {$_.Index -eq $Part.DiskIndex} -PipelineVariable Disk | 
			select 	@{n = "Disk"; 				e = {$_.index -as [int]} },
					@{n = "Model"; 				e = {$_.Model -as [string]} },
					@{n = "Firmware"; 			e = {$_.FirmwareRevision -as [string]} },
					@{n = "SerialNumber"; 		e = {$_.SerialNumber -as [string]} },
					@{n = "DiskSize(GB)"; 		e = {"{0:N1}" -f( $Disk.Size / 1024MB)} }, 
					@{n = "Partitions"; 		e = {$_.Partitions -as [int] } },
					@{n = "Partition";	 		e = {$Part.index -as [int]} }, 
					@{n = "BootPartition";	 	e = {$Part.BootPartition -as [string]} }, 
					@{n = "PartionSize(GB)"; 	e = {"{0:N1}" -f ($Part.size /1024MB)} }, 
					@{n = "Blocks"; 			e = {$Part.NumberOfBlocks -as [int]} }, 
					@{n = "BlockSize";	 		e = {$Part.BlockSize -as [int]} }, 
					@{n = "LDiskName";	 		e = {$Ldisk.Caption -as [string]} }, 
					@{n = "FileSystem"; 		e = {$Ldisk.Filesystem -as [string]} }, 
					@{n = "LDiskSize";	 		e = {"{0:N1}" -f ($Ldisk.size /1024MB)}}, 
					@{n = "LDiskFree";	 		e = {"{0:N1}" -f ($Ldisk.FreeSpace /1024MB)}}

But Is still have no Idea why. I thought maybe it wasn’t ready iterating through all the possible options after all it continu’s down the pipeline as soon as a match is found. So that’s why I threw all the % in and it works without errors.

Anyone a clue?

You can pipe to foreach, but not to gwmi. Why are you using -pipelinevariable? You can just do things like:

$Ldisk = gwmi cim_LogicalDisk | where {$_.DriveType -eq '3'}

I use the PipelineVariable for three reasons:

  1. It stores more information than the OutVariable
  2. It cleans itself up when the code has run
  3. I want to fully understand pipelining

Of course I know about $a = (something I put in here) And I used that to start with but I ran into the fact that I needed to address four different datasources or objects and wanted my own formatted output. If there is a way to do just that with a single line of code please enlighten me. Furthermore I learned that, for some reason, I cannot use $collection = @() so I was kind a forced to do it this way.

Btw am I really piping to GWMI when I use this command:

gwmi cim_DiskDrive | where {$_.Index -eq $Part.DiskIndex}

My output from the former pipe element is stored into a variable and all I do is reading from that variable and It works so why are you telling me it doesn’t work. Again I’m not trying to be a smartass, I am way to much of a novice for that, but I merely try to understand.

It’s not about the fish you get but it’s about learning to fish on you own.

No, but any time gwmi is second in the pipeline, you’re piping to it (gwmi Cim_DiskPartition and gwmi cim_DiskDrive in the original code). Because you’re using -pipelinevariable, you have to put in all those extra foreach’s or %'s to access them. You might try using get-ciminstance instead of get-wmiobject. It has more support for pipe input (and tab completion).

There’s a few fundamental points to address.
First don’t use aliases unless you’re working interactively at the PowerShell prompt.

Second use parameter names rather than positional parameters so you, and everyone else you’re asking for help, understands what’s actually happening.

Third I’m not convinced that the use of pipeline variables is actually gaining you anything

Fourth - why are you using the CIM_XXX classes rather than the Win32_XXX equivalent. CIM_XXX is the industry standard class created when CIM was invented. Win32_XXX is Microsoft’s implementation of that class when in introduced CIM to Windows as WMI and often has extra features

Fifth - the WMI cmdlets were effectively replaced by the CIM cmdlets in PowerShell v3. You should use the CIM cmdlets for preference. Depending on your version of windows you

Sixth - why divide by 1024MB when you could use 1GB which is more understandable.

Seventh - I don’t think you need the @() round your code. PowerShell automatically creates collections / arrays as it needs to

Eighth - when filtering CIM objects use the -Filter parameter on the CIM/WMI cmdlets rather than where-object

Ninth - I’m not sure you need the casts in your select statements - unless you’re deliberately changing the type you shouldn’t need it

I’ll have a look at you code and try to get a working version for you tomorrow - its late at night where I live! I think you’re going to need to use WMI associations rather than a pipeline.

I’m sure I looked at something like this when I wrote PowerShell and WMI (www.manning.com)

You’re using CIM_LogicalDiskBasedOnPartition to supply the link from logical disk to partition - there is an easier way. I’m going to write this up a blog post. I’ll post the link when I’m done

The more I look at your output the more convinced I am that you want to start at the physical disk and show the partitions and then logical disks. I’ll work it both ways

Hi Richard,

thanks for taking the time to help me out here. Let me answer your remarks and questions.

first. I’m aware of that. the code was never meant for the public and I was in fact creating a one-line command on the command line when I ran into problems, I formatted the code using PowerGui (I still think debugging in PowerGui is awesome) and went from there.

Second, I tried using paramaters but couldn’t figure out how to use them in a one-liner

Third, It was the only way I got something out of that line of code so to me it gained me something.

Fourth, I was keeping in mind that I might want to try this code on none microsoft machines.

Fifth, PowerGui stopped at version 3. So I thought those commands were not available in PowerGui. My wrong, in the meantime I loaded the modules and changed my code to get-ciminstance.

Sixth, me goofing around with different options and never setting them back.

Seventh, you are right I don’t need it but I was experimenting around so what can I say.

Eight, I had struggles with the -filter parameter when it came down to reading values from the Antecedent property. So I changed them all to the Where clause to get some uniformity.

Ninth, you are right, several of them are unnecessary but it’s easier on the eye when the all look the same.

Keep in mind that I don’t really need the code for anything except for learning and understanding. I am really looking forward to your blog about this topic though and hope to learn something from it. When I started this line of code I too thought that I should start from the disk but when I came to think of it (keeping data normalisation in mind) I felt that I had to start with the volumes. After all there can only be one partition per volume and only one disk per partition. But hey, I’m just a novice Like I said, and I’m anxious about your approach.

In the meantime I found that there is a Win32 variant of the cim_LogicalDiskBasedOnPartition named Win32_LogicalDiskToPartition.

Thanks again for sticking up with me.

Here’s the blog post I promised


Was going to write this as a comment to you blog but it errored out.
What an awesome post. Now I understand Cim class association (at least a bit).
I loved what you did with the props, creating an object like that. Never saw it done like that before.
Is there a reason why you use a mix of -ResultClass and -ResultClassName? You seem to do it consistently
so I thought I ask. I tried it and it runs fine whichever you pick.

Thanks again