Using the WMI Commandline Event Consumer DSC resource

In my earlier article, I promised a detailed walk-through of each DSC resource to manage the WMI permanent events. To start that series, I will show how to use the command line Event Consumer DSC resource. The event filter and event binding resources are very straightforward. So, I will talk about these two resources as a part of this article.

For the demo purpose, let’s look at something more useful than just monitoring process creation or deletion. In this article, we will see an event filter that detects insertion of a USB flash drive and then triggers the command line Event Consumer to perform backup of a local folder to the newly inserted USB flash drive. Also, I want the backup to be performed only when the flash drive’s volume label is ‘Backup’.

Creating an Event Filter

For detecting a USB flash drive insertion, we can use the Win32_VolumeChangeEvent class. The EventType property tells us what type of volume change event occurred. However, the event object does not contain a volume label. So, instead of this event class, I will use an intrinsic event class named __InstanceCreationEvent and watch for any new Win32_Volume instances. Using this, I get the ability to filter the event based on the label of the inserted flash drive. In the below query, we use DriveType=2 to ensure that we detect only removable disks.

$Query = "SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE
                   TargetInstance ISA 'Win32_Volume' AND
                   TargetInstance.Label='Backup' AND
                   TargetInstance.DriveType=2"

Now that we have the query, let’s see how an event filter can be created using the cWMIEventFilter DSC resource. You can get the syntax for this resource by using the -Syntax parameter with the Get-DscResource cmdlet.

As you see here, there are only two mandatory properties–Name and Query. The Name property must be unique on the target system. The Query property takes a WMI query, like the one I showed above, as input. The EventNamespace is set to root\cimv2, by default. You can set it based on where the event gets triggered. The event filter instance gets created in the root\subscription namespace.

The following code snippet shows the configuration script for a resource instance:

cWMIEventFilter UFDDetection {
   Name = 'UFDFilter'
   Query = "SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE
                TargetInstance ISA 'Win32_Volume' AND
                TargetInstance.Label='Backup' AND
                TargetInstance.DriveType=2"
   EventNamespace = 'root\cimv2'
   Ensure = 'Present'
}

Creating an instance of Commandline Consumer

We have an instance of the event filter created for the USB flash drive insertion. What we need next is a standard consumer instance that performs the action we need. In this article, we will look at the cWMICommandLineConsumer.

The Name and the CommandLineTemplate properties are the mandatory in the Commandline Consumer DSC resource. If you specify the program name to execute as part of the CommandLineTemplate property, you should avoid specifying that as the ExecutablePath property and vice versa. Before we create the event consumer instance using DSC, we need a batch command file that copies the data to the newly inserted USB flash drive. We will put a simple xcopy command in a .cmd file and store it in C:\Scripts folder.

@ECHO OFF
FOR /f %%a in ('WMIC OS GET LocalDateTime ^| find "."') DO set DTS=%%a
set TODAY=%DTS:~0,4%-%DTS:~4,2%-%DTS:~6,2%
Set BackupFolder=%1\%TODAY%
mkdir %BackupFolder%
xcopy /E /Y C:\DSCDemo\*.* %BackupFolder%

If you see the above batch file, I am creating a folder at the destination to represent a date when the backup was taken. Also, I had hard-coded the folder that needs to be copied. C:\DSCDemo is the folder that I will be copying to. If you are wondering how the batch script knows which drive letter to use for the newly inserted removable disk, that will be sent to the batch script as a commandline argument. The %1 in the batch script represents the first argument sent to the script.

With this handy, let us create the Commandline Consumer instance using DSC.

cWMICommandLineConsumer UFDCommandLine {
   Name = 'UFDCommandLineConsumer'
   CommandLineTemplate = 'cmd.exe /c C:\Scripts\Backup.cmd %TargetInstance.DriveLetter%'
   Ensure = 'Present'
}

In the configuration script, if you look at the CommandLineTemplate property, I am passing the drive letter of the removable disk as %TargetInstance.DriveLetter%.

Creating a Filter to Consumer Binding

The binding between the event filter and consumer can be created using c_WMIEventBinding_ DSC resource.

This resource takes three mandatory properties. These are Filter, Consumer, and ConsumerType. For the Filter and Consumer properties, you should use the name of the instance you specified in the configuration. In my configuration, it will be UFDFilter and UFDCommandLineConsumer. For the ConsumerType property, the valid values are listed in the screenshot above. For this example, we will need to specify CommandLine as the argument for the ConsumerType property. Here is how the configuration script looks:

cWMIEventBinding UFDCommandLineBinding {
   Filter = 'UFDFilter'
   Consumer = 'UFDCommandLineConsumer'
   ConsumerType = 'CommandLine'
   DependsOn = '[WMIEventFilter]UFDDetection','[WMICommandLineConsumer]UFDCommandLineConsumer'
   Ensure = 'Present'
}

Now that we have the configuration script for all three, it is time to see the complete configuration script for a target system.

Configuration BackuptoUFD {
    Import-DscResource -Module cWMIPermanentEvents

    cWMIEventFilter UFDDetection {
       Name = 'UFDFilter'
       Query = "SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE
                        TargetInstance ISA 'Win32_Volume' AND
                        TargetInstance.Label='Backup' AND
                        TargetInstance.DriveType=2"
       EventNamespace = 'root\cimv2'
       Ensure = 'Present'
    }

    cWMICommandLineConsumer UFDCommandLine {
       Name = 'UFDCommandLineConsumer'
       CommandLineTemplate = 'cmd.exe /c C:\Scripts\Backup.cmd %TargetInstance.DriveLetter%'
       Ensure = 'Present'
    }

    cWMIEventBinding UFDCommandLineBinding {
       Filter = 'UFDFilter'
       Consumer = 'UFDCommandLineConsumer'
       ConsumerType = 'CommandLine'
       DependsOn = '[WMIEventFilter]UFDDetection','[WMICommandLineConsumer]UFDCommandLine'
       Ensure = 'Present'
    }
}

I have not used Node block in the configuration script and therefore this generates the configuration MOF for the local system. After we enact this configuration, every time a removable disk with ‘Backup’ volume name is inserted, the backup.cmd script gets triggered and copies contents of C:\DSCDemo to the removable disk.

Share on: