WMI queries in powershell 'Invalid query' - powershell

Trying to learn more about WMI and powershell(noob) commands.
Running this:
Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding
Gets me this(fine):
__GENUS : 2
__CLASS : __FilterToConsumerBinding
__SUPERCLASS : __IndicationRelated
__DYNASTY : __SystemClass
__RELPATH : __FilterToConsumerBinding.Consumer="NTEventLogEventConsumer.Name=\"SCM Event Log Consumer\"",Filter="__EventFilter.Name=\"SCM Event Log Filter\""
__PROPERTY_COUNT : 7
__DERIVATION : {__IndicationRelated, __SystemClass}
__SERVER : COMPUTERNAME
__NAMESPACE : ROOT\Subscription
__PATH : \COMPUTERNAME\ROOT\Subscription:__FilterToConsumerBinding.Consumer="NTEventLogEventConsumer.Name=\"SCM Event Log Consumer\"",Filter="__EventFilter.Name=\"SCM Event Log Filter\""
Consumer : NTEventLogEventConsumer.Name="SCM Event Log Consumer"
CreatorSID : {1, 2, 0, 0...}
DeliverSynchronously : False
DeliveryQoS :
Filter : __EventFilter.Name="SCM Event Log Filter"
MaintainSecurityContext : False
SlowDownProviders : False
PSComputerName : COMPUTERNAME
Why does this query give me the same result as above:
Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding -Filter "__PATH LIKE '%SCM%'"
But this one, looking for the text in 'Filter' :
Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding -Filter "Filter LIKE '%SCM%'"`
gives me an invalid query error
Get-WMIObject : Invalid query "select * from __FilterToConsumerBinding where Filter LIKE '%SCM%'"
At line:1 char:1
+ Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerB ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-WmiObject], ManagementException
+ FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
I don't get it why doesn't the same query work on both objects?
Thanks!
Doesn't work on EventConsumer either but it does work on EventFilter!
Get-WMIObject -Namespace root\Subscription -Class __EventFilter -Filter "Name LIKE '%SCM%'"
__GENUS : 2
__CLASS : __EventFilter
__SUPERCLASS : __IndicationRelated
__DYNASTY : __SystemClass
__RELPATH : __EventFilter.Name="SCM Event Log Filter"
__PROPERTY_COUNT : 6
__DERIVATION : {__IndicationRelated, __SystemClass}
__SERVER : COMPUTERNAME
__NAMESPACE : ROOT\Subscription
__PATH : \\COMPUTERNAME\ROOT\Subscription:__EventFilter.Name="SCM Event Log Filter"
CreatorSID : {1, 2, 0, 0...}
EventAccess :
EventNamespace : root\cimv2
Name : SCM Event Log Filter
Query : select * from MSFT_SCMEventLogEvent
QueryLanguage : WQL
PSComputerName : COMPUTERNAME

Short answer... your ability to filter on a property depends on what kind of WMI Class it is and what the property types are.
Long answer... To get more information about a WMI class in PowerShell, use the Get-CimClass cmdlet. Properties can have different value types based on the data they return (ie String, Integers, Boolean). In your example, you are trying to query the property "Filter" which is a reference type. A reference type doesn't have a literal value, it is just a reference to an instance from another class. You can tell a reference property because it shows a full or partial path to another class (as you saw in your example "__EventFilter" was referenced. You can also get the property type information from this powershell command:
Get-CimClass -Namespace "root\Subscription" -Class __FilterToConsumerBinding | Select-Object -ExpandProperty CimClassProperties | Where-Object {$_.Name -eq 'Filter'}
Reference type properties show up on WMI classes that have a class qualifier called "Association" set to true. Class qualifiers are properties for the class itself that dictate how the class behaves. You can see all the class qualifiers using this command.
Get-CimClass -Namespace "root\Subscription" -Class __FilterToConsumerBinding | Select-Object -ExpandProperty CimClassQualifiers
Unfortunately, Since WQL is a query language you cannot filter instances of a class based on properties with a reference type using a WHERE clause. You can only use simple types such as a string, integer, or property of an embedded object (embedded objects are extremely rare though).
Since the class __EventFilter is whats referenced by the "Filter" property, and that class DOES has simple property types we can start from there. You can query __EventFilter by the Name property, which is a string type, without any problem.
There are some advanced query techniques (ASSOCIATORS OF and REFERENCES OF) that can help you get the instance from __FilterToConsumerBinding with queries based on references but its a multi-step process. I'd recommend checking out the Microsoft Doc on it Microsoft Doc WQL but here is a basic two step PowerShell command that will first get the name of the referenced instance then any instances that reference it.
$InstanceName = (Get-WmiObject -Namespace "ROOT\subscription" -Query "SELECT * FROM __EventFilter WHERE Name LIKE '%SCM%'").Name
Get-WmiObject -Namespace "ROOT\subscription" -Query "REFERENCES OF {__EventFilter.Name='$InstanceName'}"
Since REFERENCES OF will search an entire namespace you can narrow your results down to a specific class by adding a WHERE clause and naming the ResultClass as your target class. The revised second line of your command would now be
Get-WmiObject -Namespace "ROOT\subscription" -Query "REFERENCES OF {__EventFilter.Name='$InstanceName'} WHERE ResultClass = __FilterToConsumerBinding"
This should return the instance you are looking for only from __FilterToConsumerBinding. (*Note that there are no quotes around the class name. Including double or single quotes will give an invalid query error.
To answer your question about the __EventConsumer class, this brings up another WMI concept called abstract classes. I won't go into too much detail about this but an abstract class is essentially a base class (starting point for other classes) that other classes can inherit. Unlike standard base classes, though, abstract classes can not have its own instances. You can find out if a class is abstract by getting the class qualifiers of the __EventConsumer class. You'll notice if you run the command below, you will see it has the "abstract" class qualifier.
Get-CimClass -Namespace "root\Subscription" -Class __EventConsumer | Select-Object -ExpandProperty CimClassQualifiers
Since an abstract class can't contain any instances itself, if you enumerate it, it will return instances for all the classes that use it as a base class. In your example, __EventConsumer is actually yielding an instance from NTEventLogEventConsumer. Since enumerating an abstract class returns a number of different classes there would not always be the promise of the same properties to filter by so filtering is not allowed unless you are filtering on a property that is contained in the base class. To see what the base class of a WMI class is, you can use this command and see that the base class for NTEventLogEventConsumer is __EventConsumer.
Get-CimClass -Namespace "root\Subscription" -Class NTEventLogEventConsumer | Select-Object -ExpandProperty CimSuperClassName
You can usually enumerate all instances from an abstract class using the query SELECT * FROM __EventConsumer. This will show all instances but once you add a WHERE clause with a property not included in the base class, you either receive invalid query or no instances will be returned. This is the reason why you can directly query NTEventLogEventConsumer but not __EventConsumer.
Hope this helps.

Related

Powershell Call cancelled

I was running Get-CimInstance -Namespace root/CIMV2 -ClassName CIM_Component | Out-File -Encoding utf8 .\CIM_Component.txt and received
Get-CimInstance : Call cancelled
At line:1 char:2
+ (Get-CimInstance -Namespace root/CIMV2 -ClassName CIM_DirectoryContai ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-CimInstance], CimException
+ FullyQualifiedErrorId : HRESULT 0x80041032,Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand
This was not cancelled from a keyboard interrupt. It simply... timed out?
I really don't know why it cancelled at all.
I was hoping someone else might know what happened that it threw the Call cancelled error.
It may simply be that the amount of information is just too much going threw the pipe that it... but, its not a out of memory error...
I have no idea!
***strong text***Hummm… See the below for potential edification...
CIM_Component class
I saw a question about the CIM_Component class and wondered what it
was. So I tried it
Get-CimInstance -Namespace root\CIMV2 -ClassName CIM_Component | select -f 1 | fl *
# Results
GroupComponent : Win32_Directory (Name = "<directory path>")
PartComponent : CIM_DataFile (Name = "<file path>”)
PSComputerName :
CimClass : root/CIMV2:CIM_DirectoryContainsFile
CimInstanceProperties : {GroupComponent, PartComponent}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
What you’ll get is a list of every file on your system – showing
folder in the GroupComponent (parent) and File in the PartComponent
(child).
The documentation is totally accurate and totally useless as it
explains that you get a parent-child relationship but not what’s in
it!
I’d strongly recommend against using this class – there are easier
ways to get the information.

How to get specified rows from the returns of CimInstance type in PowerShell

I am trying to get specified rows from a CimInstance object and I thought to turn them to string, but did not find the answer how to do that.
PS C:\Users\laptop> Get-CimInstance -Namespace root/WMI -ClassName WmiMonitorBrightness
Active : True
CurrentBrightness : 50
InstanceName : DISPLAY\LEN40A1\4&342c5d56&0&UID67568640_0
Level : {0, 1, 2, 3...}
Levels : 101
PSComputerName :
I need to get two rows, "CurrentBrightness : 50" and "Levels : 101", from the results on screen, but the return is a [Microsoft.Management.Infrastructure.CimInstance]. I had tried to make them to be strings though.
The "rows" you see are actually the properties of an object displayed in list form. You can select properties of any given object via the Select-Object cmdlet:
Get-CimInstance ... | Select-Object CurrentBrightness, Levels

Powershell and SCCM - Unable to make a INNER JOIN query with CIM (__GENERIC CLASS error)

I am currently working on a Powershell module to help my colleage and I to works more "seemlessly" with SCCM and script (since the SCCM Cmdlets are way too slow, and therefore unusable).
In this module, I have created a function called Get-CMCollectionsAndFolder which retrieves all the collection from SCCM (using the class SMS_Collection). I also tried to add to that list the FolderName and ContainerNodeID of those collections, as indicated in the SMS_ObjectContainerItem class.
All of my module requests are done using CIM, but for some weird reason, I can't make the JOIN query written below working with CIM, only with WMI.
So, if I run this :
Get-WmiObject -ComputerName $computername -Namespace root\sms\site_$sitecode -Query "SELECT SMS_Collection.*,SMS_ObjectContainerNode.ContainerNodeId,SMS_ObjectContainerNode.Name FROM SMS_ObjectContainerItem INNER JOIN SMS_Collection ON SMS_Collection.SiteID = SMS_ObjectContainerItem.InstanceKey INNER JOIN SMS_ObjectContainerNode ON SMS_ObjectContainerNode.ContainerNodeID = SMS_ObjectContainerItem.ContainerNodeID"
I'm getting an array of object, containing thoses properties :
TypeName : System.Management.ManagementObject#root\sms\site_IP3\__GENERIC
Name MemberType Definition
---- ---------- ----------
PSComputerName AliasProperty PSComputerName = __SERVER
SMS_Collection Property System.Management.ManagementObject#SMS_Collection SMS_Collection {get;set;}
SMS_ObjectContainerNode Property System.Management.ManagementObject#SMS_ObjectContainerNode SMS_ObjectContainerNode {get;set;}
__CLASS Property string __CLASS {get;set;}
__DERIVATION Property string[] __DERIVATION {get;set;}
__DYNASTY Property string __DYNASTY {get;set;}
__GENUS Property int __GENUS {get;set;}
__NAMESPACE Property string __NAMESPACE {get;set;}
__PATH Property string __PATH {get;set;}
__PROPERTY_COUNT Property int __PROPERTY_COUNT {get;set;}
__RELPATH Property string __RELPATH {get;set;}
__SERVER Property string __SERVER {get;set;}
__SUPERCLASS Property string __SUPERCLASS {get;set;}
ConvertFromDateTime ScriptMethod System.Object ConvertFromDateTime();
ConvertToDateTime ScriptMethod System.Object ConvertToDateTime();
I then only have to "expand" the SMS_Collection and SMS_ObjectContainerNode property to get my info, which isn't that bad.
But when I try the same query and replace Get-WMIObject with Get-CIMInstance, I obtain that error :
Get-CIMInstance : The WS-Management service cannot process the request. The class __GENERIC does not exist in the
root/sms/site_SMS namespace.
At line:1 char:1
+ Get-CIMInstance -ComputerName $computername -Namespace root\sms\site_ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [Get-CimInstance], CimException
+ FullyQualifiedErrorId : HRESULT 0x80338000,Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand
I suppose there something obvious that I'm missing, but I clearly don't see. Can someone give me a solution and/or the reason of this behavior? I'm a noob in WMI and CIM, only working with those in the last 3 weeks so I still got a lot to learn.
Thanks!

Pass array of CimInstance to CimMethod

I am working with WMI API through Cim cmdlets. The problem is that I can't figure out how to pass wmi object to wmi method that accepts array of wmi objects.
Here are method parameters definition:
Name CimType Qualifiers
---- ------- ----------
Path String {ID, in}
Permissions InstanceArray {EmbeddedInstance, ID, in}
ResetChildren Boolean {ID, in}
Path and ResetChildren are simple parameters. They accepts simple values like "/path" and $true respectively. But I have troubles with Permissions parameter.
Here is my code
#Acquiring object that I want to pass to method
$group = Get-CimInstance -Namespace "root\VisualSVN" -ClassName VisualSVN_Group -Filter "Name='Readers'"
#Acquiring object which method will be called
$repositories = Get-CimInstance -Namespace "root\VisualSVN" -ClassName VisualSVN_Repository
#Preparing method arguments
$args = #{
Path = "/";
Permissions = #($group[0]); #Trouble here
ResetChildren = $true
}
#Invoking method with arguments
Invoke-CimMethod -InputObject ($repositories[0]) -MethodName SetSecurity -Arguments $args
Execution of this code will result in error:
Invoke-CimMethod : Unable to cast object of type 'Microsoft.Management.Infrastructure.CimInstance' to type 'M
icrosoft.Management.Infrastructure.Native.InstanceHandle'.
Parameter name: value
At C:\somepath\script1.ps1:11 char:1
+ Invoke-CimMethod -InputObject ($repositories[0]) -MethodName SetSecurity -Argume ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Invoke-CimMethod], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.Management.Infrastructure.CimCmdlets.Invoke
CimMethodCommand
If you change code
Permissions = #($group[0]); #Trouble here
To code
Permissions = $group; #Trouble here
Then error message will also change:
Invoke-CimMethod : Unable to cast object of type 'Microsoft.Management.Infrastructure.Native.InstanceHandle'
to type 'System.Collections.IList'.
Parameter name: value
At C:\somepath\script1.ps1:11 char:1
+ Invoke-CimMethod -InputObject ($repositories[0]) -MethodName SetSecurity -Argume ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Invoke-CimMethod], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.Management.Infrastructure.CimCmdlets.Invoke
CimMethodCommand
Any ideas how to pass $group to method properly?
I had exactly same problem with VisualSVN_Repository::SetSecurity method.
When working with CIM method arguments, you must cast ANY array arguments to [CimInstance[]].
For example, this worked for me:
$Everyone = Get-CimInstance -Namespace root/VisualSVN -ClassName VisualSVN_Everyone
# Grant Everyone a Read/Write access:
$AccessRule = New-CimInstance -Namespace root/VisualSVN -ClassName VisualSVN_PermissionEntry -ClientOnly -Property #{ Account = $Everyone; AccessLevel = [UInt32]2 }
$SvnRepo = Get-CimInstance -Namespace root/VisualSVN -ClassName VisualSVN_Repository -Filter "Name='MY_REPOSITORY_NAME'"
Invoke-CimMethod -InputObject $SvnRepo -MethodName SetSecurity -Arguments #{
Path = '/';
Permissions = [CimInstance[]]$AccessRule;
ResetChildren = $true
} | Out-Null
You must cast array argument to [CimInstance[]] even when it is just a single item.
P.S.: Be careful with Ref array arguments too: you must first cast it to [CimInstance[]] and then to [ref[]]. For example, when calling VisualSVN_Group::Create method:
[CimInstance[]] $SvnUsers = [CimInstance[]]($ArrayOf_VisualSVN_User_Objects)
Invoke-CimMethod -ClassName VisualSVN_Group -Namespace root/VisualSVN -MethodName Create -Arguments #{
Members = [ref[]]$SvnUsers;
Name = 'MY_NEW_GROUP_NAME'
} | Out-Null
See also: Tip#5: Passing ref and embedded instances on PowerShell Blog.
If you had a repro that did not rely on software I do not have installed, I could answer definitively, so I will make my best attempt based on what I can infer from your question.
To make what you are assigning to Permissions an array, and you are only assigning one value, use a comma in front.
Permissions = ,$group
Here is some sample script and the output to demonstrate.
Script
$var1 = "any value"
$var2 = ,$var1
$var1.GetType()
$var2.GetType()
$var2.Count
$var2[0]
Output
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
True True Object[] System.Array
1
any value
A possible solution (it worked for me with a similar issue that led me here) based on the 'hint' that CIM are inert objects. In my case it was installing SCCM updates in a scripted manner while allowing for reboots if the updates required them, and stopping if the entire process took more than a certain amount of time (to not go past the available maintenance window).
I was passing a CIMUpdate object through to a powershell function to perform the update to allow for rebooting if necessary between updates (Perform a single update at a time, verify if reboot is needed, then try the next update).
The CIM object was giving a Type Mismatch error when forcing it to cast as [CIMInstance[]], and if not casting at all I would get the same Unable to cast object of type 'Microsoft.Management.Infrastructure.CimInstance' to type 'Microsoft.Management.Infrastructure.Native.InstanceHandle' error message.
Solution was to use that CIMUpdate and re-query to get a live version of the object within the function and then use that new object as the argument to the CIMMethod.
as of now, it works for me , the case I am working on is passing New-ScheduledTaskTrigger instance to a function.
I am passing a CimInstance to a function and receiving it as PSCustomObject type.
Send CimInstance receive as [PSCustomObject] -> at target it remains CimInstance object type
Send CimInstance receive as [CimInstance] -> at target it remains CimInstance object type
Send CimInstance receive as [CimInstance[]] -> at target it turn to CimInstance array type

How to get WMI object from a WMI object reference?

Similar to this question except that no answer was given with regards to the main question of getting an object from reference.
For example:
PS C:\Users\admin> Get-WmiObject -Namespace $namespace -Class $class
...
IsActive : 1
oA: \\.\ROOT\abc\abc\ABC:abc.xyz="tst2"
oB : \\.\ROOT\abc\abc\ABC:abc.xyz="tst3"
PSComputerName : admin-test2
oA and oB are references and therefore come up as strings in powershell. Is there a way I can get the object they represent using WMI query in powershell?
Assuming that oA and oB actually are strings you should be able to resolve these WMI paths to WMI objects like this:
Get-WmiObject -Namespace $namespace -Class $class | ForEach-Object {
$oA = [wmi]$_.oA
$oB = [wmi]$_.oB
}
Example:
PS C:\> $namespace = 'root/cimv2'
PS C:\> $class = 'Win32_OperatingSystem'
PS C:\> $obj1 = Get-WmiObject -Namespace $namespace -Class $class
PS C:\> $obj1
SystemDirectory : C:\Windows\system32
Organization :
BuildNumber : 7601
RegisteredUser : foo
SerialNumber : 00371-OEM-8310595-XXXXX
Version : 6.1.7601
PS C:\> $obj1.GetType().FullName
System.Management.ManagementObject
PS C:\> $obj1.Path.Path
\\FOO\root\cimv2:Win32_OperatingSystem=#
PS C:\> ($obj1.Path.Path).GetType().FullName
System.String
PS C:\> $obj2 = [wmi]$obj1.Path.Path
PS C:\> $obj2
SystemDirectory : C:\Windows\system32
Organization :
BuildNumber : 7601
RegisteredUser : foo
SerialNumber : 00371-OEM-8310595-XXXXX
Version : 6.1.7601
PS C:\> $obj2.GetType().FullName
System.Management.ManagementObject
Your question is rather vague, though, so I'm not sure if this answer actually covers what you've been asking.
As OP mentioned that all he wants is a generic answer (which is again tough given the nature of Object Paths and dependency on key), I am giving another example of using Associators Of WMI query.
$query = "ASSOCIATORS OF {Win32_Account.Name='DemoGroup2',Domain='DomainName'} WHERE Role=GroupComponent ResultClass=Win32_Account"
Get-WMIObject -Query $query | Select Name
If you need to use the example above, you need to first find out what is the key property and use that in the object path.
-----Original answer -----
What namespace? What class? You need to use associations and/or references to retrieve that. It is hard to give a generic answer unless we know the exact object path. For example,
$query = "REFERENCES OF {Win32_Service.Name='Netlogon'} WHERE ClassDefsOnly"
Get-WMIObject -Query $query
The above query will give all references of Win32_Service with an object path ServiceName='NetLogon'