Get-SCOMPerformanceData.ps1
. 'C:\Users\typaul\OneDrive - Microsoft\SCOM\MP\MPDev\MPProjects\SCOMAgentHelper\SCOMHelper\Resources\vX.XX_ModuleFiles\SCOMHelper\Private.ps1' Function Get-SCOMPerformanceData { <# .SYNOPSIS Retrieves performance data from SCOM using the Operations Manager PowerShell SDK. .DESCRIPTION The Get-SCOMPerformanceData function retrieves performance counter data from System Center Operations Manager (SCOM) using the native PowerShell SDK. It can query performance data by monitoring object GUID or computer name, with flexible filtering options for specific performance counters and time ranges. The function retrieves all related monitoring objects for the specified target and collects performance data from all available performance counters. It calculates statistical values (last, maximum, average) for each counter over the specified time period. This function requires an active SCOM management group connection established via New-SCOMManagementGroupConnection. .PARAMETER Id The GUID of the monitoring object to retrieve performance data for. This parameter is mandatory when using the 'ById' parameter set. Must be a valid GUID format. Use the ObjectId alias for convenience. .PARAMETER ComputerName The display name of the computer to retrieve performance data for. This parameter is mandatory when using the 'ByComputerName' parameter set. The function will automatically resolve the computer name to its corresponding monitoring object GUID. .PARAMETER HoursLookback The number of hours to look back for performance data samples. Default is 72 hours (3 days). This parameter is ignored when PerfObjectsOnly is specified. .PARAMETER CounterNames An array of specific performance counter names to filter results. If not specified or empty, all available performance counters will be returned. Counter names are case-sensitive and should match exactly (e.g., "% Processor Time", "Free Megabytes"). .PARAMETER PerfObjectsOnly When specified, returns only the performance counter objects without retrieving actual performance data samples. This is useful for discovering available counters or when you only need metadata. .INPUTS None. This function does not accept pipeline input. .OUTPUTS [PSCustomObject[]] or [Microsoft.EnterpriseManagement.Monitoring.PerformanceData[]] When PerfObjectsOnly is false (default), returns PSCustomObject array with properties: - MonitoringRuleId: GUID of the monitoring rule collecting this data - MonitoringRuleDisplayName: Display name of the monitoring rule - MonitoringRuleDescription: Description of the monitoring rule - MonitoringObjectDisplayName: Display name of the target object - MonitoringObjectId: GUID of the target monitoring object - MonitoringObjectPath: Full path of the monitoring object - MonitoringObjectFullName: Full name including class information - MonitoringClassId: GUID of the monitoring class - ObjectName: Performance object name (e.g., "Processor", "Memory") - CounterName: Performance counter name (e.g., "% Processor Time") - InstanceName: Counter instance name (e.g., "_Total", "C:") - ScaleFactor: Scaling factor applied to counter values - LastValue: Most recent sample value - MaximumValue: Maximum value in the time range - AverageValue: Average value in the time range When PerfObjectsOnly is true, returns native SCOM PerformanceData objects. .EXAMPLE # Connect to SCOM and get basic performance data for a server New-SCOMManagementGroupConnection -ComputerName "scom.domain.com" $PerfData = Get-SCOMPerformanceData -ComputerName "SQL01.domain.com" -HoursLookback 96 $PerfData | Select-Object ObjectName, CounterName, InstanceName, LastValue, AverageValue | Format-Table This example retrieves all performance data for the last 96 hours for SQL01 and displays key metrics in a table format. .EXAMPLE # Get specific performance counters for a monitoring object $ObjectId = "c5a9c43c-84d9-a3c2-6e84-c9d68bf79aa1" $Counters = @("% Processor Time", "Free Megabytes", "% Free Space") $PerfData = Get-SCOMPerformanceData -Id $ObjectId -CounterNames $Counters -HoursLookback 12 -Verbose # Analyze CPU utilization $CPUData = $PerfData | Where-Object { $_.CounterName -eq "% Processor Time" } Write-Host "Average CPU: $([math]::Round($CPUData.AverageValue, 2))%" Write-Host "Peak CPU: $([math]::Round($CPUData.MaximumValue, 2))%" This example retrieves specific performance counters and analyzes CPU utilization statistics. .EXAMPLE # Discover available performance counters without retrieving data $PerfObjects = Get-SCOMPerformanceData -ComputerName "WEB01.domain.com" -PerfObjectsOnly $UniqueCounters = $PerfObjects | Select-Object ObjectName, CounterName, InstanceName | Sort-Object ObjectName, CounterName $UniqueCounters | Group-Object ObjectName | ForEach-Object { Write-Host "Performance Object: $($_.Name)" -ForegroundColor Green $_.Group | ForEach-Object { Write-Host " $($_.CounterName) [$($_.InstanceName)]" } } This example discovers all available performance counters for a web server without retrieving actual performance data. .EXAMPLE # Monitor disk space across multiple time periods $DiskCounters = @("Free Megabytes", "% Free Space") $Last96Hours = Get-SCOMPerformanceData -ComputerName "FILE01.domain.com" -CounterNames $DiskCounters -HoursLookback 96 $Last7Days = Get-SCOMPerformanceData -ComputerName "FILE01.domain.com" -CounterNames $DiskCounters -HoursLookback 168 # Compare disk usage trends $DiskComparison = $Last96Hours | ForEach-Object { $Counter = $_ $SevenDayAvg = ($Last7Days | Where-Object { $_.ObjectName -eq $Counter.ObjectName -and $_.CounterName -eq $Counter.CounterName -and $_.InstanceName -eq $Counter.InstanceName }).AverageValue [PSCustomObject]@{ Drive = $Counter.InstanceName Counter = $Counter.CounterName Last96HrAvg = [math]::Round($Counter.AverageValue, 2) Last7DayAvg = [math]::Round($SevenDayAvg, 2) Trend = if ($Counter.AverageValue -lt $SevenDayAvg) { "Decreasing" } else { "Increasing" } } } $DiskComparison | Format-Table This example compares disk usage trends between the last 96 hours and last 7 days. .EXAMPLE # Export performance data to CSV for analysis $Servers = @("SQL01.domain.com", "WEB01.domain.com", "APP01.domain.com") $AllPerfData = @() foreach ($Server in $Servers) { Write-Host "Collecting data from $Server..." $ServerData = Get-SCOMPerformanceData -ComputerName $Server -HoursLookback 48 $ServerData | ForEach-Object { $_ | Add-Member -NotePropertyName "ServerName" -NotePropertyValue $Server } $AllPerfData += $ServerData } $AllPerfData | Export-Csv -Path "C:\Reports\PerformanceData.csv" -NoTypeInformation Write-Host "Performance data exported to C:\Reports\PerformanceData.csv" This example collects performance data from multiple servers and exports it to CSV for further analysis in Excel or other tools. .EXAMPLE # Find performance bottlenecks across different object types $BottleneckCounters = @("% Processor Time", "Available MBytes", "Current Disk Queue Length", "% Disk Time") $PerfData = Get-SCOMPerformanceData -Id "c5a9c43c-84d9-a3c2-6e84-c9d68bf79aa1" -CounterNames $BottleneckCounters -HoursLookback 6 # Identify potential issues $Issues = $PerfData | Where-Object { ($_.CounterName -eq "% Processor Time" -and $_.AverageValue -gt 80) -or ($_.CounterName -eq "Available MBytes" -and $_.AverageValue -lt 1024) -or ($_.CounterName -eq "Current Disk Queue Length" -and $_.AverageValue -gt 2) -or ($_.CounterName -eq "% Disk Time" -and $_.AverageValue -gt 85) } if ($Issues) { Write-Host "Performance Issues Detected:" -ForegroundColor Red $Issues | ForEach-Object { Write-Host "- $($_.ObjectName)\$($_.CounterName)[$($_.InstanceName)]: Avg=$($_.AverageValue), Max=$($_.MaximumValue)" -ForegroundColor Yellow } } else { Write-Host "No performance issues detected in the last 6 hours." -ForegroundColor Green } This example identifies potential performance bottlenecks by checking key system counters against known thresholds. .EXAMPLE $PerfObjects = Get-SCOMPerformanceData -ComputerName "sql23.contoso.com" -PerfObjectsOnly # Hand pick individual counters $Counters = $PerfObjects | Out-GridView -PassThru # Example 1: use counter names that are stored in $Counters, previous 7 days # $PerfData = Get-SCOMPerformanceData -ComputerName "SQL23.contoso.com" -HoursLookback 168-CounterNames $Counters.CounterName # Example 2: hard code the counter names, previous 7 days # $PerfData = Get-SCOMPerformanceData -ComputerName "SQL23.contoso.com" -HoursLookback 168 -CounterNames '% Processor Time','% Free Space','Available MBytes' # Filter example 1, Time range, use 2nd element in the $PerfData array $PerfData[1].RawData | ? {$_.TimeSampled -lt (Get-Date).AddDays(-1) -and $_.TimeSampled -gt (Get-Date).AddDays(-2) } # Filter example 2, use Get-Date with (-) negative days offset $PerfData | Where-Object {$_.CounterName -eq '% Processor Time' -and $_.InstanceName -eq '_Total'} | Select-Object RawData -ExpandProperty RawData | Where-Object {$_.TimeSampled -lt (Get-Date).AddDays(-1) -and $_.TimeSampled -gt (Get-Date).AddDays(-2) } # Filter example 3, use specific countername, specific datestamps $procRawData = $PerfData | Where-Object {$_.CounterName -eq '% Processor Time' -and $_.InstanceName -eq '_Total'} | Select-Object RawData -ExpandProperty RawData $procRawData | Where-Object {$_.TimeSampled -gt '2025-07-06 12:00:00 AM' -and $_.TimeSampled -lt '2025-07-07 12:00:00 PM' } .NOTES Author: Tyson Paul (https://monitoringguys.com/2019/11/12/scomhelper/) Version History: 2025.06.27 - 1.0: Initial version Requirements: - SCOM 2012 or later with PowerShell SDK installed - Active SCOM management group connection (New-SCOMManagementGroupConnection) - Appropriate SCOM permissions to read monitoring objects and performance data - PowerShell 3.0 or later Performance Considerations: - Large time ranges (HoursLookback) can result in significant memory usage and longer execution times - Consider filtering by CounterNames when you only need specific metrics - Use PerfObjectsOnly for discovery scenarios to avoid unnecessary data retrieval - The function processes all related monitoring objects, which can be extensive for complex systems Troubleshooting: - Ensure SCOM management group connection is established before calling this function - Verify the computer name exists in SCOM and is properly monitored - Check that the monitoring object GUID is valid and accessible - Use -Verbose for detailed execution information Common Counter Names: - CPU: "% Processor Time" - Memory: "Available MBytes", "% Committed Bytes In Use" - Disk: "Free Megabytes", "% Free Space", "Current Disk Queue Length", "% Disk Time" - Network: "Bytes Total/sec", "Packets/sec" .LINK New-SCOMManagementGroupConnection Get-SCOMClassInstance Get-SCOMClass .FUNCTIONALITY SCOM SDK, Performance Monitoring, System Center Operations Manager #> [CmdletBinding(DefaultParameterSetName = "ByComputerNameWithCounters")] Param ( [Parameter(Mandatory = $true, ParameterSetName = "ByComputerNameWithCounters")] [Parameter(Mandatory = $true, ParameterSetName = "ByComputerNameObjectsOnly")] [string]$ComputerName, [Parameter( Mandatory = $true, ParameterSetName = "ByIdWithCounters", HelpMessage = "The GUID of the entity for which to retrieve performance counters")] [Parameter( Mandatory = $true, ParameterSetName = "ByIdObjectsOnly", HelpMessage = "The GUID of the entity for which to retrieve performance counters")] [Alias('ObjectId')] [ValidateScript({ if ([Guid]::TryParse($_, [ref][Guid]::Empty)) { $true } else { throw "Invalid GUID format. Please provide a valid GUID string." } })] [Object]$Id, [Parameter(Mandatory = $false, ParameterSetName = "ByComputerNameWithCounters")] [Parameter(Mandatory = $false, ParameterSetName = "ByIdWithCounters")] [int]$HoursLookback = 72, [Parameter(Mandatory = $true, ParameterSetName = "ByComputerNameWithCounters")] [Parameter(Mandatory = $true, ParameterSetName = "ByIdWithCounters")] [ValidateNotNullOrEmpty()] [string[]]$CounterNames, [Parameter(Mandatory = $true, ParameterSetName = "ByComputerNameObjectsOnly")] [Parameter(Mandatory = $true, ParameterSetName = "ByIdObjectsOnly")] [switch]$PerfObjectsOnly ) ########################################### Function Get-RMORecursive { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] $ClassInstance ) $arr = @() $arr += $ClassInstance # Get all related monitoring objects recursively $relatedObjects = $ClassInstance.GetRelatedMonitoringObjects() ForEach ($relatedObject in $relatedObjects) { # Recursively get related monitoring objects for this object $arr += (Get-RMORecursive -ClassInstance $relatedObject) } Return $arr }#end Function Get-RMORecursive ########################################### If ($ComputerName.Length -gt 0) { # If we are using the ComputerName parameter, we need to get the Id of the MonitoringObject try { # Only supports Microsoft.Windows.Computer class at this time. $Id = Get-SCOMIdFromHostName $ComputerName } Catch { Write-Error "Failed to get MonitoringObjectId for ComputerName '$ComputerName'. Error: $_" return $false } } Write-Verbose "Getting Performance Data for MonitoringObjectId: $Id" If (-NOT $PerfObjectsOnly) { Write-Verbose "Looking back $HoursLookback hours for performance data." } Else { Write-Verbose "Retrieving performance objects only, no data samples will be returned." } Write-Verbose "Counter Names Provided: $CounterNames" $Instance = Get-SCOMClassInstance -Id $Id # Get related monitoring objects recursively because, sadly, the standard method only returns the direct instances, not nested ones. $allRMO = Get-RMORecursive -ClassInstance $Instance ForEach ($obj in $allRMO) { #$obj.GetMonitoringPerformanceData() | ForEach-Object { ForEach ($objPerf in $obj.GetMonitoringPerformanceData()) { $perfDataRaw = $null if ($objPerf.CounterName -in $CounterNames -OR $CounterNames.Count -eq 0) { Write-Verbose "Selected Counter: $($objPerf.ObjectName)/$($objPerf.CounterName)/$($objPerf.InstanceName)" If (-NOT $PerfObjectsOnly) { $SampleStartTime = (Get-Date).AddHours(-$HoursLookback) $SampleEndTime = (Get-Date) $perfDataRaw = $objPerf | ForEach-Object {$objPerf.GetValues($SampleStartTime,$SampleEndTime)} } Else { # If we only want the performance objects, we can just return the object itself $objPerf } } Else { #TODO Test this case Continue } if ($perfDataRaw -and $perfDataRaw.Count -gt 0) { [double]$LastValue = $perfDataRaw[-1].SampleValue [double]$MaximumValue = ($perfDataRaw | Measure-Object -Property SampleValue -Maximum).Maximum [double]$AverageValue = ($perfDataRaw | Measure-Object -Property SampleValue -Average).Average $tmpObj = [PSCustomObject]@{ MonitoringRuleId = $objPerf.MonitoringRuleId MonitoringRuleDisplayName = $objPerf.MonitoringRuleDisplayName MonitoringRuleDescription = $objPerf.MonitoringRuleDescription MonitoringObjectDisplayName = $objPerf.MonitoringObjectDisplayName MonitoringObjectId = $objPerf.MonitoringObjectId MonitoringObjectPath = $objPerf.MonitoringObjectPath MonitoringObjectFullName = $objPerf.MonitoringObjectFullName MonitoringClassId = $objPerf.MonitoringClassId ObjectName = $objPerf.ObjectName CounterName = $objPerf.CounterName InstanceName = $objPerf.InstanceName ScaleFactor = $objPerf.ScaleFactor LastValue = $LastValue MaximumValue = $MaximumValue AverageValue = $AverageValue RawData = @($perfDataRaw) } $tmpObj } } } }#end Function Get-SCOMPerformanceData # $PerfObjects = Get-SCOMPerformanceData -ComputerName "sql23.contoso.com" -PerfObjectsOnly $Counters = $PerfObjects | Out-GridView -PassThru # $PerfData = Get-SCOMPerformanceData -ComputerName "SQL23.contoso.com" -HoursLookback 168-CounterNames $Counters.CounterName # $PerfData = Get-SCOMPerformanceData -ComputerName "SQL23.contoso.com" -HoursLookback 96 -CounterNames '% Processor Time','% Free Space','Available MBytes' #TODO Add filtering example $PerfData[1].RawData | ? {$_.TimeSampled -lt (Get-Date).AddDays(-1) -and $_.TimeSampled -gt (Get-Date).AddDays(-2) } # Example of filtering performance data for a specific counter and time range # Note, the time range is only applicable to the raw data in the OperationsManager DB $PerfData | Where-Object {$_.CounterName -eq '% Processor Time' -and $_.InstanceName -eq '_Total'} | Select-Object RawData -ExpandProperty RawData | Where-Object {$_.TimeSampled -lt (Get-Date).AddDays(-1) -and $_.TimeSampled -gt (Get-Date).AddDays(-2) } $procRawData = $PerfData | Where-Object {$_.CounterName -eq '% Processor Time' -and $_.InstanceName -eq '_Total'} | Select-Object RawData -ExpandProperty RawData $procRawData | Where-Object {$_.TimeSampled -gt '2025-07-06 12:00:00 AM' -and $_.TimeSampled -lt '2025-07-07 12:00:00 PM' } $procRawData | Where-Object {$_.TimeSampled -gt '2025-07-04 12:00:00 AM' } | select timesampled #TODO add raw data to Get-SCOMObjectAvailability "SQL23.contoso.com","SQL24.contoso.com" | % {Get-SCOMPerformanceData -ComputerName $_ -HoursLookback 96 -CounterNames $counters.countername} |