I want to trigger a script when a specific USB Device is plugged in, I've researched Register-WmiEvent but I'm really confused at how to approach it correctly.
So far I have successfully isolated the device as such :
Get-WmiObject win32_PNPEntity | where {$_.Caption -eq "Lexar USB Flash Drive USB Device"}
This is the WMI object returned :
_GENUS : 2
__CLASS : Win32_PnPEntity
__SUPERCLASS : CIM_LogicalDevice
__DYNASTY : CIM_ManagedSystemElement
__RELPATH : Win32_PnPEntity.DeviceID="USBSTOR\\DISK&VEN_LEXAR&PROD_USB_FLASH_DRIVE&REV_1100\\AAEDZZ5RVJ47QS4K&0"
__PROPERTY_COUNT : 26
__DERIVATION : {CIM_LogicalDevice, CIM_LogicalElement, CIM_ManagedSystemElement}
__SERVER : P7409
__NAMESPACE : root\cimv2
__PATH : \\P7409\root\cimv2:Win32_PnPEntity.DeviceID="USBSTOR\\DISK&VEN_LEXAR&PROD_USB_FLASH_DRIVE&REV_1100\\AAEDZZ5RVJ47QS4K&0"
Availability :
Caption : Lexar USB Flash Drive USB Device
ClassGuid : {4d36e967-e325-11ce-bfc1-08002be10318}
CompatibleID : {USBSTOR\Disk, USBSTOR\RAW, GenDisk}
ConfigManagerErrorCode : 0
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
Description : Lecteur de disque
DeviceID : USBSTOR\DISK&VEN_LEXAR&PROD_USB_FLASH_DRIVE&REV_1100\AAEDZZ5RVJ47QS4K&0
ErrorCleared :
ErrorDescription :
HardwareID : {USBSTOR\DiskLexar___USB_Flash_Drive_1100, USBSTOR\DiskLexar___USB_Flash_Drive_, USBSTOR\DiskLexar___, USBSTOR\Lexar___USB_Flash_Drive_1...}
InstallDate :
LastErrorCode :
Manufacturer : (Lecteurs de disque standard)
Name : Lexar USB Flash Drive USB Device
PNPClass : DiskDrive
PNPDeviceID : USBSTOR\DISK&VEN_LEXAR&PROD_USB_FLASH_DRIVE&REV_1100\AAEDZZ5RVJ47QS4K&0
PowerManagementCapabilities :
PowerManagementSupported :
Present : True
Service : disk
Status : OK
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : P7409
PSComputerName : P7409
How should I approach the event part?
Is there a way to write it so it works like "When the instance containing that Caption exists...do this" ?
I'm trying :
$query = "Select * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'win32_PNPEntity' and TargetInstance.Caption = 'Lexar USB Flash Drive USB Device'"
Register-WMIEvent -Query $query -Action { Write-Host "LEXAR FLASH DRIVE CONNECTED"} -SourceIdentifier TEST
But nothing happens when I plug/unplug it.
I experimented with:
$query = "SELECT * FROM win32_DeviceChangeEvent"
Register-WMIEvent -Query $query -Action {Write-Host "ALERT"}
This work but it triggers when ANY device is connected/disconnected. I want to be able to isolate just that device with the Lexar caption.
Much appreciated.
There is a lot of documentation online about WMI events but nothing really clear for something like this so here is how I got it working, I'm sure it'll be useful for many others.
What you want to do is register an event when the instance of your device is CREATED and when it's DELETED. (in our case this is synonymous to PLUGGED IN and UNPLUGGED)
So you have to first find the ID of the instance that gets created when you plug in the device. Mine is:
Win32_PnPEntity.DeviceID="USBSTOR\\DISK&VEN_LEXAR&PROD_USB_FLASH_DRIVE&REV_1100\\AAEDZZ5RVJ47QS4K&0"
*That device ID was causing me trouble (I think because of all the sepcial characters in it) so I did a match with parts of the string instead of the whole thing, it works just as well.
Here is how to create the two events:
#Event when plugged in (InstanceCreationEvent)
$query = "Select * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'win32_PNPEntity' and TargetInstance.DeviceID like 'USBSTOR%LEXAR%AAEDZZ5RVJ47QS4K%'"
Register-WMIEvent -Query $query -Action { msg * lexar connected} -SourceIdentifier LexarConnect
#Event when disconnected (InstanceDeletionEvent)
$query = "Select * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'win32_PNPEntity' and TargetInstance.DeviceID like 'USBSTOR%LEXAR%AAEDZZ5RVJ47QS4K%'"
Register-WMIEvent -Query $query -Action { msg * lexar disconnected} -SourceIdentifier LexarDisconnect
Like I said above, the DeviceID was creating errors so I used parts of the string with the WQL wildcard character "%" (using : like USBSTOR%LEXAR%AAEDZZ5RVJ47QS4K% matches the good DeviceID without having to use the whole string)
WMI Events are powerful stuff! Enjoy!
Related
trying to get some basic stuff scripted. When I run some commands in a powershell window I get a nice simple list however if I run a .ps1 file with the same command I get an unnecessarily verbose output. Is there a "less verbose" flag or setting I'm not aware of?
while this issue in particular is likely not tied to the commands I'm using, for reference I am using this command in this instance:
invoke-command -computername $servers -scriptblock {Get-Package -Name PSWindowsUpdate}
EDIT: For future readers - I solved the issue by inserting Select-Object Name as well. This provides a concise list:
invoke-command -computername $servers -scriptblock {get-package -name pswindowsupdate | select-object name}
It doesn't format the same over invoke-command. It's a serialized object now, so the type is different. I usually do
icm $comps { get-package program | select name,version }
You won't even see the output of select-string without extra help:
icm $comps { echo hi | select-string hi | select line }
Those without multiple computers can try invoke-command with localhost at the elevated powershell prompt. All the object properties get dumped because the object's serialized.
invoke-command localhost { get-package 'google chrome' }
PropertyOfSoftwareIdentity : PropertyOfSoftwareIdentity
PSComputerName : localhost
RunspaceId : d843e737-577c-4d04-be21-7ab608694c79
FastPackageReference : {E9AB118B-2341-3DD2-BD45-27B55F5F3802}
ProviderName : msi
Source :
Status : Installed
SearchKey : google chrome
FullPath :
PackageFilename : ?
FromTrustedSource : False
Summary :
SwidTags : {Microsoft.PackageManagement.Packaging.SoftwareIdentity}
CanonicalId : msi:Google Chrome/108.0.5359.99
Metadata : {ProductCode}
SwidTagText : <?xml version="1.0" encoding="utf-16" standalone="yes"?>
<SoftwareIdentity
name="Google Chrome"
version="108.0.5359.99"
versionScheme="multipartnumeric"
tagId="E9AB118B-2341-3DD2-BD45-27B55F5F3802"
xmlns="http://standards.iso.org/iso/19770/-2/2015/schema.xsd">
<Meta
ProductCode="{E9AB118B-2341-3DD2-BD45-27B55F5F3802}" />
</SoftwareIdentity>
Dependencies : {}
IsCorpus :
Name : Google Chrome
Version : 108.0.5359.99
VersionScheme : multipartnumeric
TagVersion :
TagId : E9AB118B-2341-3DD2-BD45-27B55F5F3802
IsPatch :
IsSupplemental :
AppliesToMedia :
Meta : {{ProductCode}}
Links : {}
Entities : {}
Payload :
Evidence :
Culture :
Attributes : {name,version,versionScheme,tagId}
I am trying to get all camera devices ID and respective names using PowerShell command line. I tried several commands, but nothing has done what I am aiming. Below my "best" approach:
Get-CimInstance Win32_PnPEntity | where caption -match 'camera'
output
Caption : Remote Desktop Camera Bus
Description : UMBus Enumerator
InstallDate :
Name : Remote Desktop Camera Bus
Status : OK
Availability :
ConfigManagerErrorCode : 0
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
DeviceID : UMB\UMB\1&841921D&0&RDCAMERA_BUS
ErrorCleared :
ErrorDescription :
LastErrorCode :
PNPDeviceID : UMB\UMB\1&841921D&0&RDCAMERA_BUS
PowerManagementCapabilities :
PowerManagementSupported :
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : DESKTOP
ClassGuid : {4d36e97d-e325-11ce-bfc1-08002be10318}
CompatibleID :
HardwareID : {UMB\UMBUS}
Manufacturer : Microsoft
PNPClass : System
Present : True
Service : umbus
PSComputerName :
I know, for example, that generally the integrated camera has a name "integrated camera" with a ID "0". But this is not what is being shown.
Get-CimInstance Win32_PnPEntity | ? { $_.service -eq "usbvideo" } | Select-Object -Property PNPDeviceID, Name
I try to automatically copy files from MTP-Devices (Smartphones) to my PC. I took several little code snippets from the internet for that and a lot of trial and error (several days), because I have no idea about powershell. But now everything works fine so far. The only problem is, that I have to manually put the name in for a device in my script, every time I took an other device. So I'm searching for a solution to automatically get the name from the actual device and save it in a variable.
I have tried this code:
Get-WmiObject -class win32_pnpentity -computername localhost | where-object {$_.HardwareID -like "*MTP*"} | format-list
As result i get:
__GENUS : 2
__CLASS : Win32_PnPEntity
__SUPERCLASS : CIM_LogicalDevice
__DYNASTY : CIM_ManagedSystemElement
__RELPATH : Win32_PnPEntity.DeviceID="USB\\VID_04E8&PID_6860&MS_COMP_MTP&SAMSUNG_ANDROID\\6&5F679FC&0
&0000"
__PROPERTY_COUNT : 26
__DERIVATION : {CIM_LogicalDevice, CIM_LogicalElement, CIM_ManagedSystemElement}
__SERVER : EXO-SURFACE
__NAMESPACE : root\cimv2
__PATH : \\EXO-SURFACE\root\cimv2:Win32_PnPEntity.DeviceID="USB\\VID_04E8&PID_6860&MS_COMP_MTP&SAM
SUNG_ANDROID\\6&5F679FC&0&0000"
Availability :
Caption : Galaxy S9+
ClassGuid : {eec5ad98-8080-425f-922a-dabf3de3f69a}
CompatibleID : {USB\MS_COMP_MTP, USB\Class_06&SubClass_01&Prot_01, USB\Class_06&SubClass_01,
USB\Class_06...}
ConfigManagerErrorCode : 0
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
Description : SM-G965F
DeviceID : USB\VID_04E8&PID_6860&MS_COMP_MTP&SAMSUNG_ANDROID\6&5F679FC&0&0000
ErrorCleared :
ErrorDescription :
HardwareID : {USB\VID_04E8&PID_6860&REV_0400&MS_COMP_MTP&SAMSUNG_Android,
USB\VID_04E8&PID_6860&MS_COMP_MTP&SAMSUNG_Android,
USB\SAMSUNG_MOBILE&MS_COMP_MTP&SAMSUNG_Android, USB\SAMSUNG_MOBILE&MI_00...}
InstallDate :
LastErrorCode :
Manufacturer : Samsung Electronics Co., Ltd.
Name : Galaxy S9+
PNPClass : WPD
PNPDeviceID : USB\VID_04E8&PID_6860&MS_COMP_MTP&SAMSUNG_ANDROID\6&5F679FC&0&0000
PowerManagementCapabilities :
PowerManagementSupported :
Present : True
Service : WUDFWpdMtp
Status : OK
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : EXO-SURFACE
PSComputerName : EXO-SURFACE
This looks very fine, because for the Name it's recognized as "Galaxy S9+" like it should. So do anyone here know, how to extract this Device Name into an variable? I would be very thankful, because I am a real noob in powershell and it's a project to help my parents, who have their problems with technology.
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.
I am trying to get PCoIP Statistics which are available through WMI, I use following command for WMIC
wmic path Win32_PerfRawData_TeradiciPerf_PCoIPSessionNetworkStatistics
or with powershell
powershell Get-WmiObject -namespace "root\cimv2" -computername computer01 -class Win32_PerfRawData_TeradiciPerf_PCoIPSessionNetworkStatistics
However when I tried to run either command forked through another process, in this case it was python, and piping the stdout, I am getting Invalid class error like below.
Get-WmiObject : Invalid class
At line:1 char:14
+ Get-WmiObject <<< -namespace root\cimv2 -computername computer01 -class
Win32_PerfRawData_TeradiciPerf_PCoIPSessionNetworkStatistics
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject], ManagementException
+ FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
if it helps, the output of powershell command through command prompt is
__GENUS : 2
__CLASS : Win32_PerfRawData_TeradiciPerf_PCoIPSessionNetworkS
tatistics
__SUPERCLASS : Win32_PerfRawData
__DYNASTY : CIM_StatisticalInformation
__RELPATH : Win32_PerfRawData_TeradiciPerf_PCoIPSessionNetworkS
tatistics.Name="PCoIP Session"
__PROPERTY_COUNT : 19
__DERIVATION : {Win32_PerfRawData, Win32_Perf, CIM_StatisticalInfo
rmation}
__SERVER : DEMO-VSGA-WS01
__NAMESPACE : rootcimv2
__PATH : \DEMO-VSGA-WS01rootcimv2:Win32_PerfRawData_Terad
iciPerf_PCoIPSessionNetworkStatistics.Name="PCoIP S
ession"
Caption :
Description :
Frequency_Object : 0
Frequency_PerfTime : 10000000
Frequency_Sys100NS : 10000000
Name : PCoIP Session
RoundTripLatencyms : 284
RXBWkbitPersec : 22034
RXBWPeakkbitPersec : 4
RXPacketLossPercent : 112
RXPacketLossPercent_Base : 28805
Timestamp_Object : 0
Timestamp_PerfTime : 299873128867
Timestamp_Sys100NS : 130641888164850000
TXBWActiveLimitkbitPersec : 1832
TXBWkbitPersec : 75615
TXBWLimitkbitPersec : 90000
TXPacketLossPercent : 7
TXPacketLossPercent_Base : 30942
I also tried using python module WMI
hostname = os.getenv('COMPUTERNAME', '')
c = wmi.WMI (hostname, namespace="root\\cimv2")
print c.Win32_PerfRawData_TeradiciPerf_PCoIPSessionNetworkStatistics
I am getting following error
print c.Win32_PerfRawData_TeradiciPerf_PCoIPSessionNetworkStatistics
File "c:\users\ramesh~1\appdata\local\temp\easy_install-tlfipc\WMI-1.4.9-py2.7
-win32.egg.tmp\wmi.py", line 1147, in __getattr__
File "C:\Python27\lib\site-packages\win32com\client\dynamic.py", line 522, in
__getattr__
raise AttributeError("%s.%s" % (self._username_, attr))
AttributeError: winmgmts://computer01/root/cimv2.Win32_PerfRawData_TeradiciP
erf_PCoIPSessionNetworkStatistics
Can this be related to impersonation and authentication level of caller?
UPDATE
I moved the powershell command to a bat file, when I run the bat file through CMD, it's again working fine.
When Popen through python, it showing same error. If it helps I am using python code.
p = subprocess.Popen ('bat.bat',stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print p.stdout.read()
I tried listing the classes under the namespace, the class was listed when the bat file was called through CMD, when Popen, none of Teradici's classes were available. The command line in bat.bat is
powershell Get-WmiObject -namespace "root\cimv2" -computername computer01 -list
All this is being run on VMWare VDI (Virtual Desktop Infrastructure), can there be any policy restrictions?
After troubleshooting for sometime, The reason seems like, the required class wasn't accessible from 32 bit programs, although when I tried through PowerShell (x64 & x86) I got correct responses.
Otherwise 64 bit WMI Provider can be accessed through 32 bit Program or vice versa, by correctly setting up __ProviderArchitecture & __RequiredArchitecture WMI Context flags,
a pythonic example is as follows
import win32com.client
import wmi
import os
objCtx = win32com.client.Dispatch("WbemScripting.SWbemNamedValueSet")
if self.is64Windows():
objCtx.Add ("__ProviderArchitecture", 64)
else:
objCtx.Add ("__ProviderArchitecture", 32)
objCtx.Add ("__RequiredArchitecture", True)
server = wmi.connect_server (server = "localhost", namespace="root\\cimv2", named_value_set=objCtx)
connection = wmi.WMI (wmi = server)
More information about Context Flags can be found on msdn
http://msdn.microsoft.com/en-us/library/aa393067%28v=vs.85%29.aspx
http://msdn.microsoft.com/en-us/library/aa390789%28v=vs.85%29.aspx
Additionally for WMI debugging and troubleshooting you can refer to
http://msdn.microsoft.com/en-us/library/aa394603%28v=vs.85%29.aspx
http://msdn.microsoft.com/en-us/library/aa392285%28v=vs.85%29.aspx