Summary: Learn how to use the Import-Counter cmdlet in Windows PowerShell to parse saved performance counter log files.

Hey, Scripting Guy! Question

Hey, Scripting Guy! We have configured a series of custom performance monitor logs that run as scheduled tasks and collect monitoring information. The problem is that I never seem to have time to run Performance Monitor to review these logs. Is there a way I can use Windows PowerShell to automate some of this?


Hey, Scripting Guy! Answer Hello CP,

Microsoft Scripting Guy, Ed Wilson, here. It is still cold here in Charlotte, North Carolina in the United States. My trip to Florida for SQL Saturday in Tampa was a welcome respite from the cold weather. In addition to warming up from the cold, I was able to get a lot of work done during the ride as the Scripting Wife drove. I love working while traveling because there is a complete lack of distraction. At home, there is always my book case of technical books that beckon to me.

CP, as I answer your email, I am sipping on a cup of English Breakfast Tea with a cinnamon stick for sweetener, and I am covered up with a blanket. I am beginning to wonder if I should wrap my teapot with an insulating blanket as well, but so far, the heavy ceramic seems to be doing just fine. The secret is to preheat the pot prior to adding the hot water and the tea.

Just as the performance of a teapot is improved with a bit of judicious monitoring and tweaking, so too is the performance of a server or workstation. Personally, I love PerfMon (that is the executable name of Performance Monitor and my personal term of endearment) and at one point I thought about writing a book about it—instead, I wrote a book about VBScript and the rest, as they say, is history. Now with the performance counter cmdlets in Windows PowerShell, I get to combine two of my passions.

The first thing I want to do is to create my own data collector set. The easiest way to do this is to open the Performance Monitor tool (Start/All Programs/Administrative Tools/Performance Monitor, or type perfmon in the Start/Search All Programs and Files text box). After Performance Monitor is running, select Performance Monitor from the left action pane. Now add the counters that you want to include in your Data Collector Sets. You add the counters by clicking the green plus (+) sign on the tool bar. After you click the green plus sign, you are able to select the counter category and drill down to individual counters. This technique is shown in the following screen shot.

Image of AddCounters dialog box

The nice thing about using the Performance Monitor tool to add counters is that it is easy to see the available instances of the counter. This means that you can add the __Total of all the instances, or you can add individual counter instances. The process is as easy as selecting the instance from the list, and clicking the Add button. This process is shown in the following screen shot.

Image of counter choices

After you have added the counters, look at the Performance Monitor chart, and examine each of the counters to see if they are returning the type of data that you wish to collect. I like to start at the top of the list of counters, and use the Control+H keyboard shortcut to highlight the selected counter. I can then use the Down arrow to view each of the counters in turn. If a counter shows a flat line, I may need to modify the default scale by choosing a different unit value. The Performance Monitor chart is shown in the following screen shot.

Image of Performance Monitor chart

When everything is working the way I like, I right-click the Performance Monitor tool from the Action menu in the left pane, and I select New Data Collector Set. The advantage of this is that I can give it a custom name, select a custom directory for storing the data (by default it saves in the %systemdrive%\PerfLogs\Admin\ folder), I can also select the user account that will be used when the Data Collector Set runs (by default it runs using the System account). This is shown in the following screen shot.

Image of Data Collector Set dialog box

When all this is accomplished, the Data Collector Set appears in the User Defined Data Collector Sets portion of the Performance Monitor tool. This is shown in the following screen shot.

Image of Performance Monitor menu

After I have run the Data Collector Set, I can import the performance information into a variable by using the Import-Counter Windows PowerShell cmdlet. I specify the variable to hold the data and the path to the saved Data Collector Set file. The command to do this is shown here.

$data = Import-Counter -Path 'C:\fso\System Monitor Log.blg'

After I have imported the counter set into a variable, I can use the techniques I discussed in Monday’s Hey, Scripting Guy! Blog post to parse the data.

If I want to see what Performance Counter Sets are captured in the System Monitor Log file, I use the ListSet parameter as illustrated here. (This is a single logical command that I broke at the pipeline character (|) so that the command would fit the blog format.)

PS C:\> Import-Counter -Path 'C:\fso\System Monitor Log.blg' -ListSet * |

Select-Object CounterSetName



Processor Information


Network Interface


Although the previous command gives me an indication of the type of data that is recorded in the log file, it does not tell me specifically what the log contains. To do this, I pipe the results to the Select-Object cmdlet and expand the Paths property. This technique is shown here. (This is a single logical command that I broke at the pipeline character (|) so that the command would fit the blog format.)

PS C:\> Import-Counter -Path 'C:\fso\System Monitor Log.blg' -ListSet * |

Select-Object paths -ExpandProperty paths

\\MRED1\Processor Information(*)\% Processor Time

\\MRED1\PhysicalDisk(*)\% Disk Time

\\MRED1\PhysicalDisk(*)\Avg. Disk Queue Length

\\MRED1\PhysicalDisk(*)\% Disk Read Time

\\MRED1\PhysicalDisk(*)\% Disk Write Time

\\MRED1\Network Interface(*)\Bytes Received/sec

\\MRED1\Network Interface(*)\Bytes Sent/sec

\\MRED1\Network Interface(*)\Bytes Total/sec

\\MRED1\Network Interface(*)\Current Bandwidth

\\MRED1\Memory\% Committed Bytes In Use

\\MRED1\Memory\Available MBytes

After I see which counter instances are stored in the log file, I may decide that I only want to retrieve data from one of the counters. To do this, I use the counter paths that I obtained from the previous command. One thing that you may want to do is to specify the ErrorAction preference for the command. This is because the Import-Counter cmdlet displays an error if a negative value is detected when importing the log. Personally, I find the error distracting, and I prefer to not see the message. The error message is shown here.

Import-Counter: A counter with a negative denominator value was detected.

At line:1 char:21

+ $proc=Import-Counter <<<< -Path 'C:\fso\System Monitor Log.blg' -counter "\Processor Information(*)\% Processor Time"

+ CategoryInfo : InvalidResult: (:) [Import-Counter], Exception

+ FullyQualifiedErrorId : CounterApiError,Microsoft.PowerShell.Commands.ImportCounterCommand

The command I use to import only the percentage of processor time is shown here.

PS C:\> $proc=Import-Counter -Path 'C:\fso\System Monitor Log.blg' -counter "\Process

or Information(*)\% Processor Time" -EA silentlycontinue

PS C:\>

The next thing I need to know is the time span of the log. To do this, I store the sorted results of the log in a variable. I then index into the array of data to retrieve the beginning and the ending time stamps. Because an array is zero-based, I need to use the count-1 property. This technique is shown here. (This is a single logical command that I broke at the pipeline character (|) so that the command would fit the blog format.)

PS C:\> $time = Import-Counter -Path 'C:\fso\System Monitor Log.blg' |

Sort-Object -Property timestamp

PS C:\> $time[0].timestamp

Monday, January 24, 2011 10:43:26 AM

PS C:\> $time[$time.count-1].timestamp

Monday, January 24, 2011 11:03:47 AM

If the time span of the log is good, I can simply work with the data that I imported into the variable. On the other hand, if I wish to “re-log” the data and select a custom time range, I can do that by specifying values for the starttime and endtime parameters that fall within the time range of the log. Because the starttime and the endtime parameters expect System.DataTime objects as parameter inputs, I only need to supply a string value that can be converted to a DataTime object. In addition, I do not need to specify seconds if I want to retrieve data from the beginning of each minute. This technique is shown here.

PS C:\> $data = Import-Counter -Path 'C:\fso\System Monitor Log.blg' -StartTime "1/24

/11 10:44:00" -EndTime "1/24/11 10:44:03"

PS C:\> $data | Format-Table -Property timestamp



1/24/2011 10:44:00 AM

1/24/2011 10:44:01 AM

1/24/2011 10:44:02 AM

1/24/2011 10:44:02 AM

I may wish to view a specific number of counter sets. To do this, I can use the maxsamples parameter and choose the number of sets to import. When I do this it imports the oldest counter sets first. The command that is shown here imports the three oldest counter sets and stores the results in the $max3 variable.

PS C:\> $max3 = Import-Counter -Path 'C:\fso\System Monitor Log.blg' -MaxSamples 3

PS C:\> $max3.count


On the other hand, I might want to display the three newest counter sets. To do this, I can use the range operator as shown here. (This time, I display the timestamps to the Windows PowerShell console, instead of storing the results in a variable.)

PS C:\> (Import-Counter -Path 'C:\fso\System Monitor Log.blg')[-1..-3] | Select-Objec

t -Property timestamp -ExpandProperty timestamp

Monday, January 24, 2011 11:03:47 AM

Monday, January 24, 2011 11:03:46 AM

Monday, January 24, 2011 11:03:45 AM

CP, that is all there is to using the Import-Counter cmdlet. I hope you enjoyed my article today as much as I enjoyed writing it. The fun of Performance Week will continue tomorrow when I will talk about exporting performance counters.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy