Three Easy Ways to Use PowerShell and WQL to Get WMI Data

Three Easy Ways to Use PowerShell and WQL to Get WMI Data

  • Comments 10
  • Likes

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using WMI Query Language and Windows PowerShell to get WMI data.

Hey, Scripting Guy! Question Hey, Scripting Guy! What is WQL? It sounds like SQL, but that does not always mean very much. I mean, General Electric and General Motors sound alike, but they do not have much in common. So back to my question, do you know what WQL is?

—WS

Hey, Scripting Guy! Answer Hello WS,

Microsoft Scripting Guy, Ed Wilson, is here. Things are all in a flutter here in Charlotte, North Carolina in the United States. Our weather has been abysmal. It has been hovering around 105 degrees Fahrenheit (40.5 degrees Celsius, according to my conversion module). With all this heat, we began dreaming of cool places, and so we booked a trip to Germany. Unfortunately, it will not be until November, but at least we will escape the heat and get a chance to see many of our Windows PowerShell friends who we have met over the years and those who we want to meet. We already have three dates lined up in some of our favorite cities. Tonight, the best we can do is go out for dinner and a movie with Brian (one of our Charlotte Windows PowerShell User Group friends).

WS, of course I know what WQL is—I wrote the book on WMI: Microsoft Windows Scripting with WMI: Self-Paced Learning Guide. I am also contributing a chapter to a new book about Windows PowerShell and SQL Server 2012 written by Laerte Junior, Bob Beachumin, and Mark Broadbent.

WMI Query Language

Here is one of those “bad news” things. WQL is WMI Query Language. WMI stands for Windows Management Instrumentation. So one would think that WQL really is Windows Management Instrumentation Query Language; but it probably is not. There is documentation on MSDN, for example, WQL (SQL for WMI), but it is more or less COM based, and while it is comprehensive, it is more extensive than a typical Net Admin or IT Pro would need to do. The basic WQL statements that you need to know are Select, From, and Where.

Using the Select statement

A typical WMI query begins by using the Select statement to choose everything or only a few properties from a WMI class. To choose all properties from a WMI class, you use the asterisk (“*”). After you have selected the properties (one or more properties, or all of them), you use the From keyword to list which WMI class to query. The following script creates a WQL query that chooses all properties from the Win32_Bios WMI class:

$query = "Select * from Win32_Bios"

To choose a single property from the WMI class, you place the property name after the Select keyword and before the From keyword. In the following query, only the name of the BIOS is selected from the Win32_Bios WMI class.

$queryName = "Select Name from Win32_Bios"

If you want to select more than one property, you separate the property names by commas. Therefore, the following WMI query chooses the name and the version from the Win32_Bios WMI class.

$queryNameVersion = "Select name, version from WIn32_Bios"

Using the WQL query

So how do you use the WQL query to retrieve information from a system? In Windows PowerShell 2.0, there are two main ways to do this. The first is to use the Get-WmiObject cmdlet, and the second is to use the [wmisearcher] type accelerator. The [wmisearcher] type accelerator creates a ManagementObjectSearcher class. The ManagementObjectSearcher class is documented on MSDN, but the thing you really need to know how to use is the Get method. This will be discussed later in this section. In Windows PowerShell 3.0, the CIM cmdlets also accept a WQL query. I will talk about using the CIM cmdlets at a later date.

Query by using the Get-WmiObject cmdlet

The first way is to use the WQL query with the Get-WmiObject cmdlet. To do this, you use the Query parameter. The command is shown here:

PS C:\> $query = "Select * from Win32_Bios"

PS C:\> Get-WmiObject -Query $query

SMBIOSBIOSVersion : 8BET56WW (1.36 )

Manufacturer      : LENOVO

Name              : Default System BIOS

SerialNumber      : R9FPY3P

Version           : LENOVO – 1360

Let’s use our other queries to see how they fare. First, select only the name of the bios as shown here.

PS C:\> $queryName = "Select Name from Win32_Bios"

PS C:\> Get-WmiObject -Query $queryName

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 1

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

Now, let’s choose the name and version query and use it.

PS C:\> $queryNameVersion = "Select name, version from WIn32_Bios"

PS C:\> Get-WmiObject -Query $queryNameVersion

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 2

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

Version          : LENOVO - 1360

Query by using the [wmisearcher] type accelerator

By using the [wmisearcher], you gain easy access to the ManagementObjectSearcher .NET Framework class. This provides you not only with the ability to query WMI for information, but also to configure the way that query is conducted. There are three basic steps to using the [wmisearcher] type accelerator.

Just the steps...

To use the [wmisearcher] type accelerator

  1. Put the WQL query in a variable.
  2. Use the [wmisearcher] to cast the WQL string into a ManagementObjectSearcher object.
  3. Call the Get method from the ManagementObjectSearcher object.

This may sound more complicated than it is. In fact, it is very simple. Here is an example of using WQL to return all of the properties from the Win32_Bios class.

Note   Not all of the properties display due to the format*XML file that specifies which properties to return.

PS C:\> $query = "Select * from Win32_Bios"

PS C:\> $bios = [wmisearcher]$query

PS C:\> $bios.Get()

 

SMBIOSBIOSVersion : 8BET56WW (1.36 )

Manufacturer      : LENOVO

Name              : Default System BIOS

SerialNumber      : R9FPY3P

Version           : LENOVO – 1360

In the same way that WQL reduces the properties selected for use with the Get-WmiObject cmdlet, the same methodology can be used with the [wmisearcher] type accelerator. In the example shown here, only the Name property returns from the query.

PS C:\> $queryName = "Select Name from Win32_Bios"

PS C:\> $biosname = [wmisearcher]$queryName

PS C:\> $biosname.Get()

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 1

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

But, rather than storing the string in a variable, then casting the string into a ManagementObjectSearcher type, and then calling the Get method, you can skip one of the steps and still have decent readability. This works because in the previous example, the WQL string was stored in a variable. The variable was then cast into the ManagementObjectSearcher type, and then the method was called. The code shown here skips one of the steps and casts the string directly to the object, and then stores the resulting object in the variable.

PS C:\> $biosname = [wmisearcher]"Select name from win32_bios"

PS C:\> $biosname.Get()

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 1

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

A more interesting way of doing this (and perhaps a bit more readable) is to leave the WQL string on the right side of the equality operator, and perform the cast to ManagementObjectSearcher on the variable. This code is even cleaner, and it allows for easier modification of the WQL query.

PS C:\> [wmisearcher]$biosname = "Select name from win32_bios"

PS C:\> $biosname.Get()

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 1

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

We can even perform this query in a single line. This works because the [wmisearcher] casts the WQL string to a ManagementObjectSearcher. From that object the Get method is available. By using the parentheses () to group the cast to the ManagementObjectSearcher first, the Get method becomes available.

PS C:\> ([wmisearcher]"Select name from win32_bios").get()

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 1

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

Using the query directly

Of course, you do not have to store your query in a variable. You can type the query directly into the Query position in your command. Although this may have advantages in simplicity (you have one command instead of two), it also makes your code more difficult to read. This approach is shown here:

PS C:\> Get-WmiObject -Query "Select * from win32_bios"

 

SMBIOSBIOSVersion : 8BET56WW (1.36 )

Manufacturer      : LENOVO

Name              : Default System BIOS

SerialNumber      : R9FPY3P

Version           : LENOVO - 1360

WS, that is all there is to using WQL. WMI Week will continue tomorrow when I will talk about limiting information that is returned by the WQL query. I will also talk about all those extra ­­system properties that were returned by WMI when we attempted to limit the properties.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • That's pretty awesome, I've not seen the [wmisearcher] out in the wild, I assume most everyone likes the Get-WmiObject better, but that is really cool.

  • @Jeffrey S. Patton Thank you! Yes, when I first saw the [wmisearcher] type accelerator back during early beta of PowerShell 2.0 I thought it was pretty redundant and that the only reason we exposed it was "because we could." But I have layed around with it, and it makes for some interesting code. For example, I have one script I wrote that looks like this:

    $arrayWMIQueries = "select * ...","Select * ...","etc ..."

    $arrayWmiQueries | foreach { [wmisearcher]$_}

    It is a great way to perform a series of WMI queries ... and is pretty readable as well.

  • @Ed Wow...it has literally never occurred to me to put a pile of WMI queries into an array...I have an inventory script that queries several different classes, so there is a big block of Get-WmiObject...it's time to revisit that script ;-)

  • @Jeffrey S. Patton yes, using an array to hold a variety of WQL queries is a really cool technique. One thing I did a few years ago, was store the WQL queries in a database. Based upon certain selection criteria (WMI filtering) I was able to choose WHICH WQL queries to run on the remote systems. Because you array can simply be one LARGE string that you split at the comma ... this is trivial. I am glad you are enjoying the series.

  • So I'm starting to mess around and I have this.

    $Queries = @("SELECT SerialNumber FROM Win32_Bios","SELECT UserName FROM Win32_ComputerSystem")

    foreach ($query in $Queries)

    {

       Get-WmiObject -Query $query

       }

    The resulting output contains all the extra __propname stuff and I'd like to just get the actual SerialNumber or UserName out. I've tried several things

    Get-WmiObject -Query $query |where-Object {$_ -notlike '__*'}

    Get-WmiObject -Query $query |where-Object {$_ -notmatch '__*'}

    Get-WmiObject -Query $query |Select-Object -ExcludeProperty '__*'

    Normally I might do something like this

    (Get-WmiObject -Query 'SELECT SerialNumber FROM Win32_Bios').SerialNumber

    I haven't quite gotten in my mind how I want to apply this, but at the moment I'm stuck since the return is not an object that I am able to deal with.

  • Easy -

    $queries=@("SELECT SerialNumber FROM Win32_Bios","SELECT UserName FROM Win32_ComputerSystem")

    $queries|

       %{

         (Get-WmiObject -query $_ ).Properties | select name, value

        }

  • @jrv, you are so awesome!

  • @Jeffrey S. Patton This is one of the things I will talk about this weekend ... dealing with the extra information that returns from a WMI query.

  • the variety of possible query techniques is awesome!

    Good to know that we can choose from these!

    Klaus.

  • @K_Schulte Yes, to some extent you get to use the method that is most comfortable and is easiest for you to use. At times, one method will be easier than an other.