I was building a more involved function the past couple of days for gathering data from a PC in the enterprise should troubleshooting be necessary. The type of company I work for doesn’t always allow us to stay on the PC and troubleshoot an item (unless it’s a total blocker) so being able to gather a broad range of data is necessary to really troubleshoot since we can’t be on the machine. Part of that function is retrieving hard drive information. I’m only gathering a few pieces of info, but since PC’s can have multiple drives I needed a good way to manage each disk separately. I could have done it with the following line of code, but as you can see from the screenshot, it’s messy and the numbers output are in bytes which isn’t useful at first glance.
gwmi -class win32_logicaldisk | Where {$_.DriveType -eq 3 -OR $_.DriveType -eq 4} | select -property DeviceID,Size,FreeSpace
The big thing is to convert those numbers into something more meaningful. I also don’t like the clumped output and wanted to be able to handle each disk separately and call any of it’s properties without additional formatting (to see what I mean, run that code and then try pulling just $var.DeviceID and you’ll see all the hashtable @ and{} characters… it’s a mess). I decided to create the hashtable myself. Since there could be multiple disks I also needed it to really be more like an object. That’s where nesting my hashtables came in. My other requirement, dynamically creating variables, comes from wanting to return all of my unknown-beforehand-number-of-disks within the same variable, but needing a way to make unique hashtable key names ($return.Disk1… , $return.Disk2… , etc). It sounds confusing, so here’s the code:
Function getHD($strComputer) { # Retrieves hard drive information $return = @{} # declare our return variable as a hashtable so it doesn't get created as a string $i = 0 # counter and dynamic part of variable for disks $hd = gwmi -comp $strComputer -class win32_logicaldisk ForEach ($disk in $hd) { If ($disk.Drivetype -eq 3 -OR $disk.DriveType -eq 4) { # only return physical hard drive and network drive types (5 is CD) $i++ $return.numDrives = $i # where I store the number of drives found so I can use that to iterate through them all later $return += @{ "Disk$i" = @{ # new hashname must be in quotes so variable will be expanded DriveLetter = $disk.DeviceID Size = $disk.Size/1048576 # Convert to MB FreeSpace = $disk.FreeSpace/1048576 # Convert to MB } } } } return $return }
So now what? I call my function by: $hdinfo = getHD $pc Then I run through that returned hashtable “object” and output the data to a file. That is just a few simple lines:
For ($i=1; $i -le $hdInfo.numDrives;$i++) { $hdvar = "`$hdinfo.disk$i" invoke-expression $hdvar | Out-file $global:consolePath\PCData\$pc\PCVitals.txt -append -noclobber }
And what does it all look like?
Name Value ---- ----- FreeSpace 1171586.5703125 DriveLetter C: Size 1437513.9921875 Name Value ---- ----- FreeSpace 1143174.359375 DriveLetter D: Size 1423843.99609375 Name Value ---- ----- FreeSpace 1034453.83984375 DriveLetter E: Size 1430727.99609375
I could do a little more formatting if I wanted, but this is good enough for me! And if I wanted to further use that data in a function it’s easy to reference an individual item with just $hdinfo.Disk1.FreeSpace or $hdinfo.Disk2.Size
You must be logged in to post a comment.
2 Responses to “Powershell: Dynamically Creating Variable Names; Nested Hashtables”
September 23rd, 2013 at 6:14 am
Interesting post!
I’ve doing something similar but I have trouble outputting my data properly because format-table doesn’t understand nested arrays or hashtables. To take your example, is it possible to output your data as follows (sorry for the lousy spacing):
Freespace DriveLetter Size
——— ———– —-
1171586.5703125 C: 1437513.9921875
1143174.359375 D: 1423843.99609375
1034453.83984375 E: 1430727.99609375
Thanks!
September 23rd, 2013 at 7:48 am
Yes it is possible, but it’s kind of confusing (at least for me). You have to build your own custom format for a table, and then pass that as the -property parameter to the format-table cmdlet. That’s a topic I’ve been wanting to post about but life keeps getting in the way of blogging here. You can check out this page: http://technet.microsoft.com/en-us/library/ee692794.aspx
Basically, you take a variable and you assign it the columns and value that should go in each column. @{} defines the format/display of each column. (although it’s not really a hashtable as it has more properties. The properties each has are Expression, Label, and width. There may be more but I haven’t used them. The Label is just the column name. In your example it would be Freespace, DriveLetter, and Size. So you would have three @{}.
I’m not sure how you’re creating your object but I would assume your custom table format would look like:
$customTable = @{Expression={$_.Freespace};Label=”Freespace”;width=30},@{Expression={$_.DriveLetter};Label=”Drive Letter”;width=30},@{Expression={$_.Size};Label=”Total Size”;width=30}
Then, you would pass your returned object to the format-table cmdlet but tell the cmdlet to use your custom format:
$returnedObject | format-table -property $customTable