Introduction
This script can be used for exporting specified performance counter values from multiple remote target servers to CSV file. The challenge is to gather the specific counter information and append it to CSV file with Powershell2.0.
Performance counters
The script will pull the below mentioned counter values
- Processor (_total)\% processor time
- system\processor queue length
- Memory\Available MBytes
- Memory\% committed bytes in use
- PhysicalDisk (*)\Current Disk Queue Length
- PhysicalDisk (*)\Avg. Disk sec/Transfer
- PhysicalDisk (*)\% Disk Time
- PhysicalDisk (*)\Disk Read Bytes/sec
- PhysicalDisk (*)\Disk Write Bytes/sec
- Network Interface (*)\Bytes Total/sec
Scenarios
IT Administrators may want to analyze the performance the servers with the help of perfmon counters information’s.
Prerequisites
- The source machine should have PowerShell 2.0 installed
Download the code:- http://gallery.technet.microsoft.com/PowerShell-Perfmon-0f013da8
Script
You can use this script in following ways:
1. Download the script.
2. Open the script file with Notepad or any other script editors (preferably Windows PowerShell ISE)
3. Change the Input file path and Output file if required other than the default location as shown in below screenshot.

4. Counters can be selected based on your requirement by changing the below code

5. Save the file then run the script via PowerShell in “Run as administrator” mode.
You can run this script manually or by scheduler task
How to create a scheduler task:
1. Open “Task Scheduler” (Goto START—RUN —Type Tasks and hit enter)
2. Click “Create task”
3. Pick a name, and choose “Run whether user is logged on or not”

Choose “Triggers” Tab, Click “New”
5. Specify option you like, and then click “OK” to create a trigger

Choose “Actions” tab, Click “New”
7. Copy following command to “Program/script” textbox, click C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
8. Enter the path of the saved script file in “Add arguments (optionally)” textbox
As per the screenshot I saved the file under C:\Perform_script.ps1 hence I updated the add arguments text box as
C:\Perform_script.ps1; exit”
Code:
<# # Script: Get-CounterStats # Author: Prashanth and Praveen # Comments: This script will collect the specific counters value from the multiple target machines/servers which will be used to analayze the performance of target servers. #> #Define Input and output filepath $servers=get-content "C:\servers.txt" $outfile="C:\perfmon.csv" ################################################################################################################ ################################################################################################################ #Actual script starts here function Global:Convert-HString { [CmdletBinding()] Param ( [Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [String]$HString )#End Param Begin { Write-Verbose "Converting Here-String to Array" }#Begin Process { $HString -split "`n" | ForEach-Object { $ComputerName = $_.trim() if ($ComputerName -notmatch "#") { $ComputerName } } }#Process End { # Nothing to do here. }#End }#Convert-HString #Function to have the customized output in CSV format function Export-CsvFile { [CmdletBinding(DefaultParameterSetName='Delimiter', SupportsShouldProcess=$true, ConfirmImpact='Medium')] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSObject] ${InputObject}, [Parameter(Mandatory=$true, Position=0)] [Alias('PSPath')] [System.String] ${Path}, #region -Append [Switch] ${Append}, #endregion [Switch] ${Force}, [Switch] ${NoClobber}, [ValidateSet('Unicode','UTF7','UTF8','ASCII','UTF32','BigEndianUnicode','Default','OEM')] [System.String] ${Encoding}, [Parameter(ParameterSetName='Delimiter', Position=1)] [ValidateNotNull()] [System.Char] ${Delimiter}, [Parameter(ParameterSetName='UseCulture')] [Switch] ${UseCulture}, [Alias('NTI')] [Switch] ${NoTypeInformation}) begin { # This variable will tell us whether we actually need to append # to existing file $AppendMode = $false try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Export-Csv', [System.Management.Automation.CommandTypes]::Cmdlet) #String variable to become the target command line $scriptCmdPipeline = '' # Add new parameter handling #region Dmitry: Process and remove the Append parameter if it is present if ($Append) { $PSBoundParameters.Remove('Append') | Out-Null if ($Path) { if (Test-Path $Path) { # Need to construct new command line $AppendMode = $true if ($Encoding.Length -eq 0) { # ASCII is default encoding for Export-CSV $Encoding = 'ASCII' } # For Append we use ConvertTo-CSV instead of Export $scriptCmdPipeline += 'ConvertTo-Csv -NoTypeInformation ' # Inherit other CSV convertion parameters if ( $UseCulture ) { $scriptCmdPipeline += ' -UseCulture ' } if ( $Delimiter ) { $scriptCmdPipeline += " -Delimiter '$Delimiter' " } # Skip the first line (the one with the property names) $scriptCmdPipeline += ' | Foreach-Object {$start=$true}' $scriptCmdPipeline += '{if ($start) {$start=$false} else {$_}} ' # Add file output $scriptCmdPipeline += " | Out-File -FilePath '$Path' -Encoding '$Encoding' -Append " if ($Force) { $scriptCmdPipeline += ' -Force' } if ($NoClobber) { $scriptCmdPipeline += ' -NoClobber' } } } } $scriptCmd = {& $wrappedCmd @PSBoundParameters } if ( $AppendMode ) { # redefine command line $scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock( $scriptCmdPipeline ) } else { # execute Export-CSV as we got it because # either -Append is missing or file does not exist $scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock( [string]$scriptCmd ) } # standard pipeline initialization $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } process { try { $steppablePipeline.Process($_) } catch { throw } } end { try { $steppablePipeline.End() } catch { throw } } } #Performance counters declaration function Get-CounterStats { param ( [String]$ComputerName = $ENV:ComputerName ) $Object =@() $Counter = @" Processor(_total)\% processor time system\processor queue length Memory\Available MBytes Memory\% committed bytes in use PhysicalDisk(*)\Current Disk Queue Length PhysicalDisk(*)\Avg. Disk sec/Transfer PhysicalDisk(*)\% Disk Time PhysicalDisk(*)\Disk Read Bytes/sec PhysicalDisk(*)\Disk Write Bytes/sec Network Interface(*)\Bytes Total/sec "@ (Get-Counter -ComputerName $ComputerName -Counter (Convert-HString -HString $Counter)).counterSamples | ForEach-Object { $path = $_.path New-Object PSObject -Property @{ computerName=$ComputerName; Counter = ($path -split "\\")[-2,-1] -join "-" ; Item = $_.InstanceName ; Value = [Math]::Round($_.CookedValue,2) datetime=(Get-Date -format "yyyy-MM-d hh:mm:ss") } } } #Collecting counter information for target servers foreach($server in $Servers) { $d=Get-CounterStats -ComputerName $server |Select-Object computerName,Counter,Item,Value,datetime $d |Export-CsvFile $outfile -Append -NoTypeInformation } #End of Script
Output:-


How can I contact you regarding SQL database information?
LikeLike
Hi,
You can write to me [email protected]
Best Regards,
Prashanth
LikeLike
This is a nice script. But I do have a question: how would I go about modifying this script to where it sends me an email/alert? I’m still learning Powershell, but this script is one of the best ones I’ve found for monitoring performance on my company’s servers.
But I would like to be able to send an email/alert if one of the servers were to reach its threshold for more than 5 minutes. For instance, if the Processor Time were to be at its peak at 100 for more than 5 minutes, I would like to get an email/alert saying which server has reached its threshold. Is it possible to modify the script with what I’m asking for?
LikeLike
Hi Prasanth, i need a script but don’t know if it’s possible (i’m newbie) that loops the registry for a value and deletes the value, and the upper key, suppose it founds a GUID\shell\open\data\value i need to delete the whole thing…is that possible ? Thx 4 your help.
LikeLike
Hi Pascal,
Make sure you take a backup of registry before trying any code.
I think this is what you are looking for
http://stackoverflow.com/questions/27015060/find-and-delete-change-registry-entry-with-powershell
–Prashanth
LikeLike
Hi Prashanth,
Thx for your reply i’m new to posh, i don’t know how to make it loop through HKCR/HKCU/HKLM and whenever it finds the value it deletes the key can u help me ? Nothin’ to do with posh but I’m Tamil living in France what about You ? Wish U an happy new year !
LikeLike
Get-counter:unable to connect to the specified computer or the computer is offline
Get-counter -computername -counter $ComputerName -Counter (Convert-HString -HString $Counter)).counterSamples |
Category info : invalid result[get-counter]exception
fullyqualified error: CounterAPI Error,Microsoft.Powershell.Commands.GetCounterCommand
Did you encounter any such issues while executing it?plz help me resolve it.I tried some methods from microsoft blog but nothing worked.
LikeLike
Hi Vinila,
Did you check the computer? Try to run the Powershell script for few servers. Make sure you have access and its online.
The error states the It connect to the specified computer or the computer is offline.
–Prashanth
LikeLike
Can you explain what you are trying to do? how are you running this script?
The way its being called is wrong
It should like this
Get-counter -computername $ComputerName -Counter (Convert-HString -HString $Counter)).counterSamples
LikeLike