Finding Disks with Unallocated Space

So i’m trying to find the easiest way to find drives that have unallocated space.

I’ve forgotten to expand a drive in the OS after the vmdk was expanded, so i’m sure there’s several others out there as well.

So far I haven’t found a good way to pull it without WMI, and I’m pretty terrible with dealing with multiple strings, so for now I have 2 separate relevance statements that seems to work for the C and D drives only.

((((it as integer) of string value of selects "Size from Win32_DiskDrive WHERE Index=0 AND size!='' " of wmi)-(sum of ((it as integer) of string values of selects "Size from Win32_DiskPartition WHERE DiskIndex=0" of wmi)))/1024/1024/1024)>1

((((it as integer) of string value of selects "Size from Win32_DiskDrive WHERE Index=1 AND size!='' " of wmi)-(sum of ((it as integer) of string values of selects "Size from Win32_DiskPartition WHERE DiskIndex=1" of wmi)))/1024/1024/1024)>1

Since they’re WMI calls and fairly expensive, I’d probably add the values to an analysis vs having a task, but having a “policy” task that automatically extends a partition sounds somewhat appealing as well.

Surprisingly, I couldn’t find any past topics discussing this, so maybe it’s unique to our environment.

It’s not elegant, but a quick hack - throw this @ your systems in question. Systems w/ no unallocated space will ignore it, disks that have unallocated space will extend…

// Disable wow64 redirection on x64 OSes
action uses wow64 redirection {not x64 of operating system}

delete __createfile

// CREATEFILE
createfile until END_OF_FILE

$MaxSize = (Get-PartitionSupportedSize -DriveLetter c).sizeMax
Resize-Partition -DriveLetter c -Size $MaxSize

END_OF_FILE

delete powershell.ps1
move __createfile powershell.ps1

waithidden { pathname of file ((it as string) of value “Path” of key “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell” of native registry) } -ExecutionPolicy Bypass -File powershell.ps1

I’ve written fixes for the issue, mainly using diskpart so I don’t have to worry about powershell versions.

My main desire is to be able to find any C/D/E/X drives with unpartitioned space instead of running a fixlet across all servers.

I hate to do this, but @JasonWalker do you have any suggestions about how to find this more elegantly?

1 Like

I’ll take a look…I seem to recall something similar actually came up here not long ago that we may be able to leverage.

I think the thread at Disk Space Inaccuracy may be helpful.

If we follow the WMI paths I think we can get somewhere with ‘select objects of “query”’. I think what you have already is very close to what we’ll need.

Ok @cjwlford I think I have something for you, and an interesting nerd-story to go along with it.

I find it easier to make relationships between two WMI objects by querying them as…objects. Here I use ‘select objects’ rather than ‘string value of selects’ because to me it’s easier to keep them separated.

As you’ve seen, we can query Win32_DiskPartition and Win32_DiskDrive. On Win32_DiskPartition, the “DiskIndex” property refers to the “Index” property of Win32_DiskDrive. Both objects have “Size” properties as well.

q: (properties "DiskIndex" of it, properties "Size" of it) of select objects "* from Win32_DiskPartition" of wmi
A: DiskIndex=0, Size=524288000
A: DiskIndex=0, Size=508478619648
A: DiskIndex=0, Size=2970615808
A: DiskIndex=1, Size=1000203091968
T: 491.991 ms
I: plural ( wmi select, wmi select )

q: (properties "Index" of it, properties "Size" of it) of select objects "* from Win32_DiskDrive" of wmi
A: Index=0, Size=512105932800
A: Index=1, Size=1000202273280
T: 475.147 ms
I: plural ( wmi select, wmi select )

On my machine, I have two physical disks. The first disk has three partitions, the second disk only one.

We can start by making a cross-product tuple of every disk to every partition…here, “item 0” and “item 1” refer to the Win32_DiskDrive properties, and item 2 contains the DiskIndex and Size properties of Win32_DiskPartition.

q: (item 0 of it, item 1 of it,(properties "DiskIndex" of it, properties "Size" of it) of select objects "* from Win32_DiskPartition" of wmi)  of (properties "Index" of it, properties "Size" of it) of select objects "* from Win32_DiskDrive" of wmi
A: Index=0, Size=512105932800, ( DiskIndex=0, Size=524288000 )
A: Index=0, Size=512105932800, ( DiskIndex=0, Size=508478619648 )
A: Index=0, Size=512105932800, ( DiskIndex=0, Size=2970615808 )
A: Index=0, Size=512105932800, ( DiskIndex=1, Size=1000203091968 )
A: Index=1, Size=1000202273280, ( DiskIndex=0, Size=524288000 )
A: Index=1, Size=1000202273280, ( DiskIndex=0, Size=508478619648 )
A: Index=1, Size=1000202273280, ( DiskIndex=0, Size=2970615808 )
A: Index=1, Size=1000202273280, ( DiskIndex=1, Size=1000203091968 )
T: 513.234 ms
I: plural ( wmi select, wmi select, ( wmi select, wmi select ) )

We’ll need to bring the DiskDrive’s “Index” property inside the tuple so we can filter for Win32_DiskPartition objects with matching DiskIndex values -

q: (item 0 of it, item 1 of it, (item 0 of it, (properties "DiskIndex" of it, properties "Size" of it) of select objects "* from Win32_DiskPartition" of wmi) ) of (properties "Index" of it, properties "Size" of it) of select objects "* from Win32_DiskDrive" of wmi
A: Index=0, Size=512105932800, ( Index=0, ( DiskIndex=0, Size=524288000 ) )
A: Index=0, Size=512105932800, ( Index=0, ( DiskIndex=0, Size=508478619648 ) )
A: Index=0, Size=512105932800, ( Index=0, ( DiskIndex=0, Size=2970615808 ) )
A: Index=0, Size=512105932800, ( Index=0, ( DiskIndex=1, Size=1000203091968 ) )
A: Index=1, Size=1000202273280, ( Index=1, ( DiskIndex=0, Size=524288000 ) )
A: Index=1, Size=1000202273280, ( Index=1, ( DiskIndex=0, Size=508478619648 ) )
A: Index=1, Size=1000202273280, ( Index=1, ( DiskIndex=0, Size=2970615808 ) )
A: Index=1, Size=1000202273280, ( Index=1, ( DiskIndex=1, Size=1000203091968 ) )
T: 462.169 ms
I: plural ( wmi select, wmi select, ( wmi select, ( wmi select, wmi select ) ) )

Now that we have the whole cross-product we can begin filtering so we keep all the partitions with their correct disks…

q: (item 0 of it, item 1 of it, (item 0 of it, (properties "DiskIndex" of it, properties "Size" of it) of select objects "* from Win32_DiskPartition" of wmi) whose (integer value of item 0 of it = integer value of item 0 of item 1 of it) ) of (properties "Index" of it, properties "Size" of it) of select objects "* from Win32_DiskDrive" of wmi
A: Index=0, Size=512105932800, ( Index=0, ( DiskIndex=0, Size=524288000 ) )
A: Index=0, Size=512105932800, ( Index=0, ( DiskIndex=0, Size=508478619648 ) )
A: Index=0, Size=512105932800, ( Index=0, ( DiskIndex=0, Size=2970615808 ) )
A: Index=1, Size=1000202273280, ( Index=1, ( DiskIndex=1, Size=1000203091968 ) )
T: 424.367 ms
I: plural ( wmi select, wmi select, ( wmi select, ( wmi select, wmi select ) ) )

Now we have the three partitions on disk 0 all together and related to disk 0, and all the (one) partitions of disk 1 in its disk. We’ll want to get those values converted into Integers so we can start adding them up together…

q: (item 0 of it, item 1 of it, integer values of items 1 of items 2 of (item 0 of it, item 1 of it, (properties "DiskIndex" of it, properties "Size" of it) of select objects "* from Win32_DiskPartition" of wmi) whose (integer value of item 0 of it = integer value of item 0 of item 1 of it) ) of (properties "Index" of it, properties "Size" of it) of select objects "* from Win32_DiskDrive" of wmi
A: Index=0, Size=512105932800, 524288000
E: The expression could not be evaluated: Windows Error 0x8002000a: Out of present range.

Avast, a problem! WMI can’t seem to handle Integers this large. I found that BigFix itself doesn’t have a problem with it, so we can take a sidestep of pulling them out of WMI as strings and have BigFix cast them into Integers instead.

q: (item 0 of it, item 1 of it, string values of items 1 of items 2 of (item 0 of it, item 1 of it, (properties "DiskIndex" of it, properties "Size" of it) of select objects "* from Win32_DiskPartition" of wmi) whose (integer value of item 0 of it = integer value of item 0 of item 1 of it) ) of (properties "Index" of it, properties "Size" of it) of select objects "* from Win32_DiskDrive" of wmi
A: Index=0, Size=512105932800, 524288000
A: Index=0, Size=512105932800, 508478619648
A: Index=0, Size=512105932800, 2970615808
A: Index=1, Size=1000202273280, 1000203091968
T: 376.431 ms
I: plural ( wmi select, wmi select, string )

q: (item 0 of it, item 1 of it, (it as integer) of string values of items 1 of items 2 of (item 0 of it, item 1 of it, (properties "DiskIndex" of it, properties "Size" of it) of select objects "* from Win32_DiskPartition" of wmi) whose (integer value of item 0 of it = integer value of item 0 of item 2 of it) ) of (properties "Index" of it, properties "Size" of it) of select objects "* from Win32_DiskDrive" of wmi
A: Index=0, Size=512105932800, 524288000
A: Index=0, Size=512105932800, 508478619648
A: Index=0, Size=512105932800, 2970615808
A: Index=1, Size=1000202273280, 1000203091968
T: 331.430 ms
I: plural ( wmi select, wmi select, integer )

The next step is to sum up all of the partitions for each drive. We do this inside the “parentheses loop” so that we can still keep each drive separate.

q: (item 0 of it, item 1 of it, sum of (it as integer) of string values of items 1 of items 2 of (item 0 of it, item 1 of it, (properties "DiskIndex" of it, properties "Size" of it) of select objects "* from Win32_DiskPartition" of wmi) whose (integer value of item 0 of it = integer value of item 0 of item 2 of it) ) of (properties "Index" of it, properties "Size" of it) of select objects "* from Win32_DiskDrive" of wmi
A: Index=0, Size=512105932800, 511973523456
A: Index=1, Size=1000202273280, 1000203091968
T: 285.579 ms
I: plural ( wmi select, wmi select, integer )

Now we can convert the Disk size to integer as well, taking the same sidestep through a string type, and figure out how much space is free (unpartitioned) on each Disk

q: (item 0 of it, item 1 of it - item 2 of it) of ((item 0 of it, (it as integer) of string value of item 1 of it, sum of (it as integer) of string values of items 1 of items 2 of (item 0 of it, item 1 of it, (properties "DiskIndex" of it, properties "Size" of it) of select objects "* from Win32_DiskPartition" of wmi) whose (integer value of item 0 of it = integer value of item 0 of item 2 of it) ) of (properties "Index" of it, properties "Size" of it) of select objects "* from Win32_DiskDrive" of wmi) 
A: Index=0, 132409344
A: Index=1, -818688
T: 150.090 ms
I: plural ( wmi select, integer )

Ooops, we really shouldn’t have negative values there. I can’t really account for it, except that maybe there is an inaccuracy in WMI when pulling partition sizes from a Bitlocker-to-Go partition that hasn’t been unlocked (as is the case for my second physical drive). So we’ll probably want to deal with “Absolute Values” to handle weird edge cases.

Finally, the following will show each physical disk, and how much free/unpartitioned space is on the disk:

q: (item 0 of it, absolute value of (item 1 of it - item 2 of it)) of ((item 0 of it, (it as integer) of string value of item 1 of it, sum of (it as integer) of string values of items 1 of items 2 of (item 0 of it, item 1 of it, (properties "DiskIndex" of it, properties "Size" of it) of select objects "* from Win32_DiskPartition" of wmi) whose (integer value of item 0 of it = integer value of item 0 of item 2 of it) ) of (properties "Index" of it, properties "Size" of it) of select objects "* from Win32_DiskDrive" of wmi) 
A: Index=0, 132409344
A: Index=1, 818688
T: 102.889 ms
I: plural ( wmi select, integer )

We probably shouldn’t bother trying to expand a partition if the disk free space is below some threshold. So the following relevance should show only those disks that have more than 500 MB of free space (I don’t have any, but you can try tuning the whose() filter at the end to test whether it works as expected, and decide your own threshold for “how much space is worth reclaiming”):

q: (item 0 of it, absolute value of (item 1 of it - item 2 of it)) of ((item 0 of it, (it as integer) of string value of item 1 of it, sum of (it as integer) of string values of items 1 of items 2 of (item 0 of it, item 1 of it, (properties "DiskIndex" of it, properties "Size" of it) of select objects "* from Win32_DiskPartition" of wmi) whose (integer value of item 0 of it = integer value of item 0 of item 2 of it) ) of (properties "Index" of it, properties "Size" of it) of select objects "* from Win32_DiskDrive" of wmi) whose (absolute value of (item 1 of it - item 2 of it) > 500 * 1024 * 1024)
T: 54.978 ms
I: plural ( wmi select, integer )

Finally this relevance will return a True/False value indicating whether the machine has any partitions worth growing. Mine doesn’t, but if I tune that ‘500 MB’ down to ‘5 MB’, I get a True:

q: exists (item 0 of it, absolute value of (item 1 of it - item 2 of it)) of ((item 0 of it, (it as integer) of string value of item 1 of it, sum of (it as integer) of string values of items 1 of items 2 of (item 0 of it, item 1 of it, (properties "DiskIndex" of it, properties "Size" of it) of select objects "* from Win32_DiskPartition" of wmi) whose (integer value of item 0 of it = integer value of item 0 of item 2 of it) ) of (properties "Index" of it, properties "Size" of it) of select objects "* from Win32_DiskDrive" of wmi) whose (absolute value of (item 1 of it - item 2 of it) > 500 * 1024 * 1024)
A: False
T: 46.235 ms
I: singular boolean

I think you should be able to use this last query as a Task/Fixlet relevance, along with the earlier one showing which disks can be grown to fill-in your Diskpart script. The final relevance seems to run reasonably quickly, which is a bonus and not always the case for WMI queries.

Hope this helps, and it was a fun journey!

2 Likes

Is there a way to modify this to show the drive letter and the unallocated space instead of the index number?

? Unallocated space won’t have a drive letter? That’s the complexity to this query, once it has a drive letter it’s allocated space…

Sorry, what I meant is there a way to show a mapping of the disk index to the allocated space and the unallocated space. For ex: in the below screenshot, I want to create a property that shows the allocated space and the unallocated space for a disk,
Output should be:
Index=1, E: 1215 GB, unallocated: 3.54 GB
Index=2, F: 49.98 GB, unallocated: 10 GB