Learn about Windows PowerShell
Summary: Microsoft Scripting Guy, Ed Wilson, talks about using WMI Query Language and Windows PowerShell to get WMI data.
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
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.
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.
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"
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.
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 :
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
__PROPERTY_COUNT : 2
Version : LENOVO - 1360
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
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:\> $bios = [wmisearcher]$query
PS C:\> $bios.Get()
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:\> $biosname = [wmisearcher]$queryName
PS C:\> $biosname.Get()
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"
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"
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()
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"
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
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.