Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Creating CIM Scripts without Scripting

Posted on January 29, 2014January 29, 2014

When Windows 8 and Windows Server 2012 came out, along with PowerShell 3.0, we got our hands on some terrific technology in the form of the CIM cmdlets. Actually, we got much more than people realize. One of the reasons there was a big bump in the number of shipping modules and cmdlets was CDXML. With some work and a properly formatted XML file, you could create your own commands based on WMI and CIM classes. Modules like SMBShare were built the same way. But creating a CDXML based module is not a task for someone just getting started with PowerShell.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

So I decided to build a tool that just about anyone could use to create their own CIM-based commands, using relatively common PowerShell scripting techniques. You might find some of the things I do in my script just as interesting or useful. I was also motivated because I know that many IT Pros want to script but don't have the time or feel they don't have the skills. I hope what I've come up with will help jump start or accelerate the process.

My script, CIMScriptMaker.ps1, will guide you through identifying a WMI class and create an advanced function to get instances of that class. You can even add a filter and select which properties to display. You can then edit the function further if you want and end up with a practical tool that didn't take a lot of time to write. Let me walk you through the process. The script works in either the console or the ISE. I'll launch the script from the prompt.

PS C:\> C:\scripts\CIMScriptMaker.ps1
Enumerating namspaces on JH-WIN81-ENT....please wait...

The script defaults to the local computer, but you can specify a remote computer running PowerShell 3 or later. The script then enumerates the namespaces and presents a list using Out-Gridview. I use the title as a prompt.

I'll select Root\CimV2 and click OK. My script then queries all the classes in that namespace and again displays a list using Out-Gridview. What's nice is that you can use the filtering capability to quickly find a class you are interested in.

I'm going to select Win32_PageFileUsage and click OK. Using the Popup method from the old Wscript.Shell VBScript object I prompt if the user wants to test retrieving all instances of the selected class.

Naturally, I do. If there are results, they will be displayed in Out-Gridview again.

The script will wait for me to close the gridview. After which I'll be prompted to continue.

Next the script will display a list of properties for the class and ask if I want to filter.

If you opt to filter, you'll get a gridview with a list of operators to choose from. The default is =. Then you'll get a Visual Basic message box asking you to enter in a value. For this demo I clicked Cancel to skip filtering.

Next I am prompted to select the properties. Clicking cancel will in essence give you the full CIM object. If you select properties then behind the scenes I'm generating code that is piping a Get-CimInstance command to Select-Object. I'll select a few.

And that's it! The script will generate an advanced function based on the selections you have made. The function defaults to a name using the Get verb and the noun is the class name. The function is copied to the clipboard so that you can paste it into your script editor and also saved to a global variable, $cimscript, just in case you accidentally overwrite the clipboard

If you are in the console, you can right-click to paste it right into your current session. The function includes comment based help. The function can take computer names or you can use CIMSessions.

Here's what the generated code looks like.

#Requires -version 3.0

Function Get-Win32_PageFileUsage  {
<#
.Synopsis
Get Win32_PageFileUsage information
.Description
This command uses the CIM cmdlets to query a remote computer for information
from the Win32_PageFileUsage class in the Root\CIMV2 namespace. 
This command requires PowerShell 3.0 or later.
.Parameter Computername
The name of a computer to query. It should be running PowerShell 3.0 or later.
This parameter also supports aliases of CN and Host.
.Parameter CimSession
A previously created CimSession. Works best when you pipe the CimSession
to this command. See examples.
.Example
PS C:\> Get-Win32_PageFileUsage

Run the command defaulting to the local computername.
.Example
PS C:\> Get-CimSession | Get-Win32_PageFileUsage | Out-Gridview -title Get-Win32_PageFileUsage

Get all CIMSessions and pipe them to this command sending results to Out-Gridview.
.Notes
Version     : 1.0
Author      : JH-WIN81-ENT\Jeff
Last Updated: 1/29/2014
.Inputs
String or CimSession
.Outputs
CIMObject or custom object
.Link
Get-CimInstance
Get-CimSession
#>

[cmdletbinding(DefaultParameterSetName="Computer")]
Param(
[Parameter(Position=0,ValueFromPipelinebyPropertyName=$True,
ParameterSetName="Computer")]
[ValidateNotNullorEmpty()]
[Alias("CN","Host")]
[string[]]$Computername=$env:Computername,

[Parameter(Position=0,ValueFromPipeline=$True,
ParameterSetName="Session")]
[string[]]$CimSession

)

Begin {
 Write-Verbose "Starting command $($MyInvocation.Mycommand)"
 #create a hashtable of parameters to splat against Get-CimInstance
 $cimParam=@{
 Namespace = "Root\CIMV2"
 ClassName = "Win32_PageFileUsage "
 ErrorAction = "Stop" 
 }
} #begin

Process {
 if ($computername) {
   $cimParam.Computername=$computername
   Write-Verbose "Processing $Computername"
 }
 else {
   #must be a cimsession
   $cimParam.CIMSession=$CimSession
   Write-Verbose "Processing $($CimSession.ComputerName)"
 }
 
 Try {
    Get-CimInstance @cimParam | 
    Select-Object -property InstallDate,Name,Status,AllocatedBaseSize,CurrentUsage,PeakUsage,TempPageFile,PSComputername
 } #try
 Catch {
    Write-Warning "Failed to retrieve information. $($_.Exception.Message)"
 } #catch
} #Process

End {
 Write-Verbose "Ending command $($MyInvocation.Mycommand)"
} #end

} #end function

Now I have a CIM based tool that just works or I can develop it further.

Right now, all my script does is generate a command to get WMI information using Get-CimInstance. With a little more work I could probably have it generate commands that do things as well using WMI class methods. But that's for another day. In the mean time, here is my script.

#requires -version 3.0

<#
Version: 1.0
Author : Jeff Hicks
         @jeffhicks
         https://jdhitsolutions.com/blog

"Those who forget to script are doomed to repeat their work."

Learn more:
 PowerShell in Depth: An Administrator's Guide 
 PowerShell Deep Dives 
 Learn PowerShell 3 in a Month of Lunches 
 Learn PowerShell Toolmaking in a Month of Lunches 
 PowerShell and WMI 
 
  ****************************************************************
  * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED *
  * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK.  IF   *
  * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, *
  * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING.             *
  ****************************************************************
#>

Param([string]$computername = $env:COMPUTERNAME)

Function Get-Namespace {
#this function will recursively enumerate namespaces

Param(
[string]$Namespace="Root",
[Microsoft.Management.Infrastructure.CimSession]$CimSession
)

 $nspaces = $cimsession | Get-CimInstance -Namespace $Namespace -ClassName __Namespace
  foreach ($nspace in $nspaces) {

  $child = Join-Path -Path $Namespace -ChildPath $nspace.Name
  $child
  Get-Namespace $child $CimSession
  }
}

#create a CIMSession
$cimsess = New-CimSession -ComputerName $computername

#browse namespaces
Write-Host "Enumerating namspaces on $computername....please wait..." -ForegroundColor Cyan
$ns = Get-Namespace -CimSession $cimsess | Sort |
Out-GridView -Title "$($cimsess.Computername): Select a namespace" -OutputMode Single

if ($ns) {
    #get classes filtering out system classes
    Write-Host "Enumerating classes...please wait..." -ForegroundColor Cyan
    $class = $cimsess | Get-CimClass -Namespace $ns | 
    Where {$_.cimclassname -notmatch "^__" -AND $_.CimClassProperties.Name -notcontains "Antecedent"} | 
    Sort CimClassName | Select CimClassName,CimClassProperties |
    Out-GridView -Title "$NS : Select a class name" -OutputMode Single
}

if ($class) {

    #create a VBScript message box
    $wshell = New-Object -ComObject "Wscript.Shell"
    $r = $wshell.Popup("Do you want to test this class?",-1,$class.CimClassname,32+4)

    if ($r -eq 6) {
        #Yes
        $test = $cimsess | Get-CimInstance -Namespace $ns -ClassName $class.CimClassName 
        if ($test) {
         $test | Out-GridView -Title "$NS\$($Class.cimClassName)" -Wait
         $prompt="Do you want to continue?"
         $icon=32+4
        }
        else {
          $prompt="No results were returned. Do you want to continue?"
          $icon=16+4
        }

        $r = $wshell.Popup($prompt,-1,$class.CimClassname,$icon)
        if ($r -eq 7) {
          Write-Host "Exiting. Please try again later." -ForegroundColor Yellow
          #bail out
          Return
        }

    } #if r = 6

    #define basic command
    $cmd = "Get-CimInstance @cimParam"
    
    #create a filter
    $filterProperty = $class.CimClassProperties | Select Name,CimType,Flags |
    Out-GridView -Title "Select a property to filter on or cancel to not filter." -OutputMode Single

    if ($filterProperty) {
        $operator = "=","<",">","<>",">=","<=","like" | 
        Out-GridView -Title "Select an operator. Default if you cancel is =" -OutputMode Single

        #create a VBSCript inputbox
        Add-Type -AssemblyName "microsoft.visualbasic" -ErrorAction Stop 
        $Prompt = "Enter a value for your filter. If using a string, wrap the value in ''. If using Like, use % as the wildcard character."
        $title= "-filter ""$($filterproperty.Name) $operator ?"""
        $value=[microsoft.visualbasic.interaction]::InputBox($Prompt,$Title)

        $filter = "-filter ""$($filterproperty.Name) $operator $value"""
        
        $cmd+=" $filter"
    } #if filterproperty

    #show properties
    Write-Host "Getting class properties" -ForegroundColor Cyan
    $properties = $class.CimClassProperties | select Name,CimType,Flags | 
    Out-Gridview -Title "$($class.CimClassName) : Select one or more properties. Cancel will select *" -PassThru

    if ($properties) {
     $select = $properties.name -join ","
     $cmd+= @"
 | 
    Select-Object -property $select,PSComputername
"@
    } #if properties

} #if $class

#define a name for the function using the class name
$cmdName = "Get-$($class.CimClassName)"

#the auto-generated PowerShell code
$myScript = @"
#Requires -version 3.0

Function $cmdName  {
<#
.Synopsis
Get $($Class.CimClassName) information
.Description
This command uses the CIM cmdlets to query a remote computer for information
from the $($Class.CimClassName) class in the $NS namespace. 
This command requires PowerShell 3.0 or later.
.Parameter Computername
The name of a computer to query. It should be running PowerShell 3.0 or later.
This parameter also supports aliases of CN and Host.
.Parameter CimSession
A previously created CimSession. Works best when you pipe the CimSession
to this command. See examples.
.Example
PS C:\> $cmdName

Run the command defaulting to the local computername.
.Example
PS C:\> Get-CimSession | $cmdName | Out-Gridview -title $cmdName

Get all CIMSessions and pipe them to this command sending results to Out-Gridview.
.Notes
Version     : 1.0
Author      : $($env:userdomain)\$($env:username)
Last Updated: $((Get-Date).ToShortDateString())
.Inputs
String or CimSession
.Outputs
CIMObject or custom object
.Link
Get-CimInstance
Get-CimSession
#>

[cmdletbinding(DefaultParameterSetName="Computer")]
Param(
[Parameter(Position=0,ValueFromPipelinebyPropertyName=`$True,
ParameterSetName="Computer")]
[ValidateNotNullorEmpty()]
[Alias("CN","Host")]
[string[]]`$Computername=`$env:Computername,

[Parameter(Position=0,ValueFromPipeline=`$True,
ParameterSetName="Session")]
[string[]]`$CimSession

)

Begin {
 Write-Verbose "Starting command `$(`$MyInvocation.Mycommand)"
 #create a hashtable of parameters to splat against Get-CimInstance
 `$cimParam=@{
 Namespace = "$NS"
 ClassName = "$($Class.CimClassName) "
 ErrorAction = "Stop" 
 }
} #begin

Process {
 if (`$computername) {
   `$cimParam.Computername=`$computername
   Write-Verbose "Processing `$Computername"
 }
 else {
   #must be a cimsession
   `$cimParam.CIMSession=`$CimSession
   Write-Verbose "Processing `$(`$CimSession.ComputerName)"
 }
 
 Try {
    $cmd
 } #try
 Catch {
    Write-Warning "Failed to retrieve information. `$(`$_.Exception.Message)"
 } #catch
} #Process

End {
 Write-Verbose "Ending command `$(`$MyInvocation.Mycommand)"
} #end

} #end function
"@

$myScript | clip
$global:cimscript = $myScript

#remove the cimsession
$cimsess | Remove-CimSession

Write-Host "Your function, $cmdName, has been saved to the clipboard and in the global variable `$cimscript." -ForegroundColor Green

What do you think?


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

7 thoughts on “Creating CIM Scripts without Scripting”

  1. Kenny says:
    January 30, 2014 at 12:12 am

    Thank you very much and i find it a great way to accelerate my learning process by reviewing the generated code. Your Powershell courses on Pluralsight (http://www.pluralsight.com/training/Courses/TableOfContents/powershell-v3-essentials-it-pt1) was excellent.

  2. Pingback: Zajímavé novinky ze světa IT z 5. týdne 2014 | Igorovo
  3. Jacob says:
    February 6, 2014 at 3:04 pm

    This is incredible!

  4. Joe says:
    February 6, 2014 at 11:31 pm

    This could be very helpful for someone learning PS as you said and not sure how to find the info the really want. Good work

    1. Jeffery Hicks says:
      February 7, 2014 at 8:41 am

      Thank you.

  5. Pingback: Microsoft Most Valuable Professional (MVP) – Best Posts of the Week around Windows Server, MSExchange, SystemCenter and more – #66 - Dell TechCenter - TechCenter - Dell Community
  6. Pingback: Microsoft Most Valuable Professional (MVP) – Best Posts of the Week around Windows Server, MSExchange, SystemCenter and more – #66 - Windows Management - TechCenter - Dell Community

Comments are closed.

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d