Registering for WMI Events in PowerShell

registering-for-wmi-events

An alternate title might be ‘Running PowerShell Code ONLY when the power state changes’, because that was the very interesting task I received from my customer this week.

It was honestly too cool of a StackOverflow answer NOT to share, so here it goes, you can vote for it here if you thought it was worth-while.

If you want your code to trigger only when the System Power State changes, as described here, use this code.


Register-WMIEvent -query "Select * From Win32_PowerManagementEvent" `
 -sourceIdentifier "Power" `
 -action {
     #YourCodeHere
      }

Now, this will trigger whenever the power state changes, whether you plug the device in, OR unplug it. So you might further want to stop and pause to ask the question:

Am I on power or not?

Fortunately we can use the WMI Class Win32_BatteryStatus to detect if we’re charging or not, so here’s the full construct that I use to ONLY run an operation when a power event changes, and then only if I’m no longer on Power.

Locking the workstation when the system is unplugged


Register-WMIEvent -query "Select * From Win32_PowerManagementEvent" `
  -sourceIdentifier "Power" `
  -action {
      if ([BOOL](Get-WmiObject -Class BatteryStatus -Namespace root\wmi).PowerOnLine ){
         #Device is plugged in now, do this action
         write-host "Power on!"
     }
    else{
        #Device is NOT plugged in now, do this action
        write-host "Now on battery, locking..."
        [NativeMethods]::LockWorkStation()
     }

If you’re curious how this looks in real time

Registering for device events

It can also be useful to have your code wait for something to happen with devices, such as running an action when a device is added or removed. To do this, use this code.


#Register for power state change
#Where TargetInstance ISA 'Win32_Process'"
Register-WMIEvent -query "Select * From Win32_DeviceChangeEvent where EventType = '2'" `
-sourceIdentifier "Power" `
-action {#Do Something when a device is added
Write-host "Device added at $(Get-date)"
}

You might also want to do an action if a device is removed instead, so use this table to choose which event is right for you. Read more about it here.

EventType Id
ConfigurationChanged 1
Device Arrived 2
Device Removed 3
Device Docked 4

What else can I wait for?

Not only these, but you can trigger your code to execute on a variety of useful WMI Events, all of which can be seen in this image below!

ClassName Triggers when
Win32_DeviceChangeEvent  A device is installed, removed, or deleted, or the system is docked
Win32_VolumeChangeEvent Something happens to your disk drives
Win32_PowerManagementEvent Your device is plugged, unplugged or docked
Win32_ComputerSystemEvent Something major happens to the system
Win32_ComputerShutdownEvent The system is shutting down!
RegistryEvent Anythign happens to the registry
RegistryKeyChangeEvent A reg key you specify is changed
RegistryValueChangeEvent A reg value you specify is changed
Advertisements

5 thoughts on “Registering for WMI Events in PowerShell

  1. Hugh McIntyre (@HughMc) March 21, 2017 / 10:32 pm

    Hey Stephen, have you managed to get these to be persistent? Currently they’ll only last for the session.

  2. Hugh McIntyre (@HughMc) March 22, 2017 / 9:13 pm

    Actually figured it out with:

    Set-WmiInstance -Class __EventFilter
    Set-WmiInstance -Class ActiveScriptEventConsumer
    Set-WmiInstance -Class __FilterToConsumerBinding

    Pretty nifty combined with burnt toast. thanks for the idea!

    • FoxDeploy March 23, 2017 / 3:24 pm

      WOuld you mind sharing your full code? I’m not sure how those steps make it persistent.

  3. Hugh McIntyre (@HughMc) March 23, 2017 / 7:47 pm

    Hi Stephen! First we have to create the script that creates the binding with a query on Win32_VolumeChangeEvent where EventType = ‘2’ (device added). Basically flogged this from Ed Wilson (thanks Ed) https://blogs.technet.microsoft.com/heyscriptingguy/2012/07/20/use-powershell-to-create-a-permanent-wmi-event-to-launch-a-vbscript/

    $computer = “$env:COMPUTERNAME”
    $filterNS = “root\cimv2”
    $wmiNS = “root\subscription”
    $query = @”
    Select * From Win32_VolumeChangeEvent where EventType = ‘2’
    “@
    $filterName = “USBConnectionFilter”
    $scriptFileName = “$env:ProgramData\Client\USBNotification\LaunchPowerShell.vbs” #script that runs on event

    # Create a filter and a consumer then bind them.

    $filterPath = Set-WmiInstance -Class __EventFilter `
    -ComputerName $computer -Namespace $wmiNS -Arguments `
    @{name=$filterName; EventNameSpace=$filterNS; QueryLanguage=”WQL”;
    Query=$query}

    $consumerPath = Set-WmiInstance -Class ActiveScriptEventConsumer `
    -ComputerName $computer -Namespace $wmiNS `
    -Arguments @{name=”USBConnection”; ScriptFileName=$scriptFileName;
    ScriptingEngine=”VBScript”}

    Set-WmiInstance -Class __FilterToConsumerBinding -ComputerName $computer `
    -Namespace $wmiNS -arguments @{Filter=$filterPath; Consumer=$consumerPath} |
    out-null

    Run this script as a deployment to machines (or during OSD) along with the BurntToast module.

    Inside the above LaunchPowerShell.vbs script we import the BurntToast module and run a notification

    Import-Module BurntToast

    New-BurntToastNotification -Text ‘USB Device Usage is Being Monitored’ -AppLogo “$env:windir\OEMlogo.bmp”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s