Trevor Sullivan's Tech Room

Minding the gap between administration and development

Posts Tagged ‘windows management instrumentation’

PowerShell: PowerEvents Module Update to 0.3 Alpha

Posted by Trevor Sullivan on 2012/01/11


imageIf you haven’t already checked it out, I wrote and published a PowerShell module on CodePlex a little over a year ago. It’s called PowerEvents for Windows PowerShell, and allows you to easily work with permanent WMI (Windows Management Instrumentation) event subscriptions. Some folks may not be aware that I’ve also written comprehensive documentation on the theory behind WMI events and why they’re useful. This ~30-page PDF document is included in the PowerEvents download, and is useful even if you do not want to use the PowerEvents module.

As a bonus, the PowerEvents module was mentioned just recently in the PowerScripting Podcast (listen around 1h19m)!!

http://powerscripting.wordpress.com/2012/01/09/episode-171-listener-call-in/

Listen to my interview with the PowerScripting Podcast back in December 2010!

http://powerscripting.wordpress.com/2010/12/13/episode-134-trevor-sullivan-on-wmi-events-in-powershell/

PowerEvents Download Link: http://powerevents.codeplex.com/

Posted in powershell, scripting, tools, wmi | Tagged: , , , , , , , , | 1 Comment »

PowerShell / ConfigMgr 2012: Check Client Reboot Pending State

Posted by Trevor Sullivan on 2011/07/25


Introduction

If you’ve worked with Configuration Manager 2007 for very long, you probably know that clients pending reboots can cause you quite a headache. Determining whether or not a client needs a reboot can be a challenging task, and most folks used desired configuration management rules to detect it.

Well, I’m happy to announce that there’s a new method of figuring out whether or not a SCCM client requires a reboot! There’s a new WMI namespace called root\ccm\ClientSDK, and within it is a WMI class called CCM_ClientUtilities, which has a static method called DetermineIfRebootPending() – the method does not take any input parameters, however it spits out several [out] parameters when it is called.

Read the rest of this entry »

Posted in configmgr, ConfigMgr vNext, powershell, scripting, wmi | Tagged: , , , , , , , , , , , | Leave a Comment »

ConfigMgr 2012: Deleting Advertisements

Posted by Trevor Sullivan on 2011/07/06


Where’d my Advertisements Go?

Advertisements aren’t quite what you think they are. At least, that’s probably the case if you’re talking in context of Microsoft System Center Configuration Manager (SCCM / ConfigMgr). At first glance you might see “deleting advertisements” and exclaim: “YES! Please do!” The difference here is that advertisements in SCCM are not product or service advertisements, but rather proclamations of the availability of software to a computer system. If you’re reading this, you most likely already knew that, but if not, then … now you do.

Back in the days of the SCCM 2007 console, there was a single Software Distribution –> Advertisements node which contained advertisements for both standard software distribution and operating system deployment (OSD) task sequences. This worked pretty well, but it was a little confusing since the operating system deployment node was entirely separate from software distribution. Hierarchically, it just didn’t make sense.

image

Read the rest of this entry »

Posted in configmgr, ConfigMgr vNext | Tagged: , , , , , , , , , , , , , , , , | Leave a Comment »

Removing Permanent WMI Event Registrations

Posted by Trevor Sullivan on 2011/01/18


Introduction

Since I’ve worked on the PowerEvents PowerShell module, several folks have been confused about how to remove event registrations once they’ve been created. I wrote some documentation that’s included in the download, that explains how to manually remove these registrations using the built-in wbemtest tool. This is the fool-proof method, since wbemtest is included in every Windows installation.

In the interest of making things easier, however, I wrote a WinForms utility in C# a little while back. This utility simply enumerates the various permanent event objects in the WMI repository (filters, consumers, and bindings), and allows you to remove them. That’s all it does 🙂

In the remainder of this blog post, I’ll talk about the manual method of removing event objects, and show you the utility I just mentioned.

The Manual Method

While experimenting with the PowerEvents module, you might accumulate many WMI event filter and consumer objects. At some point, you’ll probably want to clear those out. As of this writing, the PowerEvents module needs further work on the Get-* and Remove-* advanced functions. Until this functionality can be implemented, use the following procedure to remove the filter/consumer bindings.

Here are the individual steps to remove an unwanted event binding:

  1. Open wbemtest as an administrator
  2. Click Connect
  3. In the Namespace field, type root\subscription
  4. Click Connect or press {ENTER}
  5. Click Enum Instances
  6. Type __FilterToConsumerBinding in the text box
  7. Click OK or press {ENTER}
  8. Click the binding instance you want to remove
  9. Click the Delete button

Upon removal of the __FilterToConsumerBinding instance, the consumer will no longer respond to events. This process can be repeated to remove filters and consumers. Rather than enumerating instances of __FilterToConsumerBinding however, you would use “__EventFilter” or any of the five consumer class names.

The WMI Event Helper Utility

The utility I wrote a little while back is intended to display all filters, consumers, and event bindings to you, so that you can remove them from a friendly GUI. Although it works quite well in my personal testing, the tool has one important limitation, albeit it probably won’t affect most people.

The Limitation

In its current form, it does not allow you to specify a WMI namespace where the event objects reside. As I said, most folks should be OK, but you should be aware that you can create WMI filter objects in ANY WMI namespace, not just root\subscription (the default). The out-of-box consumer instances can be created in either root\default or root\subscription (latter is default). Finally, event bindings can be created in ANY WMI namespace.

Unless you’re explicitly specifying an alternate WMI namespace when creating the various WMI event objects, you won’t be affected by this. Just be aware that, if you’re a power user, you might want to make sure you don’t have event objects in other namespaces.

The Tool

Here’s what the tool looks like:

image

As you can see, it lists filters, consumers, and bindings in the root\subscription namespace. You might notice that above the consumer list, there’s a “Consumer Type” combo box. By clicking the arrow, or scrolling through the list using your mouse scroll wheel (the control must have focus), you can select the Consumer Class that you want to enumerate instances of.

Simply click on the item you want to delete, and select the appropriate “Remove” button. That’s all there is to it!

You can download the WMI Event Helper utility on the PowerEvents project page.

Conclusion

This blog post has covered the removal of WMI permanent event objects both manually, and using a C# event management utility. For more information, please review the comprehensive documentation included in the PowerEvents module release download.

Posted in .NET, powershell, scripting, wmi | Tagged: , , , , , , , , , | Leave a Comment »

PowerShell: PowerEvents Module

Posted by Trevor Sullivan on 2010/12/01


Hey guys, I haven’t written anything new in a while, because I’ve been working on a PowerShell module called PowerEvents. PowerEvents is a module that facilitates working with WMI (Windows Management Instrumentation) permanent event registrations. You can query for and respond to events, all from within WMI! PowerEvents simply makes creating those registrations easier.

For more information about this module, please visit the project page on CodePlex.

Look for a couple articles on the Hey, Scripting Guy! blog (@ScriptingGuys) next week, as well as my appearance on the @PowerScripting podcast with Hal Rottenberg and Jonathan Walz! Thanks for the invitation, Hal!

Posted in powershell, scripting, tools, wmi | Tagged: , , , , , , , , , | Leave a Comment »

PowerShell: ConfigMgr WMI Provider (feat. Lazy Properties)

Posted by Trevor Sullivan on 2010/09/28


What are Lazy Properties?

So if you’re a script writer, and you also use System Center Configuration Manager 2007 (ConfigMgr), you may have run into a concept called Lazy Properties. Lazy properties are certain (not all) properties on certain (not all) WMI classes, within the ConfigMgr provider namespace, that are marked with a WMI qualifier denoting the property as “lazy.” That was quite a mouthful! What exactly does this mean? Why are certain properties LAZY? Well, during the development of ConfigMgr, Microsoft realized that the enumeration of some information from the ConfigMgr provider could result in a high load on the server where the provider is installed. They introduced lazy properties as a method of providing only (what they deemed) “essential information” unless the script/software developer specifically requested more information.

How do I identify Lazy Properties?

Now that you know what lazy properties are, you are probably wondering how you’re supposed to know whether or not you have to design your code around lazy properties. This actually isn’t too hard. There are a couple different ways you’ll know that you’re dealing with a lazy property:

  • You’re not getting data back that you’d expect to when querying a ConfigMgr WMI class
  • You find a property marked with the lazy WMI qualifier using a WMI browsing tool (eg. wbemtest, SAPIEN WMI Explorer, CIM Studio, etc.)
  • Run a PowerShell script against the ConfigMgr namespace and look for property definitions with the lazy qualifier — read the last section of this article

Here is a screenshot of WbemTest (WMI utility built into Windows), browsing the class definition of root\sms\site_xyz:SMS_AuthorizationList. Instances of the SMS_AuthorizationList class represent Update List objects in ConfigMgr Software Updates. We can see that the property named “Updates” on this class is marked as lazy. This is just one example, and there are many more properties just like this one.

Ok, so how do I get values from Lazy Properties?

Getting values from lazy properties takes just a couple extra steps than normally working with WMI objects. The key thing to remember with lazy properties is that you have to get a reference to each WMI instance (aka. object) explicitly. You’re still ok to enumerate WMI instances using the SwbemServices.ExecQuery() COM interface in VBscript & PowerShell, or Get-WmiObject in PowerShell, but when you’re iterating over each instance, you must get a second, explicit reference to the WMI instance you’re wanting to work with. Let’s look at an example.

PowerShell Code – Retrieving a Lazy Property

Here is a sample script that retrieves all instances of the SMS_AuthorizationList class, that we examined above. Take note that as we iterate over each instance of SMS_AuthorizationList, we are redefining the $UpdateList variable by retrieving a direct reference to the instance. If we hadn’t done this, the Updates property that we looked at in WbemTest would appear to be empty!

# Retrieve an array of all Update List objects
$UpdateLists = Get-WmiObject -Namespace root\sms\site_lab -Class SMS_AuthorizationList

# Iterate over each Update List object in results
foreach ($UpdateList in $UpdateLists)
{
    # Using the __PATH property, obtain a direct reference to the instance
    $UpdateList = [wmi]"$($UpdateList.__PATH)"

    # Iterate over each update CI_ID in the Updates array
    foreach ($Update in $UpdateList.Updates)
    {
        # Get a reference to the update we're working with, based on its CI_ID
        $Update = Get-WmiObject -Namespace root\sms\site_lab -Query "select * from SMS_SoftwareUpdate where CI_ID = '$Update'"
        Write-Host "Update List ($($UpdateList.LocalizedDisplayName)) contains update: ",$Update.LocalizedDisplayName
    }
}

Code Results

Here is a screenshot of Quest’s free PowerGUI Script Editor running the script above, and retrieving a ConfigMgr lazy property (SMS_AuthorizationList.Updates). It’s actually doing a bit more than simply retrieving a WMI instance with its lazy properties — the lazy property (Updates) itself contains only an array of the UpdateIDs (aka. it’s CI_ID) associated to the Update List (SMS_AuthorizationList) object. We’re going one step further and cross-reference the UpdateID with the SMS_SoftwareUpdate class, so we can retrieve more information about the individual updates. This enables us to print out the friendly name of the update, as seen below in the lower-right PowerShell Console window.

What happens if I don’t retrieve lazy properties in my script?

So you’re thinking you can get off easy by just not retrieving lazy properties, right? Well, you can, but you’ll likely run into trouble. The thing is, if you make any changes to your WMI objects, and write them back to the ConfigMgr provider using the Put() method, you’ll effectively erase the values of any lazy properties on that object! It goes without saying that you need to be very careful when making any changes to your ConfigMgr objects via the provider (which is the only way you should be doing it in the first place, programatically).

If you’re simply reading information from the provider, and do not require the information contained within any lazy properties, then you are safe to read the information without performing the additional steps above.

Mega-Bonus Round: Listing All ConfigMgr Lazy Properties

Here’s a very simple PowerShell script we can use to spit out a list of all the lazy properties in the ConfigMgr namespace! Keep in mind that this could change with service pack or “R” revisions to ConfigMgr. I also would surmise that ConfigMgr vNext will introduce more changes to this list, and who knows, maybe they’ll eliminate lazy properties altogether! Just a theory, don’t quote me 🙂

PowerShell Code – Listing All ConfigMgr Lazy Properties

If you copy/paste/execute this code, after changing “lab” to your ConfigMgr site code, you should be able to get similar output to what I’ve included below for a reference. If you’re running the script from a remote management workstation, rather than on your server with the ConfigMgr provider, simply tack on the -ComputerName parameter to the Get-WmiObject cmdlet.

$WmiClassList = Get-WmiObject -List -Namespace root\sms\site_lab
$LazyList = @()

# Iterate over each WMI class in the namespace
foreach ($WmiClass in $WmiClassList)
{
    # Iterate over properties for current WMI class
    foreach ($WmiProperty in $WmiClass.Properties)
    {
        # Iterate over WMI qualifiers for current WMI property
        foreach ($WmiQualifier in $WmiProperty.Qualifiers)
        {
            # If qualifier is named "lazy", and the value is "true", then it's a lazy property
            if ($WmiQualifier.Name -eq 'lazy')
            {
                # Add class name, property name, and qualifier name to array for processing
                $LazyList += New-Object PSCustomObject -Property @{ "Class" = $WmiClass.__CLASS; "Property" = $WmiProperty.Name; "Qualifier" = $WmiQualifier.Name }
            }
        }
    }
}

# Sort array of lazy properties by WMI class name, property name
$LazyList = $LazyList | Sort-Object -Property Class,Property
# Write array of lazy properties to console
Format-Table -InputObject $LazyList -Property Class,Property,Qualifier | Out-File "All ConfigMgr Lazy Properties.txt"

ConfigMgr Lazy Property Reference

This is a reference of all ConfigMgr lazy properties, based on an installation of ConfigMgr 2007 SP2 with R3 Release Candidate installed. It’s possible that this list could change, but if you simply run the script above (change your site code from “lab), you can get a current list once again.

Class                                       Property                                    Qualifier
-----                                       --------                                    ---------
SMS_Advertisement                           AssignedSchedule                            lazy
SMS_Advertisement                           AssignedScheduleEnabled                     lazy
SMS_Advertisement                           AssignedScheduleIsGMT                       lazy
SMS_Advertisement                           ExpirationTimeEnabled                       lazy
SMS_Advertisement                           ExpirationTimeIsGMT                         lazy
SMS_Advertisement                           ISVData                                     lazy
SMS_Advertisement                           ISVDataSize                                 lazy
SMS_Advertisement                           PresentTimeEnabled                          lazy
SMS_Advertisement                           PresentTimeIsGMT                            lazy
SMS_AuthorizationList                       LocalizedInformation                        lazy
SMS_AuthorizationList                       Updates                                     lazy
SMS_BootImagePackage                        BackgroundBitmapPath                        lazy
SMS_BootImagePackage                        ContextID                                   lazy
SMS_BootImagePackage                        EnableLabShell                              lazy
SMS_BootImagePackage                        ImageDiskLayout                             lazy
SMS_BootImagePackage                        ImageIndex                                  lazy
SMS_BootImagePackage                        ImageProperty                               lazy
SMS_BootImagePackage                        ReferencedDrivers                           lazy
SMS_CategoryInstanceBase                    LocalizedInformation                        lazy
SMS_CertificateInfo                         Certificate                                 lazy
SMS_CertificateInfo                         PublicKey                                   lazy
SMS_CertificateInfo                         Thumbprint                                  Lazy
SMS_CI_CurrentComplianceStatus              ComplianceStatusDetails                     lazy
SMS_Collection                              CollectionRules                             lazy
SMS_Collection                              RefreshSchedule                             lazy
SMS_Collection                              RefreshType                                 lazy
SMS_Collection                              ReplicateToSubSites                         lazy
SMS_CollectionSettings                      CollectionVariables                         lazy
SMS_CollectionSettings                      PowerConfigs                                lazy
SMS_CollectionSettings                      ServiceWindows                              lazy
SMS_ConfigurationItem                       LocalizedEulas                              lazy
SMS_ConfigurationItem                       LocalizedInformation                        lazy
SMS_ConfigurationItemBaseClass              IsDigest                                    lazy
SMS_ConfigurationItemBaseClass              SDMPackageLocalizedData                     lazy
SMS_ConfigurationItemBaseClass              SDMPackageXML                               lazy
SMS_DeviceSettingItem                       PropList                                    lazy
SMS_DeviceSettingPackage                    DeviceSettingItemUniqueIDs                  lazy
SMS_DistributionPoint                       ISVData                                     lazy
SMS_DistributionPoint                       ISVDataSize                                 lazy
SMS_Driver                                  LocalizedEulas                              lazy
SMS_Driver                                  LocalizedInformation                        lazy
SMS_EULAContent                             EULAText                                    lazy
SMS_G_System_CollectedFile                  FileData                                    lazy
SMS_ImagePackage                            ImageDiskLayout                             lazy
SMS_ImagePackage                            ImageProperty                               lazy
SMS_MachineSettings                         MachineVariables                            lazy
SMS_ObjectContainerNode                     SearchString                                lazy
SMS_OperatingSystemInstallPackage           ImageProperty                               lazy
SMS_PackageBaseclass                        AlternateContentProviders                   lazy
SMS_PackageBaseclass                        ExtendedData                                lazy
SMS_PackageBaseclass                        ExtendedDataSize                            lazy
SMS_PackageBaseclass                        IconSize                                    lazy
SMS_PackageBaseclass                        ISVData                                     lazy
SMS_PackageBaseclass                        ISVDataSize                                 lazy
SMS_PackageBaseclass                        RefreshPkgSourceFlag                        lazy
SMS_PackageBaseclass                        RefreshSchedule                             lazy
SMS_PDF_Package                             Icon                                        lazy
SMS_PDF_Package                             IconSize                                    lazy
SMS_PDF_Package                             RequiredIconNames                           lazy
SMS_PDF_Package                             Status                                      lazy
SMS_PDF_Program                             Icon                                        lazy
SMS_PDF_Program                             IconSize                                    lazy
SMS_PendingRegistrationRecord               Certificate                                 lazy
SMS_PendingRegistrationRecord               PublicKey                                   lazy
SMS_PendingRegistrationRecord               Thumbprint                                  Lazy
SMS_Program                                 ExtendedData                                lazy
SMS_Program                                 ExtendedDataSize                            lazy
SMS_Program                                 IconSize                                    lazy
SMS_Program                                 ISVData                                     lazy
SMS_Program                                 ISVDataSize                                 lazy
SMS_Program                                 SupportedOperatingSystems                   lazy
SMS_Report                                  DrillThroughReportPath                      lazy
SMS_Report                                  ReportParams                                lazy
SMS_Report                                  SQLQuery                                    lazy
SMS_SiteControlFile                         BuildNumber                                 lazy
SMS_SiteControlFile                         FormatVersion                               lazy
SMS_SiteControlFile                         SCFData                                     lazy
SMS_SiteInstallMap                          BuildNumber                                 lazy
SMS_SiteInstallMap                          FormatVersion                               lazy
SMS_SiteInstallMap                          IMapData                                    lazy
SMS_SoftwareUpdate                          LocalizedEulas                              lazy
SMS_SoftwareUpdate                          LocalizedInformation                        lazy
SMS_SoftwareUpdateSource                    PublicKeys                                  Lazy
SMS_StateMigration                          UserNames                                   lazy
SMS_TaskSequencePackage                     BootImageID                                 lazy
SMS_TaskSequencePackage                     Category                                    lazy
SMS_TaskSequencePackage                     CustomProgressMsg                           lazy
SMS_TaskSequencePackage                     DependentProgram                            lazy
SMS_TaskSequencePackage                     References                                  lazy
SMS_TaskSequencePackage                     Reserved                                    lazy
SMS_TaskSequencePackage                     Sequence                                    lazy
SMS_TaskSequencePackage                     SupportedOperatingSystems                   lazy
SMS_TaskSequencePackage                     TaskSequenceFlags                           lazy
SMS_TaskSequencePackage                     Type                                        lazy
SMS_Template                                Data                                        lazy
SMS_UpdatesAssignment                       LegacyCollectInventory                      lazy
SMS_UpdatesAssignment                       LegacyDeploymentSchedule                    lazy
SMS_UpdatesAssignment                       LegacyDPLocality                            lazy
SMS_UpdatesAssignment                       LegacyForceReboot                           lazy
SMS_UpdatesAssignment                       LegacyInstallAllOnDeadline                  lazy
SMS_UpdatesAssignment                       LegacyInstallAllowedWindow                  lazy
SMS_UpdatesAssignment                       LegacyPostponeInstall                       lazy
SMS_UpdatesAssignment                       LegacySilentInstall                         lazy
SMS_VirtualApp                              IconSize                                    lazy

Posted in .NET, configmgr, powershell, scripting, tools, vbscript, wmi | Tagged: , , , , , , , , , , , , | Leave a Comment »

PowerShell Module: Enable Wake for Devices

Posted by Trevor Sullivan on 2010/09/23


So in my last post, I shared a PowerShell script that enables you to enable devices to wake up computers. This script relies solely on a WMI interface, but despite the remote nature of WMI, I had provided no method of entering a remote computer name. That changes with the release of my first ever formal PowerShell module! I’ve taken the code from the “enable device wake” script and adapted it to a PowerShell advanced function. I also added the cmdlet binding attribute to it, which makes it an official script cmdlet.

This command more or less replaces the functionality that powercfg.exe -DeviceEnableWake offers.

It has full support for:

  • Script example documentation
  • Named and positional parameters
  • Supports -WhatIf and -Confirm common parameters
  • Configuring remote computers

Download Set-DeviceWake v1.0

Output

Here is an example screenshot of the script’s execution. Please let me know if you have any feedback in the comments or at pcgeek86@gmail.com! As I said before, this is the first PowerShell module that I’ve shared, and I want to make sure it’s done right!

Running on Multiple Computers

(Updated on 2010-09-28)

So, you might be wondering how you can deploy this to multiple computers. The ComputerName parameter only takes a single computer name, rather than an array. You basically have three options here:

  1. Run from management workstation: Wrap the Set-DeviceWake function in a foreach loop, and pipe a text file with a bunch of computer names into the ForEach-Object
  2. Run from management workstation: Wrap the Set-DeviceWake function with Start-Job and a list of computer names
  3. Run on each PC individually: Deploy the script to target workstations via GPO (if they’re Windows 7), or deploy via systems management software, such as ConfigMgr

I don’t have examples handy just yet, but just thought I’d share this information at least!

Posted in .NET, powershell, scripting, tools, wmi | Tagged: , , , , , , , , , , | Leave a Comment »

PowerShell: Finding Currently Loaded DLLs

Posted by Trevor Sullivan on 2010/08/25


I was just browsing through the root\cimv2 WMI namespace this morning, using SAPIEN’s free WMI Explorer tool, when I happened across a WMI class called CIM_ProcessExecutable. In fact, what I was doing in a bit more detail, was going through the CIM_* classes, with the Instances tab selected, so I could discover if any of them were actually usable. I was stepping through them, in reverse order, by hitting {Up Arrow} + {Enter}, and finally came across one that had instances (this one, being CIM_ProcessExecutable).

Now, by clicking through a handful of these instances, it became quickly apparent to me that this class could be useful. It appeared that the classes’ purpose was to correlate running processes (instances of Win32_Process) with files on the filesystem (instances of CIM_DataFile), that are executable. Executable files would typically include files with the .dll or .exe file extensions.

Depending on what you are trying to accomplish, this could be incredibly useful information. For example, a lot of malware can load itself as a DLL in another process, or more simply, as its own executable. This info would be useful in discovering some (but not all) malware. Additionally, since you can get a reference to specific files on the filesystem, you could then determine the version of the file that is loaded into memory. This could be helpful if you are troubleshooting an issue that requires a specific version of an executable file.

Exploring the Class

If we simply enumerate a list of CIM_ProcessExecutable instances, we will get a long list of WMI paths to instances of other classes, specifically Win32_Process and CIM_DataFile. Go ahead and fire up you favorite PowerShell editor, or just the PowerShell console itself, and run this command:

Get-WmiObject -Namespace root\cimv2 -Class CIM_ProcessExecutable

Let’s find out how many executable files are currently loaded on the system. We do that simply by assigning the results of the above command to a PowerShell variable, and then echoing the value of its Count or Length property:

$ProcExes = Get-WmiObject -Namespace root\cimv2 -Class CIM_ProcessExecutable
Write-Host $ProcExes.Count

Retrieving File Properties

Let’s say that we want to just grab a list of interesting file properties, for the loaded executables. In order to do that, we can iterate over the items in $ProcExes, pass the WMI path for the CIM_DataFile instance to the WMI type accelerator in PowerShell, and then select a subset of the properties on the CIM_DataFile instance. Here’s how we do that:

$ProcExes = Get-WmiObject -Namespace root\cimv2 -Class CIM_ProcessExecutable

foreach ($item in $ProcExes)
{
    # Get the CIM_DataFile instance from the WMI path in Antecedent
    # Pass the CIM_DataFile object to the Select-Object cmdlet, and select only a few properties
    [wmi]"$($item.Antecedent)" | select FileName,Extension,Manufacturer,Version
}

Here is a sample of the output we’d get from running the above script:

Now we can take that even a step further. Let’s say our goal is to find out these same file properties, but only for non-Microsoft executables. This could help in a malware investigation, even a remote one, since we’re just using WMI here! We can use the Where-Object PowerShell cmdlet to filter the results that are written to the console. FYI, I am using the default short-hand for Where-Object, which is simply a question mark; Don’t let the syntax confuse you 🙂

foreach ($item in $ProcExes)
{
    # Get the CIM_DataFile instance from the WMI path in Antecedent
    # Filter for only files that are NOT from Microsoft
    # Pass the CIM_DataFile object to the Select-Object cmdlet, and select only a few properties
    [wmi]"$($item.Antecedent)" | ? { $_.Manufacturer -ne 'Microsoft Corporation' } | select FileName,Extension,Manufacturer,Version
}

As you can see, we’ve added the necessary code to filter out non-Microsoft files. That will work, at least as long as the file’s metadata is properly filled out. As you’ll see from the following screenshot, we still have some files that are obviously Microsoft PowerShell related, but were not caught because the Manufacturer field was missing from the assembly’s metadata.

Viewing Executables for a Process

Since the CIM_ProcessExecutable class binds executable files and processes together (these are not typically a 1:1 relationship), you might want to view all of the executable files that a particular process has loaded. The Dependent property of CIM_ProcessExecutable contains the WMI path to the process that has a particular executable file loaded. Each process has a unique ProcessID, but processes (instances of root\cimv2:Win32_Process) are uniquely identified in WMI by the Handle property. This is verified by examining the Win32_Process.Handle property in WMI Explorer (or wbemtest), and finding that the Key WMI qualifier is set to “True.” The discussion of WMI qualifiers is beyond the scope of this article, but in short, they are metadata that describe WMI properties. You can read more about WMI qualifiers on MSDN.

Typically, we won’t know the handle of a process, but we will likely know at least the name, or maybe even the process ID. Therefore, our process flow, for the goal we just mentioned, would look something like this:

  1. Input a process name
  2. Get the process Handle for the process(es) — we will actually just get the built-in __PATH property, because this directly correlates to the Dependent property on CIM_ProcessExecutable. Then we can avoid doing a LIKE query with just the process handle.
  3. Retrieve instances of CIM_DataFile for the process handle (this would be a string comparison on the Dependent property)
  4. For each instance of CIM_DataFile we retrieve: 1) get an instance of CIM_DataFile using the WMI path in Antecedent, 2) Echo the file properties to the console

Let’s take a look at the code necessary to make the above workflow happen:

Clear-Host

function Get-Executables()
{
    PARAM (
        [string] $ProcessName = $(throw "Please specify a process name.")
    )

    process
    {
        # Retrieve instance of Win32_Process based on the process name passed into the function
        $Procs = Get-WmiObject -Namespace root\cimv2 -Query "select * from Win32_Process where Name = '$ProcessName'"

        # If there are no processes returned from the query, then simply exit the function
        if (-not $Procs)
        {
            Write-Host "No processes were found named $ProcessName"
            break
        }
        # If one process is found, get the value of __PATH, which we will use for our next query
        elseif (@($Procs).Count -eq 1)
        {
            Write-Verbose "One process was found named $ProcessName"
            $ProcPath = @($Procs)[0].__PATH
            Write-Verbose "Proc path is $ProcPath"
        }
        # If there is more than one process, use the process at index 0, for the time being
        elseif ($Procs.Count -gt 1)
        {
            Write-Host "More than one process was found named $ProcessName"
            $ProcPath = @($Procs)[0].__PATH
            Write-Host "Using process with path: $ProcPath"
        }

        # Get the CIM_ProcessExecutable instances for the process we retrieved
        $ProcQuery = "select * from CIM_ProcessExecutable where Dependent = '$ProcPath'".Replace("\","\\")

        Write-Verbose $ProcQuery
        $ProcExes = Get-WmiObject -Namespace root\cimv2 -Query $ProcQuery

        # If there are instances of CIM_ProcessExecutable for the specified process, go ahead and grab the important properties
        if ($ProcExes)
        {
            foreach ($ProcExe in $ProcExes)
            {
                # Use the [wmi] type accelerator to retrieve an instance of CIM_DataFile from the WMI __PATH in the Antecentdent property
                $ExeFile = [wmi]"$($ProcExe.Antecedent)"
                # If the WMI instance we just retrieve "IS A" (think WMI operator) CIM_DataFile, then write properties to console
                if ($ExeFile.__CLASS -eq 'CIM_DataFile')
                {
                    Select-Object -InputObject $ExeFile -Property FileName,Extension,Manufacturer,Version -OutVariable $Executables
                }
            }
        }
    }

    # Do a little clean-up work. Not exactly necessary, but useful for debugging in PowerGUI
    end
    {
        Write-Verbose "End: Cleaning up variables used for function"
        Remove-Item -ErrorAction SilentlyContinue -Path variable:ExeFile,variable:ProcessName,variable:ProcExe,
        variable:ProcExes,variable:ProcPath,variable:ProcQuery,variable:Procs
    }
}

# Call the function we just defined, with its single parameter
. Get-Executables gmailgrowl.exe

Here is an example of the output we would get, from running the above code. Make sure to change the process name, at the very bottom of the script, where we call the function, to something you’re interested in discovering information about.

Conclusion

That’s all I’ve got for now, folks! Hopefully this gives you some ideas around what else you can do with this information 🙂 There are lots of uncovered gems out there to help you with systems management, and this is just another one of them!

Posted in powershell, scripting, tools, wmi | Tagged: , , , , , , , , , , , , | Leave a Comment »

Exporting ConfigMgr Task Sequences

Posted by Trevor Sullivan on 2010/05/13


This article will briefly discuss how to export a ConfigMgr OSD task sequence.

The SMS_TaskSequencePackage class has a lazy property named Sequence which contains the XML for the task sequence. Because this is a lazy property, we must obtain a reference to each WMI instance using its direct path, before we can open the property’s value. Once we have the XML for the task sequence, we can send it off to a text file. The PowerShell code to do that would look like this:

$TsList = Get-WmiObject SMS_TaskSequencePackage -Namespace root\sms\site_lab

ForEach ($Ts in $TsList)
{
$Ts = [wmi]”$($Ts.__PATH)”
Set-Content -Path “$($ts.PackageId).xml” -Value $Ts.Sequence
}

Posted in configmgr, powershell, scripting, tools, wmi | Tagged: , , , , , , , , , | 7 Comments »

PowerShell: Getting started with WMI Events

Posted by Trevor Sullivan on 2009/11/16


Introduction

PowerShell version 1 provided good integration with WMI using the Get-WmiObject cmdlet, allowing you to easily retrieve and modify WMI instances, and call WMI methods, but PowerShell v2 has taken it, and many other things, a lot farther. One of those areas is eventing, and not just WMI eventing, but responding to WMI events is what I’d like to discuss in this article. Because WMI contains a large repository of information regarding a system’s hardware and software state, it is useful to understand WMI events, so that you can determine where they can fit into your environment. This topic is mostly geared towards systems administrators or engineers that are looking to do some advanced monitoring of their systems.

To get started with WMI events in PowerShell v2, I’m going to show you an example of how to use WMI events to detect DHCP lease changes. DHCP renewals occur infrequently, relative to the frequency of many other event types, however forcing a DHCP renewal is easy, and therefore makes for a good development example. Also, it seems like every other article out there uses Win32_Process instance creations or deletions for their WMI eventing examples, so I figured I would try something a little bit different. Keep in mind that DHCP lease updates are only one of thousands of potential uses for WMI events; I am simply attempting to convey the concept to you, so that you can figure out other methods of using it for your own needs.

About WMI Event Queries

For those of  you who may be unfamiliar with WMI event queries (aka. notification queries), they use a syntax similar to this:

SELECT <Properties> FROM <EventClass> WITHIN <Seconds> WHERE TargetInstance ISA '<WmiClass>' AND <OtherCriteria>

Now, writing an event query is a little bit different from a standard select query. Why, you ask? Well, the system event classes you’ll work with respond to WMI instance events for all of the built-in WMI classes. In other words, they are responsible for a very large number of events. Because of this, you will need to write a query that provides extensive filters in order to avoid some inherent limitations of WMI. The main event classes of which I speak are as follows:

  • __InstanceCreationEvent – occurs when a WMI instance is created (eg. a Win32_Process being instantiated)
  • __InstanceDeletionEvent – occurs when a WMI instance is deleted (eg. a Win32_Process terminating)
  • __InstanceModificationEvent – occurs when a WMI instance is modified (eg. a Win32_Process uses additional memory, or deallocates some)
  • __InstanceOperationEvent – occurs when a WMI instance is created, deleted, or modified (any of the above)

These are the main WMI event classes you’ll work with, unless you have another specific need, for example:  monitoring a 3rd party piece of software, or are looking to monitor installation or deletion of WMI classes (as opposed to instances of classes).

If you would like to test out writing an event query, you can use the Wbemtest utility, that is included with Windows 2000 and up (XP, 2003, Vista, 2008, 7, 2008 R2). Simply type “wbemtest” at the run prompt, or from a command prompt, then use the “Connect” button to connect to the “root\cimv2” namespace, and you should be presented with a window that looks similar to the below screenshot. Make sure that you select the “Asynchronous” method invocation option, as that will enhance wbemtest’s GUI performance during event queries; If you leave it at the default of “Semisynchronous“, you will probably find the GUI frustrating to work with, as it hangs during the polling interval.

Wbemtest

WbemTest Utility (connected to root\cimv2)

To test out an event query select the “Notification Query” button, and you’ll be presented with a box to type your query into. For now, just to get you started, an easy query to type here would be:

SELECT * FROM __InstanceModificationEvent WITHIN 3 WHERE TargetInstance ISA 'Win32_Process'
Notification Query Window

Notification Query Window (wbemtest)

Once you’ve written your query, select “Apply,” wait the interval you specified (in the WITHIN clause), and you will start to see WMI event instances start to populate in the “Query Result” window. This window will remain open (and subscribed to events) until you close it. You can double-click on these event instances to open them, and then close the “Query Result” window if you’d like to stop polling for events.

Wbemtest Notification Query Results

Event Query Results (wbemtest)

Here is what you’ll see when you double-click on one of these events:

WMI Event Instance

WMI Event Instance (wbemtest)

As you can see, the event instance contains a few useful properties:

  • TIME_CREATED – When the event occurred (we’ll talk about how to interpret this later)
  • SECURITY_DESCRIPTOR – Not sure what this is used for, but it appears to be NULL typically
  • PreviousInstance – The WMI instance in its state, prior to the event
  • TargetInstance – The WMI instance in its state, after the event occurred

By double-clicking on the PreviousInstance and TargetInstance properties (and then clicking the “View Embedded” button), we can view the WMI instance, and its properties, both before the event occurred, and after the event occurred. Because we have access to both of these states in an __InstanceModificationEvent, we can do a comparison to see which properties actually changed, and which ones didn’t. The __InstanceCreationEvent, __InstanceDeletionEvent, and  __InstanceOperationEvent classes do not have the PreviousInstance property, because they are only dealing with a WMI instance in a single state, not a before/after state.

Property Editor for TargetInstance (wbemtest)

Property Editor for TargetInstance (wbemtest)

Object Editor for TargetInstance (wbemtest)

Object Editor for TargetInstance (wbemtest)

The above screenshot is a representation of the WMI instance after it was modified. Had we gone through and opened the PreviousInstance property, we would have seen a similar screen for it as well. On top of that, you can click the “Show MOF” button, which generates the Managed Object Format (MOF) syntax that represents that object. This is great for development and troubleshooting, because you can copy/paste both the MOF syntax for both PreviousInstance and TargetInstance into a text editor, and compare all of the different property values that changed. In fact, to save even having to do that, you can go all the way back to the __InstanceModificationEvent screen, click “Show MOF” there, and it will include the sub-instances (embedded objects).

__InstanceModificationEvent MOF Syntax (wbemtest)

__InstanceModificationEvent MOF Syntax (wbemtest)

For now, this concludes the section about WMI event queries, and how to test them using the wbemtest utility. If you have other questions regarding WMI Events, please review the resources available on MSDN.

Building Our DHCP Lease WMI Event Query

In this section, let’s talk a little bit more deeply about the WMI event query we need to create in order to detect DHCP lease changes. Remember, the goal of this article is to show you how to detect and respond to WMI events that indicate a change in the DHCP lease time. At any point, we can invoke a DHCP lease renewal by issuing the command:

ipconfig /renew

or (from PowerShell):

([wmiclass]"Win32_NetworkAdapterConfiguration").RenewDHCPLeaseAll()

Granted, the PowerShell method is a little longer, but it’s also more understandable, as we’re directly calling the WMI API to initiate the DHCP renewal, rather than going through a software utility. I just wanted to make sure you understood that, without “ipconfig”, there is still an easy way to do this through PowerShell. That’s the beauty of PowerShell … simple access to .NET and WMI objects! Anyway, we’re getting off-topic here.

Now that we know how to initiate a DHCP renewal, we can talk about how to write our event query in such a way that we can pick up on this event. It so happens that there is a WMI class called Win32_NetworkAdapterConfiguration in the root\cimv2 WMI namespace, which contains information about the network adapters in a computer (not restricted to only physical adapters). This class has some useful properties:

Win32_NetworkAdapterConfiguration WMI Class (wbemtest)

Win32_NetworkAdapterConfiguration WMI Class (wbemtest)

The “DHCPLeaseObtained” property is pretty self-explanatory, and contains the date & time that the DHCP lease was obtained on a particular network adapter. Now, keep in mind that you will typically see a number of instances of the Win32_NetworkAdapterConfiguration, but normally only the “real” network interfaces (eg. wired, wireless, or VPN) will actually have a non-null value for “DHCPLeaseObtained“. This will be helpful information for writing our WMI event query.

For our first attempt at writing an event query, we might come up with something as simple as this:

SELECT * FROM __InstanceModificationEvent WITHIN 3 WHERE TargetInstance ISA 'Win32_NetworkAdapterConfiguration'

After all, that query will return any changed instances of network adapter configurations, but what is the problem with that? Well, the problem is that we’ll get back events for a whole lot more than just DHCP lease changes. So then, how do we zero in the information we need? If you check out my (albeit high-level) original definition of a WMI event query, you’ll notice the “AND <OtherCriteria>” part at the end. Thankfully, we can dig deeper into objects’ properties, as part of our query, to determine a restricted set of returned instances. Because we already know that: 1) there is a DHCPLeaseObtained property, and 2) we have access to both a PreviousInstance and TargetInstance, we can construct a query that looks like this:

SELECT * FROM __InstanceModificationEvent WITHIN 3 WHERE TargetInstance ISA 'Win32_NetworkAdapterConfiguration' AND TargetInstance.DHCPLeaseObtained <> PreviousInstance.DHCPLeaseObtained

If you examine the above query, you’ll see that we’re further restricting the query, by only returning instances where the target instance’s DHCPLeaseObtained property does not match the previous instance’s DHCPLeaseObtained property. What this effectively gives us, is only events where the DHCP renewal time has changed, and nothing else! So, for the remainder of this article, the query directly above this paragraph is what we’ll use to detect DHCP lease time changes.

Before we go on, and now that we have identified the query to use, let’s test it out using wbemtest:

Wbemtest

1. Open wbemtest, connect to rootcimv2, select Asynchronous, and click Notification Query

Wbemtest - Notification Query

2. Paste your notification query and click Apply

PowerShell - DHCP Renewal

3. Use PowerShell to initiate a DHCP renewal

Wbemtest - Notification Query Result

4. Double-click the resulting event

Wbemtest - Event Object

5. Click the Show MOF button

Wbemtest - Event Object MOF

6. Check out the PreviousInstance and TargetInstance values for the DHCPLeaseObtained property!

This completes the current section, on determining the WMI event query to identify DHCP lease changes. Next, we’ll look at how to use all of this with PowerShell.

PowerShell WMI Event Cmdlets

Now that we’ve talked about WMI event queries, how to test them out by themselves first, and how to build our DHCP lease event query, we are ready to talk about the PowerShell cmdlets that allow us to easily put those queries to good use. The easiest way to get started with PowerShell and events, is to simply issue the command:

help *event*
PowerShell - Help Event Command

Help *event* Command

From this command’s output, you’ll see that there are a number of event-related Cmdlets as well as a HelpFile to get us started. We’ll concern ourselves with just a few of these cmdlets however:

  • Register-WmiEvent – Registers an event subscription and allows you to specify a PowerShell ScriptBlock to respond to the event
  • Get-EventSubscriber – Retrieves a list of all current event subscriptions (not just WMI ones)
  • Unregister-Event – Unregisters event subscriber(s) (not just WMI ones)

Remember, for any PowerShell cmdlets, simply type the following to get full documentation on how to use it:

help <cmdletname> -full
help Register-WmiEvent -full
Register-WmiEvent Help

Register-WmiEvent Help

If you type “Register-WmiEvent” without any parameters, you’ll be prompted for a WMI class name. Unfortunately, for our purposes, this can be deceiving, because as we talked about above, we don’t actually want to register for all event instances, only certain ones. Because of this, we’ll need to specify the “-Query” parameter on the “Register-WmiEvent” cmdlet, which lets us set the event / notification query we want to use. Your event query can be stored in a PowerShell string variable also, but for the sake of this article, we’ll just keep it in-line with the cmdlet. Now technically, we could run this cmdlet using only the “-Query” parameter like this:

Register-WmiEvent no Action

Register-WmiEvent without an action

Running this command yields a new event subscription, but what happens when an event is actually triggered? You guessed it, nothing by default. Actually, what this does is puts events into an event queue, so they can be retrieved using the Get-Event cmdlet, but for now we want an immediate response to our event. So, let’s un-register the event subscription we created and try again.

PowerShell - Unregister-Event

Unregistering PowerShell event handlers

If we want something to actually happen in response to these events we are subscribing to, we’ll need to specify an action using the “-Action” parameter on the “Register-WmiEvent” cmdlet. The action parameter allows us to specify a PowerShell script block to respond to events. This could be as simple as a quick “Write-Host” command, or we could call a pre-defined function as our event handler (eg. function “DhcpLeaseChangeHandler“). Let’s stick with a simple “Write-Host” for now though, and then call a DHCP renewal to test it out. I’ll talk about some more advanced options in the next section.

PowerShell-Register-WmiEvent-Complete

Our completed sample!

So that’s it! If you’ve made it this far, you’ve figured out how to register for WMI events and respond to them using a PowerShell script block! If you’re interested, stick around, and I’ll show you how to do some even cooler stuff in the next section.

Advanced WMI Event Handling

Alright, so you’ve made it far enough to figure out how to register WMI event handlers, but you want a bit more. Maybe you’re asking yourself: “Can I see information about the event from the script, like I did using wbemtest?” If so, then you’ll be glad to hear that the answer is “yes!”

To start off, let’s look at the help for the “-Action” parameter of “Register-WmiEvent“:

-Action <scriptblock>
Specifies commands that handle the events. The commands in the Action parameter run when an event is raised ins
tead of sending the event to the event queue. Enclose the commands in braces ( { } ) to create a script block.

The value of the Action parameter can include the $Event, $EventSubscriber, $Sender, $SourceEventArgs, and $Sou
rceArgs automatic variables, which provide information about the event to the Action script block. For more inf
ormation, see about_Automatic_Variables.

When you specify an action, Register-WmiEvent returns an event job object that represents that action. You can
use the cmdlets that contain the Job noun (the Job cmdlets) to manage the event job.

Required?                    false
Position?                    102
Default value                The event is added to the event queue.
Accept pipeline input?       false
Accept wildcard characters?  false

From this, we see that there are a few built-in variables that enable us to capture event information, with a reference over to the “about_Automatic_Variables” help file. The $Event variable sounds pretty promising, doesn’t it? Let’s take a look at that (in the aforementioned help file):

$Event
Contains a PSEventArgs object that represents the event that is being
processed.  This variable is populated only within the Action block of
an event registration command, such as Register-ObjectEvent. The value
of this variable is the same object that the Get-Event cmdlet returns.
Therefore, you can use the properties of the $Event variable, such as
$Event.TimeGenerated , in an Action script block.

That sounds like what we’re after: information about the event that gets created. So let’s replace our “-Action” script block with the following: { $Global:MyEvent = $Event }. This way, when an event gets created, it will assign the event to the global $MyEvent variable, so we can play with it.

PowerShell-Register-WmiEvent-Advanced-MyEvent

Checking out the contents of $Event

As we can see, the $Event variable (which we reassigned to $MyEvent) contains a PSEventArgs object. If we issue a few more commands to the PowerShell console, we can discover the underlying WMI event object, so we can retrieve relevant information from it.

The underlying WMI event object

The underlying WMI event object

Finally! We’ve gotten to the bottom of our WMI event object, and can now check out the WMI properties that we wanted to compare, in order to determine the differences between the different instances of Win32_NetworkAdapterConfiguration. Let’s take a look at the DHCPLeaseObtained property on both our TargetInstance and PreviousInstance. Also, let’s look at the .NET API to take the TIME_CREATED property and convert it to a readable DateTime format; It’s simple, trust me.

PowerShell - WMI Event Object

The underlying WMI Event Object

There they are … the two CIM_DATETIME values that represent our DHCPLeaseObtained values before and after the event occurred (that we triggered)! The TIME_CREATED property is a 64-bit integer that represents the number of 100 nano-second intervals that have occurred between 12:00:00 AM January 1st, 1601 and the time that the event was generated. Don’t ask.

This example shows that you can dynamically retrieve event information on-the-fly from a PowerShell WMI event subscription. This information could be used in other ways, such as sending an e-mail alert to an administrator, calling an executable, or just about anything else you could think of.

Conclusion

This article stemmed from my interest in learning about event support in PowerShell version 2.0. I hope that by portraying my experiences with PowerShell events, you are able to learn something as well.

Please pass any feedback you may have on to me via pcgeek86@gmail.com or in the comments of this article.

Posted in powershell, scripting, tools, wmi | Tagged: , , , , , , , , , , , , | 9 Comments »