Using PowerShell and WMI to Find Folders by File Type

We recently published an article about how to use PowerShell to find folders that contain a certain file type, such as the MP3 file format. I showed you how to search the file system using the DIR command. Although this command works fine when run locally, it might not perform very well if you want to search an entire drive. If you want to search remote computers, then querying the file system and even using PowerShell remoting with Invoke-Command is less than optimal. If you are targeting a specific root folder, it might not be that bad, but using Get-ChildItem (DIR) doesn’t scale very well, in my opinion. As an alternative, you can use Windows Management Instrumentation (WMI).

It turns out that WMI can search for files just as easily by querying instances of the CIM_Datafile class. From experience, I know that you should use the most targeted filter you can. This means you need to know property names.

get-cimclass cim_datafile | Select -ExpandProperty CIMClassProperties | Select Name,CimType | out-gridview

The out-gridview for get-cimclass. (Image Credit: Jeff Hicks)
The out-gridview for get-cimclass. (Image Credit: Jeff Hicks)

Here is an actual instance.

Status                : OK
Name                  : c:\work\a\b\demo.mp3
Caption               : c:\work\a\b\demo.mp3
Description           : c:\work\a\b\demo.mp3
InstallDate           : 1/15/2015 8:47:16 AM
AccessMask            : 18809343
Archive               : True
Compressed            : False
CompressionMethod     :
CreationClassName     : CIM_LogicalFile
CreationDate          : 1/15/2015 8:47:16 AM
CSCreationClassName   : Win32_ComputerSystem
CSName                : WIN81-ENT-01
Drive                 : c:
EightDotThreeFileName : c:\work\a\b\demo.mp3
Encrypted             : False
EncryptionMethod      :
Extension             : mp3
FileName              : demo
FileSize              : 18
FileType              : MP3 Format Sound
FSCreationClassName   : Win32_FileSystem
FSName                : NTFS
Hidden                : False
InUseCount            :
LastAccessed          : 1/15/2015 8:47:16 AM
LastModified          : 1/15/2015 8:47:26 AM
Path                  : \work\a\b\
Readable              : True
System                : False
Writeable             : True
Manufacturer          :
Version               :
PSComputerName        : WIN81-ENT-01
CimClass              : root/cimv2:CIM_DataFile
CimInstanceProperties : {Caption, Description, InstallDate, Name...}
CimSystemProperties   : Microsoft.Management.Infrastructure.CimSystemProperties

Let’s start by querying locally, again looking for folders that contain MP3 files under C:\work. Here’s my query.

Get-CimInstance CIM_DataFile -filter "Drive='c:' AND path Like '\\work\\%' AND extension='mp3'" -OutVariable m

I find that these types of queries perform best if I limit the search to a specific drive. You can also see where I’m filtering for the file extension. Note that there is no period. The trickiest part is the path component, where I’m using the LIKE operator and the wildcard (denoted by ‘%’) to retrieve any instance where the path starts with \work\. In WMI, you need to escape the backward-slash character (denoted by ‘\’), which is why I end up with \\work\\. In a WMI filter, use a wildcard instead of an asterik. My command writes results to the pipeline and saves the results to a variable, $m, so that I can look at them later without having to re-run the command.

Using Get-CimInstance in Windows PowerShell. (Image Credit: Jeff Hicks)
Using Get-CimInstance in Windows PowerShell. (Image Credit: Jeff Hicks)

Remember, my goal is to get the directory name. As you can see from my full sample instance above, you could try to parse out a value. Instead, we’ll have WMI do the work. In WMI, classes are often associated with other classes. In this case, each of these data file objects has a corresponding association to a Win32_Directory type of object. The CIM cmdlets make it very easy to retrieve this information.

Get-CimInstance CIM_DataFile -filter "Drive='c:' AND path Like '\\work\\%' AND extension='mp3'" -OutVariable m |
Get-CimAssociatedInstance -ResultClassName Win32_Directory | Select Name

Using WMI to parse out values in PowerShell. (Image Credit: Jeff Hicks)
Using WMI to parse out values in PowerShell. (Image Credit: Jeff Hicks)

Again, $m holds the results from the Get-CimInstance cmdlet. Now I have Win32_Directory objects at the end of the pipeline. I should probably filter out duplicates and sort my results.

Get-CimInstance CIM_DataFile -filter "Drive='c:' AND path Like '\\work\\%' AND extension='mp3'" |
Get-CimAssociatedInstance -ResultClassName Win32_Directory | Select Name -Unique | Sort

Win32_Directory object results. (Image Credit: Jeff Hicks)
Win32_Directory object results. (Image Credit: Jeff Hicks)

Now for the fun part. I have an expression that works locally. Let’s scale out and search remote computers. An advantage of using Get-CIMInstance is that the request goes out over WSMan, which means I only need to worry about a single port. No DCOM or SMB traffic needs to go over the wire.

I want to search for ZIP files under C:\Work on CHI-CORE01 and get the matching folder paths.

Get-CimInstance CIM_DataFile -filter "Drive='c:' AND path Like '\\work\\%' AND extension='zip'" -computername chi-core01  |
Get-CimAssociatedInstance -ResultClassName Win32_Directory | Select Name,PSComputername -Unique | Sort Name

The result of our PowerShell search. (Image Credit: Jeff Hicks)
The result of our PowerShell search. (Image Credit: Jeff Hicks)

I included the computer name in the result, so I would know where to look. I can also modify my filter and search the entire C:\ drive.

Get-CimInstance CIM_DataFile -filter "Drive='c:' AND extension='zip'" -computername chi-core01 |
Get-CimAssociatedInstance -ResultClassName Win32_Directory | Select Name,PSComputername -Unique | Sort Name

This is much faster than using the DIR command and searching C: recursively.
011515 1602 FindFolders6
If I can search one computer, I can just as easily search multiple computers.

Get-CimInstance CIM_DataFile -filter "Drive='c:' AND extension='zip'" -computername chi-core01,chi-fp02 |
Get-CimAssociatedInstance -ResultClassName Win32_Directory | Select Name,PSComputername -Unique |
Sort Name | out-gridview -title "Zip Files"

The out-gridview for zip files. (Image Credit: Jeff Hicks)
The out-gridview for zip files. (Image Credit: Jeff Hicks)

In this article, I have been using the CIM cmdlets, which assumes you are running PowerShell 3.0 or later locally and remotely.

If you don’t have access to these cmdlets, then you can use the legacy Get-WMIObject cmdlet. There’s no cmdlet for retrieving associations. Instead, you will need to invoke the GetRelated() method. I’m using the resulting class name, Win32_Directory, as a parameter to limit the results.

Get-WMIObject CIM_DataFile -filter "Drive='c:' AND extension='zip'" -computername chi-core01 |
foreach { $_.GetRelated("Win32_Directory")} |  Select Name,PSComputername -Unique | Sort Name

The result is the same WMI information.

Using the GetRelated() method in PowerShell to retrieve associations. (Image Credit: Jeff Hicks)
Using the GetRelated() method in PowerShell to retrieve associations. (Image Credit: Jeff Hicks)

With that said, hopefully you can use the CIM cmdlets. It wouldn’t take much to turn this into a PowerShell tool that you could re-use. Although I’ve been focusing on the associated Win32_Directory class, you might be interested in the CIM_Datafile class. If you have question on any of this or run into issues building your own tools, I encourage you to use the PowerShell forum on the Petri IT Knowledgebase.