Reinventing the Wheel: Get-WmiChildNamespace and Get-WmiChildPropertyData

I love and hate WMI.  I love how it offers the ability to access so many parts of the OS that PowerShell has not (yet) exposed via cmdlet.  I hate how the taxonomy is so deep, how you need to know what you’re looking for to find it.

Also, I’ve been having a hard time differentiating between –Namespace and –Class, and the PropertyData properties available within a given class.

I wrote this primarily as a learning exercise to tell the difference and to also to change the –Recurse behavior to only display WMI namespaces (listing all the WMI classes and properties makes for fascinating reading, but it’s a little unmanageable.)

Finally, this helps me remember that Get-WmiObject –Namespace ROOT –Class __Namespace will list all the available WMI namespaces, not just ROOT\cimv2 (which contains all the fun stuff, really.)

Enjoy!

One note about Get-WmiChildNamespace: it populates the global variable $__GetWmiChildNamespaceExclude with a list of all the WMI namespaces that returned error (usually due to permission issues).  However, this is after Get-WmiObject freezes for 10 seconds or so trying to query it before it’s rejected.  While this is not the default behavior (unseen magic is bad), subsequent runs can benefit from this cached data by adding the parameter –Exclude $__GetWmiChildNamespaceExclude, so Get-WmiChildNamespace knows to skip those problem namespaces.

Also, I couldn’t get Select-String –Exclude [string[]]$exclusionList to work properly.  I ended up using –NotMatch, which required –Pattern to be used to specify the RegEx patterns to exclude.  Very convoluted…

 
function Get-WmiChildNamespace
{

    param (
        [parameter(ValueFromPipeline=$true)][string[]]$Namespace = 'ROOT',
        [string[]]$Pattern = @('.*'),
        [string[]]$Exclude= @()
    );

    process
    {

    foreach ($myNamespace in $Namespace)
    {

        # handle pattern matching here
        if ($Pattern -and !(Select-String -InputObject $myNamespace -Pattern $Pattern)) { return; }
        if ($Exclude -and !(Select-String -InputObject $myNamespace -Pattern $Exclude -NotMatch)) { return; }
    
        # need this to print the namespace name once, and only if the query succeeds (i.e. sufficient permissions)
        [bool]$printmyNamespaceName = $true;

        # banner to show current query, which will freeze for a few seconds if user has insufficient permissions
        Write-Progress -Id (
            [math]::Abs(($MyInvocation.MyCommand.Name).GetHashCode()) % [int]::MaxValue
        ) -Activity $MyInvocation.MyCommand.Name -Status "Get-WmiObject -Namespace $myNamespace -Class __Namespace";

        # query the namespace for children namespaces
        Get-WmiObject -Namespace $myNamespace -Class __Namespace -ErrorAction SilentlyContinue -ErrorVariable gwmiError |
        % {

            # only print the current namespace name once
            if ($printmyNamespaceName)
            {
                $myNamespace;
                $printmyNamespaceName = $false;
        
            } # if ($printmyNamespaceName)

            # warning: here be recursive warning: here be recursive warning: here be...
            & $MyInvocation.MyCommand.Name -Namespace "$myNamespace\$($_.Name)" -Pattern $Pattern -Exclude $Exclude;

        } # Get-WmiObject -Namespace $myNamespace -Class __Namespace ...

        if ($gwmiError)
        {
            Write-Warning "$($MyInvocation.MyCommand.Name) -Namespace $myNamespace query failed";

            if (!$Global:__GetWmiChildNamespaceExclude) { $Global:__GetWmiChildNamespaceExclude = $Exclude; }

            $excludedNameSpace = $myNamespace -replace '\\', '\\';

            if ([array]::IndexOf($Global:__GetWmiChildNamespaceExclude, $excludedNameSpace) -eq -1)
            {
                $Global:__GetWmiChildNamespaceExclude += $excludedNameSpace;

            } # if ([array]::IndexOf($Global:__GetWmiChildNamespaceExclude, ...

        } # if ($gwmiError)
        elseif ($printmyNamespaceName)
        {
            $myNamespace;
            $printmyNamespaceName = $false;
    
        } # if ($gwmiError)

        } # foreach ($mymyNamespace in $Namespace
    
    } # process

} # function Get-WmiChildNamespace

function Get-WmiChildPropertyData
{
    param (
        [parameter(ValueFromPipeline=$true)][string[]]$Namespace = @(),
        [string[]]$Class   = @(),
        [string[]]$Pattern = @('.*'),
        [string[]]$Exclude = @()
    );

    process
    {
        foreach ($myNamespace in $Namespace) 
        {

            # banner to show current query, which will freeze for a few seconds if user has insufficient permissions
            Write-Progress -Id (
                [math]::Abs(($MyInvocation.MyCommand.Name).GetHashCode()) % [int]::MaxValue
            ) -Activity $MyInvocation.MyCommand.Name -Status "Get-WmiObject -Namespace $Namespace -List";

            foreach ($myClass in & {
                if ($Class)
                {
                   Get-WmiObject -Namespace $myNamespace -Class $Class -ErrorAction SilentlyContinue -ErrorVariable gwiError;
                }
                else
                {
                    Get-WmiObject -Namespace $myNamespace -ErrorAction SilentlyContinue -ErrorVariable gwiError -List
                }
            }) {
                if ($Pattern -and !(Select-String -InputObject $myClass.Name -Pattern $Pattern)) { continue; }
                if ($Exclude -and !(Select-STring -InputObject $myClass.Name -Pattern $Exclude -NotMatch)) { continue; }

                $myClass.Properties |
                Select-Object -Property @{
                    n = "Namespace";
                    e = { $myNamespace; }
                }, @{
                    n = "Class";
                    e = { $myClass.Name; }
                }, Name, Type;
            
            } # foreach ($myClass in ( Get-WmiObject -Namespace $myNamespace ...
        
        } # foreach ($myNamespace in $Namespace) 

    } # process

} # function Get-WmiChildPropertyData