Creating CIM Scripts without Scripting

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.

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.

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.

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.

What do you think?

Get PowerShell Version with WMI

With the release of PowerShell 4.0, it is possible you might end up with a mix of systems in your environment. I know I do because I do a lot of writing, testing and development that requires multiple versions in my test network. Recently I was doing some Group Policy work when I thought about WMI filters. A WMI filter is a way to limit Group Policy to machines that meet certain criteria. I thought it would be useful to have a WMI filter that could determine if the computer was running PowerShell 3 or 4. Why? Well one reason would be so that v4 machines would update help from a local v4 source and likewise for v3. So I started looking a developing a WMI query.

That led to the development of this:

This function uses WMI to retrieve the instance of the CIM_DATAFILE class for PowerShell.exe. I have found when querying for this class to be as specific in your query as you can. This runs pretty quickly and returns some useful information. I can even run it for a group of computers.

Which gives me this:

The first two machines are running PowerShell 2.0, the next 2 v3 and the last 2 v4. Now that I know this works, I can create a WMI filter with a query like this:

This should filter only for computers running PowerShell 3.0.

I wrote this function thinking I would add support for alternate credentials. But if you don’t need it, you can also get the same information using the [WMI] type accelerator.

I needed to test and develop a SELECT query which is why I ended up with the function I did. The date information is extra and if you use Get-CIMInstance, the dates are converted for you.

Yes, I know you can get this information with Test-WSMan as well and that is certainly a much easier way. Although if by chance you still have PowerShell 1.0 in use, I don’t think that will work. Anyway, there you have it. A quick way using WMI to find out the PowerShell version and a query you can use to build a WMI filter for Group Policy.


UPDATE 11/14
I don’t know what I was thinking with my original query. It was much more complicated than it needed to be. I’ve updated my code samples with a better filter. Remember when using \ in any paths, it needs to be escaped which is why you see the name as you do.

Friday Fun: Create a PowerShell Trace Window

magnifying-glassWay back in the day, it was all VBScript and HTAs for me. I built a number of HTA tools for other people to use. As you might expect they didn’t always work and troubleshooting something I couldn’t see was difficult. So I came up with a solution to use an Internet Explorer window as a sort of immediate or trace window. In my HTA I added code to use this trace window. The user could then copy and paste the contents of the window and I would have a much better idea about what went wrong. I always thought this was a cool trick and figured why not do it in PowerShell?

Using the COM object you can create an instance of Internet Explorer and then use DOM methods and properties to configure the window and write text to it. Here’s the PowerShell version of what I used to do in VBScript.

The function is written with the assumption that you will use it within a script. I’ll have an example in a moment. The function first checks for a variable, presumably in the script scope, called TraceEnabled. If it is set to True, the function will continue. Then it checks to see if an existing trace window has already been created. If not, it creates it using some default properties for size, position, and color all of which you can change via parameters. Once the the window is created, your message string is written to the Internet Explorer window. I prepend the current date and time to the message. I also include some default user and system information when the IE window is first created. In the past this was often helpful, especially when dealing with less tech savvy users.

When you include the function in a script, everything should be cleaned up when you close the windows. The IE window will remain even after the script ends. This is handy because you can print from it, copy and paste or whatever. If you run your script from the PowerShell ISE, things don’t always clean up nicely so I added a -Terminate parameter (with an alias of -Kill). When you use this in the ISE run Trace -Kill to close the window and clean up.

So how would you use this?

Well, here’s a version of a script (which I don’t think I’ve published before so you get a bonus today) that gets some basic server health information and creates an HTML report.

As you look through this you’ll see a number of Trace commands. The trace script, which is dot sourced in this script, creates an alias of Trace. These commands won’t do anything unless $TraceEnabled is set to $True. This version of the script includes a -Trace switch which does just that. From there the script runs and all of my trace messages are written to an IE window.


If there is a problem, then I can see where the script is breaking down or failing. If all goes well, I end up with a report like this:


The server health script gets some information using Get-CIMInstance on memory, disk usage and event logs from that last day. The script will dynamically create a DCOM option if it detects that the remote computer is not running PowerShell 3.

You can use my Trace-Message function in conjunction with Write-Verbose. You can display the same information or different. It is up to you. You could probably accomplish the same result by using a Windows form or WPF via ShowUI. But everyone has Internet Explorer and this was pretty easy to pull together.

By the way, if you just want a copy of the server health script without the Trace commands, here you go:

Today you get twice the fun. What do you think about all of this? As always, I look forward to hearing what you think.

Friday Fun: 50 Shades of PowerShell HTML Reports

happyreport I’ve been working on a project for a client that includes creating an HTML report, generated by PowerShell. I originally thought I would include a certain feature but decided against it. However, this is so cool I thought I’d share it with you as a Friday Fun article. I’ve done alot this year with some advanced HTML scripting techniques and this one might come in handy.

I’m always looking for ways to add visual reinforcement to my HTML reports. And since keeping track of disk space is a common IT Pro task, I figured it would be nice to have a visual representation on disk utilization. So I created a short proof of concept script that generates an HTML report like this:


What do you think? Here’s how I did it.

The key element here is the addition of a gradient. In the script you can see that I’ve defined a here string for the gradient. The string includes code to support just about any browser, that’s why you see all the background-image lines. The here string also has place holders, {0} and {1} for the starting and ending percentages. I’ll explain how that works in a moment.

Using Get-CIMInstance, the script gets fixed logical disks. Now, instead of simply creating an HTML fragment, I create the fragment as an XML document. This allows me to add a caption to the table, using the computer name. Then I iterate through the table node, skipping the first row which is the table header. I create an attribute called Style.

Next, I get the value of the last cell, which is the PercentFree value. That’s one of the reasons I used an ordered hashtable so I could guarantee that the last cell would always be the PercentFree property. I grab the value and make sure it is an integer.

I can use this value and plug it in to my gradient here string using the -f operator.

I append the style attribute to each node. This allows me to set different values for each drive.

After going through the table rows, all that remains is to add the modified HTML, which is the InnerXML property to my array of fragments and create the final report.

The gradient isn’t absolute but it gives you a rough visual approximation of how much free space is on each drive. By the way, you can also use the gradient in the body element of a style sheet if you want to jazz up the background of your report.

I included plenty of comments in my code which I hope helps. If not, please leave a comment. Enjoy!

Runspaces, Remoting and Workflow, Oh My!

talkbubbleThe other day on Twitter I saw a message about new script in the Microsoft Script Center on getting remote event logs with WMI. So I took a look at the script. If you take a minute to look at the script you’ll quickly realize this is not a script for beginners. My initial thought was “Why?”. We already have cmdlets in PowerShell for querying event logs on remote computers. I realize the script was trying to avoid some of the issues we run into with WMI and I can’t question the effectiveness of his function. It works and with out a doubt using runspaces like this is faster than cmdlets.

My concern when I see scripts like this is that someone new to PowerShell will see it and run to the hills thinking they’ll never be able to use PowerShell and that is definitely not the case. So I decided to see what I could come up with that used a more IT Pro friendly cmdlet-based approach. I wanted to write something that most of you could have come up with.

My first attempt is a function that uses Invoke-Command to run the Get-Eventlog cmdlets in a remote session. In the function I define a scriptblock that gets all the event logs with records, and then gets all non-information or SuccessAudit events from those logs that have happended since midnight yesterday. My function supports credentials and takes advantage of a few other features from Invoke-Command.

The function works and took a about 1 1/2 min to query 8 machines in my virtual test environment. There is definitely some overhead when using Invoke-Command, but the trade off is a script that is a little easier to develop and maintain.

Then I thought, what about a workflow? I’m querying event logs but there’s no reason I can’t query all of them simultaneously. Here’s my workflow that does essentially the same thing as my function.

Interestingly, in my tests the workflow took about the same amount of time to run. But this is a shorter script to develop because all of the features like remoting, credentials and jobs are automatically part of the workflow. There is a potential downside in that all the remote machines must be running PowerShell 3.0. This workflow is also a great example in that workflows aren’t always the answer. There’s nothing really here, other than potentially the use of parallelism, that makes this a better choice than my function.

My last concern with the gallery script, and I don’t know if this would have an effect on its performance, is that all the event logs are rolled up in a property for each computer. This means you have to take some further steps to expand and format the results. My function and workflow, because they rely on Get-Eventlog, are formatted and ready to go.

What I haven’t tried yet, is how this same task can be done with Get-WinEvent or Get-CimInstance. The latter helps avoid some of the issues with WMI and might perform better. If I have time, I’ll get back to you with my results. But in the meantime, what do you think about all of this?

Using runspaces in PowerShell has a place, but definitely requires an advanced skill set and a willingness to accept a tradeoff of a more complicated script to develop and maintain (especially if someone else has to) with improved performance. I can see where using a runspace approach makes sense when you have 1000s of computers. But I might also argue that if that is your environment, you probably have full-blown management suites. Yes, I know there will always be exceptions. But for the majority of you, are you happy writing scripts that use existing cmdlets or do you feel obligated to learn .NET before you can use PowerShell?

I tried this using Get-CIMInstance. Yes, this requires PowerShell 3 remotely (unless you take an extra step to setup a DCOM CIM session) and the use of the WSMan protocol, but this performs suprisingly well and only takes a few lines of PowerShell.

I ran this in my test environment and it took about 30 seconds to return 188 event log entries. Because we’re using WSMAN we avoid some of the issues with RPC and DCOM. So here is a solution, at least in this case, that is as fast as using runspaces but only took a few minutes to write and is easy to follow.

Everything we do as IT Pros is a matter of balancing trade-offs and working within a set of limitations.