Public/Get-SCOMDWPerformanceData.ps1
|
Function Get-SCOMDWPerformanceData { <# .SYNOPSIS Retrieves performance counter data from the SCOM Data Warehouse for specified monitoring objects and time ranges. .DESCRIPTION The Get-SCOMDWPerformanceData function queries the SCOM (System Center Operations Manager) Data Warehouse to retrieve actual performance counter data values for specific monitoring objects. Unlike the signature function that shows what counters are available from the raw perf table, this function returns the actual performance data samples with timestamps and values. The function supports two main modes of operation: 1. Retrieve data for specific performance counters by providing RuleId, CounterName, and ObjectName. Designed for piping perf signature in. 2. Retrieve all available performance data for a monitoring object (when using NoRule parameter sets) Key features include: - Support for both hourly and daily aggregated data - Flexible date range filtering - Multiple input methods (computer name or monitoring object GUID) - Pipeline support for bulk processing - Automatic data warehouse server discovery - Comprehensive error handling and validation This function is particularly useful for: - Performance trend analysis and reporting - Capacity planning and resource utilization studies - Troubleshooting performance issues - Creating custom performance dashboards - Automated performance monitoring and alerting .PARAMETER DataWarehouseServerName Specifies the name of the SCOM Data Warehouse server. If not provided, the function will attempt to auto-discover the server name from the registry or SCOM PowerShell cmdlets. Type: String Required: False Default: Auto-discovered from SCOM configuration .PARAMETER DataWarehouseDatabaseName Specifies the name of the SCOM Data Warehouse database. Type: String Required: False Default: "OperationsManagerDW" .PARAMETER QueryTimeout Specifies the time in seconds to wait for the SQL commands to execute before timing out. Type: Integer Required: False Default: 30 seconds Range: 1-3600 seconds .PARAMETER ConnectionTimeout Specifies the time in seconds to wait for a database connection to open before timing out. Type: Integer Required: False Default: 15 seconds Range: 1-300 seconds .PARAMETER ComputerName Specifies the name of the computer for which to retrieve performance data. This parameter is used in the 'ByComputerNameNoRule' parameter set for retrieving all performance data for the computer. Type: String Required: True (when using ByComputerNameNoRule parameter set) .PARAMETER MonitoringObjectId Specifies the GUID of the monitoring object for which to retrieve performance data. This parameter accepts pipeline input and supports multiple aliases for flexibility. Type: Object (GUID) Required: True (when using Id-based parameter sets) Aliases: ObjectId, ManagedEntityGuid Pipeline Support: ValueFromPipelineByPropertyName Validation: Must be a valid GUID format .PARAMETER StartDate Specifies the start date for the performance data retrieval range. Only data from this date forward will be included in the results. Type: DateTime Required: False Default: 30 days ago from current date .PARAMETER EndDate Specifies the end date for the performance data retrieval range. Only data up to this date will be included in the results. Type: DateTime Required: False Default: Current date and time .PARAMETER LanguageCode Specifies the language code for localized text retrieval from the data warehouse. Type: String Required: False Default: "ENU" (English US) .PARAMETER AggregationType Specifies whether to retrieve hourly or daily aggregated performance data. Type: String Required: False Default: "Hourly" ValidateSet: "Hourly", "Daily" .PARAMETER RuleId Specifies the GUID of the performance collection rule for which to retrieve data. This parameter is mandatory when using rule-specific parameter sets and supports pipeline input. Type: String Required: True (when using WithRule parameter sets) Pipeline Support: ValueFromPipelineByPropertyName .PARAMETER CounterName Specifies the name of the performance counter for which to retrieve data. Examples: "% Processor Time", "Available MBytes", "% Free Space" Type: String Required: True (when using WithRule parameter sets) Pipeline Support: ValueFromPipelineByPropertyName .PARAMETER ObjectName Specifies the name of the performance object for which to retrieve data. Examples: "Processor", "Memory", "LogicalDisk" Type: String Required: True (when using WithRule parameter sets) Pipeline Support: ValueFromPipelineByPropertyName .EXAMPLE Get-SCOMDWPerformanceData -MonitoringObjectId "68e53d0e-955f-1671-9dd4-ac1c0d80e8b8" -RuleId "12345678-1234-1234-1234-123456789012" -CounterName "% Processor Time" -ObjectName "Processor" Description: Retrieves hourly aggregated processor time performance data for the specified monitoring object using a specific performance collection rule. This returns the last 30 days of data by default. Output: Returns performance data objects with timestamps, values, and aggregation statistics for the processor utilization of the specified system. .EXAMPLE Get-SCOMDWPerformanceData -MonitoringObjectId "874432c3-ed32-6297-137c-9458657a4b51" -RuleId "10639f06-7913-fb2a-1610-86e169d8f93c1" -CounterName "Free Megabytess" -ObjectName "LogicalDisk" -StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date) -AggregationType "Daily" Description: Retrieves daily aggregated memory availability data for the last 7 days. This example shows how to specify custom date ranges and aggregation types for more focused analysis. Output: Returns daily aggregated memory availability statistics including minimum, maximum, and average values for each day in the specified range. .EXAMPLE $signatures = Get-SCOMDWPerformanceDataSignature -ComputerName "SQL01.contoso.com" | Where-Object {$_.ObjectName -eq "Processor"} $perfData = $signatures | Get-SCOMDWPerformanceData -StartDate (Get-Date).AddDays(-60) Description: Pipeline example that first gets all processor-related performance counter signatures for a computer, then retrieves the actual performance data for those counters over the last 60 days. This demonstrates how to combine the signature and data functions for comprehensive analysis. Output: Returns performance data for all processor-related counters configured on the specified server, allowing for complete CPU performance analysis. .EXAMPLE $servers = @("SQL01.contoso.com", "WEB01.contoso.com", "APP01.contoso.com") $memoryData = foreach ($server in $servers) { $objId = Get-SCOMIdFromHostName $server Get-SCOMDWPerformanceData -MonitoringObjectId $objId -RuleId "memory-rule-guid" -CounterName "Available MBytes" -ObjectName "Memory" -AggregationType "Daily" -StartDate (Get-Date).AddDays(-30) } $memoryData | Export-Csv -Path "C:\Reports\MemoryTrends.csv" -NoTypeInformation Description: Bulk processing example that retrieves memory performance data from multiple servers and exports the results to a CSV file for further analysis. This shows how to automate performance data collection across an entire environment. Output: CSV file containing 30 days of daily memory availability data from all specified servers, suitable for trend analysis and capacity planning. .EXAMPLE $criticalAlert = Get-SCOMDWPerformanceData -MonitoringObjectId "guid-here" -RuleId "disk-rule-guid" -CounterName "% Free Space" -ObjectName "LogicalDisk" -StartDate (Get-Date).AddHours(-24) -AggregationType "Hourly" $lowSpaceHours = $criticalAlert | Where-Object {$_.AverageValue -lt 10} if ($lowSpaceHours) { Write-Warning "Critical disk space detected in the last 24 hours!" $lowSpaceHours | Select-Object DateTime, AverageValue, MinValue | Format-Table -AutoSize } Description: Monitoring example that checks for critical disk space conditions in the last 24 hours. This demonstrates how to use the function for real-time alerting and threshold monitoring. Output: Warning message and formatted table showing periods when disk free space fell below 10%, useful for immediate attention and capacity management. .EXAMPLE $performanceBaseline = Get-SCOMDWPerformanceData -MonitoringObjectId "web-server-guid" -RuleId "cpu-rule-guid" -CounterName "% Processor Time" -ObjectName "Processor" -StartDate (Get-Date).AddDays(-90) -EndDate (Get-Date).AddDays(-30) -AggregationType "Daily" $recentPerformance = Get-SCOMDWPerformanceData -MonitoringObjectId "web-server-guid" -RuleId "cpu-rule-guid" -CounterName "% Processor Time" -ObjectName "Processor" -StartDate (Get-Date).AddDays(-7) -AggregationType "Daily" $baselineAvg = ($performanceBaseline | Measure-Object -Property AverageValue -Average).Average $recentAvg = ($recentPerformance | Measure-Object -Property AverageValue -Average).Average $percentChange = [math]::Round((($recentAvg - $baselineAvg) / $baselineAvg) * 100, 2) Write-Host "Baseline CPU Average (60-30 days ago): $([math]::Round($baselineAvg, 2))%" Write-Host "Recent CPU Average (last 7 days): $([math]::Round($recentAvg, 2))%" Write-Host "Performance Change: $percentChange%" Description: Advanced analysis example that compares recent performance against a historical baseline to identify performance trends and changes. This demonstrates using the function for capacity planning and performance trend analysis. Output: Console output showing baseline vs recent CPU utilization with percentage change calculation, helping identify performance degradation or improvement trends. .EXAMPLE $multiCounterData = @() $counters = @( @{RuleId="cpu-rule-guid"; CounterName="% Processor Time"; ObjectName="Processor"}, @{RuleId="memory-rule-guid"; CounterName="Available MBytes"; ObjectName="Memory"}, @{RuleId="disk-rule-guid"; CounterName="% Free Space"; ObjectName="LogicalDisk"} ) foreach ($counter in $counters) { $data = Get-SCOMDWPerformanceData -MonitoringObjectId "server-guid" @counter -StartDate (Get-Date).AddDays(-7) -AggregationType "Hourly" $data | Add-Member -NotePropertyName "CounterType" -NotePropertyValue "$($counter.ObjectName)\$($counter.CounterName)" $multiCounterData += $data } $multiCounterData | Group-Object CounterType | ForEach-Object { Write-Host "`n$($_.Name) Statistics (Last 7 Days):" $stats = $_.Group | Measure-Object -Property AverageValue -Average -Maximum -Minimum Write-Host " Average: $([math]::Round($stats.Average, 2))" Write-Host " Maximum: $([math]::Round($stats.Maximum, 2))" Write-Host " Minimum: $([math]::Round($stats.Minimum, 2))" } Description: Comprehensive monitoring example that retrieves multiple performance counters and provides statistical analysis for each. This shows how to create a complete system health overview using multiple performance metrics. Output: Grouped statistical summary for CPU, memory, and disk performance over the last 7 days, providing a comprehensive system health report. .EXAMPLE # Pipeline processing with error handling $serverList = Import-Csv "C:\ServerList.csv" # Contains columns: ServerName, RuleId, CounterName, ObjectName $allPerformanceData = $serverList | ForEach-Object { try { $objId = Get-SCOMIdFromHostName $_.ServerName Get-SCOMDWPerformanceData -MonitoringObjectId $objId -RuleId $_.RuleId -CounterName $_.CounterName -ObjectName $_.ObjectName -StartDate (Get-Date).AddDays(-30) -ErrorAction Stop } catch { Write-Warning "Failed to process $($_.ServerName): $($_.Exception.Message)" $null } } | Where-Object {$_ -ne $null} $allPerformanceData | Export-Csv -Path "C:\Reports\MonthlyPerformanceReport.csv" -NoTypeInformation Write-Host "Successfully processed $($allPerformanceData.Count) performance data records" Description: Enterprise-scale example showing how to process performance data for multiple servers and counters using CSV input with comprehensive error handling. This demonstrates bulk processing capabilities for large-scale monitoring environments. Output: CSV report containing performance data from all successfully processed servers and counters, with console output showing processing statistics and any errors encountered. .INPUTS String You can pipe server names to this function when using the ComputerName parameter. System.Guid You can pipe monitoring object GUIDs to this function via the MonitoringObjectId parameter. PSCustomObject You can pipe objects with properties that match the parameter names (MonitoringObjectId, RuleId, CounterName, ObjectName) for bulk processing scenarios. .OUTPUTS PSCustomObject[] Returns an array of PSCustomObject instances containing performance data with the following properties: - DateTime: The timestamp of the performance sample - AverageValue: The average value for the time period (hourly/daily aggregation) - MinValue: The minimum value recorded during the time period - MaxValue: The maximum value recorded during the time period - SampleCount: The number of raw samples used to calculate the aggregated values - MonitoringObjectId: The GUID of the monitoring object - RuleId: The GUID of the performance collection rule - CounterName: The name of the performance counter - ObjectName: The name of the performance object - InstanceName: The performance counter instance name (if applicable) .NOTES ==================================================================== Created with: Visual Studio Code Author: Tyson Paul Created: 2025.09.19 Last Modified: 2025.09.19 Version: 1.0 Blog: https://monitoringguys.com ==================================================================== Requirements: - System Center Operations Manager PowerShell module (OperationsManager) - Appropriate permissions to read from the SCOM Data Warehouse - Network connectivity to the SCOM Data Warehouse server - Active connection to a SCOM Management Group Performance Considerations: - Large date ranges may result in significant amounts of data and longer query times - Consider using daily aggregation for longer historical periods - Hourly aggregation provides more granular data but requires more processing time - Use specific RuleId filtering when possible to improve query performance Error Handling: - Validates GUID format for MonitoringObjectId and RuleId parameters - Handles SQL connection and query execution errors gracefully - Provides verbose logging for troubleshooting - Auto-discovery of Data Warehouse server with multiple fallback methods Authentication: - This function connects to the SCOM Data Warehouse using Windows Integrated Authentication (Trusted_Connection). SQL Server authentication (mixed mode) is not supported. - The connection runs under the identity of the current PowerShell process. If your account does not have read access to the Data Warehouse, use 'runas /netonly' to launch PowerShell under an authorized identity: runas /netonly /user:DOMAIN\DWReaderAccount powershell.exe The /netonly flag applies the alternate credential only to network resources (SQL, SCOM SDK) while keeping your local desktop session unchanged. - A PSCredential parameter is intentionally not provided because SCOM Data Warehouse instances are configured for Windows Integrated Authentication only. Related Functions: - Get-SCOMDWPerformanceDataSignature: Discovers available performance counters - Get-SCOMClassInstance: Retrieves SCOM monitoring objects - Get-SCOMIdFromHostName: Converts computer names to SCOM object GUIDs - Verify-SCOMDWServername: Validates and discovers Data Warehouse server names .LINK https://monitoringguys.com #> [CmdletBinding(DefaultParameterSetName = "ByComputerNameWithRule")] Param ( # Defaults to OperationsManagerDW # If not specified, it will be retrieved from the registry or SCOM settings. [Parameter(Mandatory = $false)] [string]$DataWarehouseServerName, # Defaults to OperationsManagerDW [Parameter(Mandatory = $false)] [string]$DataWarehouseDatabaseName = "OperationsManagerDW", # Defaults to 30 seconds # The time in seconds to wait for the command to execute. [Parameter(Mandatory = $false)] [int]$QueryTimeout = 30, # Defaults to 15 seconds # The time (in seconds) to wait for a connection to open. [Parameter(Mandatory = $false)] [int]$ConnectionTimeout = 15, [Parameter(Mandatory = $true, ParameterSetName = "ByComputerNameNoRule")] [string]$ComputerName, [Parameter( Mandatory = $true, ParameterSetName = "ByIdNoRule", ValueFromPipelineByPropertyName = $true, HelpMessage = "The GUID of the entity for which to retrieve all performance counters")] [Parameter( Mandatory = $true, ParameterSetName = "ByIdWithRule", ValueFromPipelineByPropertyName = $true, HelpMessage = "The GUID of the entity for which to retrieve performance counters")] [Alias('ObjectId','ManagedEntityGuid')] [ValidateScript({ if ([Guid]::TryParse($_, [ref][Guid]::Empty)) { $true } else { throw "Invalid GUID format. Please provide a valid GUID string." } })] [Object]$MonitoringObjectId, [Parameter(Mandatory = $false, ParameterSetName = "ByComputerNameNoRule")] [Parameter(Mandatory = $false, ParameterSetName = "ByIdNoRule")] [Parameter(Mandatory = $false, ParameterSetName = "ByComputerNameWithRule")] [Parameter(Mandatory = $false, ParameterSetName = "ByIdWithRule")] [datetime]$StartDate = (Get-Date).AddDays(-30), [Parameter(Mandatory = $false, ParameterSetName = "ByComputerNameNoRule")] [Parameter(Mandatory = $false, ParameterSetName = "ByIdNoRule")] [Parameter(Mandatory = $false, ParameterSetName = "ByComputerNameWithRule")] [Parameter(Mandatory = $false, ParameterSetName = "ByIdWithRule")] [datetime]$EndDate = (Get-Date), [Parameter(Mandatory = $false, ParameterSetName = "ByComputerNameNoRule")] [Parameter(Mandatory = $false, ParameterSetName = "ByIdNoRule")] [Parameter(Mandatory = $false, ParameterSetName = "ByComputerNameWithRule")] [Parameter(Mandatory = $false, ParameterSetName = "ByIdWithRule")] [string]$LanguageCode = 'ENU', [Parameter(Mandatory = $false, ParameterSetName = "ByComputerNameNoRule")] [Parameter(Mandatory = $false, ParameterSetName = "ByIdNoRule")] [Parameter(Mandatory = $false, ParameterSetName = "ByComputerNameWithRule")] [Parameter(Mandatory = $false, ParameterSetName = "ByIdWithRule")] [ValidateSet('Hourly','Daily')] [string]$AggregationType = 'Hourly', [Parameter(Mandatory = $true, ParameterSetName = "ByComputerNameWithRule")] [Parameter(Mandatory = $true, ParameterSetName = "ByIdWithRule", ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [string]$RuleId, [Parameter(Mandatory = $true, ParameterSetName = "ByComputerNameWithRule")] [Parameter(Mandatory = $true, ParameterSetName = "ByIdWithRule", ValueFromPipelineByPropertyName = $true)] [string]$CounterName, [Parameter(Mandatory = $true, ParameterSetName = "ByComputerNameWithRule")] [Parameter(Mandatory = $true, ParameterSetName = "ByIdWithRule", ValueFromPipelineByPropertyName = $true)] [string]$ObjectName ) Begin { Write-Verbose "Get-SCOMDWPerformanceData called with ParameterSet: $($PSCmdlet.ParameterSetName)" $DataWarehouseServerName = Verify-SCOMDWServername -DataWarehouseServerName $DataWarehouseServerName -Verbose:($PSBoundParameters.ContainsKey('Verbose')) # Create hash table for aggregation type mapping $AggregationMap = @{ "Hourly" = 0 "Daily" = 1 } 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. $MonitoringObjectId = Get-SCOMIdFromHostName $ComputerName } Catch { Write-Error "Failed to get MonitoringObjectId for ComputerName '$ComputerName'. Error: $_" return $false } } }#Begin Process { #region BuildCounterListXml # If no counters provided, assume all counters should be retrieved for the object. # Get the collection of all perf signatures for the object from DW. if ($MonitoringObjectId -is [Guid]) { $MonitoringObjectId = $MonitoringObjectId.ToString() } Write-Verbose "Getting Performance Data for MonitoringObjectId: $MonitoringObjectId" $PerfSignatures = [system.collections.arraylist]::new() # Implies no counters provided, so get all counters for the object if ($PSCmdlet.ParameterSetName -in @('ByIdNoRule','ByComputerNameNoRule')) { $paramObj = [ordered]@{ Id = $MonitoringObjectId #RuleId = $RuleId #ComputerName = $ComputerName #ClassName = $ClassName #CounterName = $CounterName #ObjectName = $ObjectName #ManagementPackName = $ManagementPackName #StartTime = $StartTime #EndTime = $EndTime QueryTimeout = $QueryTimeout ConnectionTimeout = $ConnectionTimeout DataWarehouseServerName = $DataWarehouseServerName DataWarehouseDatabaseName = $DataWarehouseDatabaseName #Verbose = $PSBoundParameters['Verbose'] Verbose = $PSBoundParameters.ContainsKey('Verbose') } $tmpPerfSignatures = Get-SCOMDWPerformanceDataSignature @paramObj ForEach ($Sig in $tmpPerfSignatures) { $PerfSignatures.Add($Sig) | Out-Null } if ($null -eq $PerfSignatures -or $PerfSignatures.Count -eq 0) { If ($ComputerName){ Write-Warning "No performance signatures found for ComputerName: $ComputerName" } Else { Write-Warning "No performance signatures found for MonitoringObjectId: $MonitoringObjectId" } return $null } } # Assume user provided ObjectName, CounterName, RuleId, Id. Create a single signature object to process. Else{ $PerfSignatures.Add([PSCustomObject]@{ ObjectName = $ObjectName CounterName = $CounterName RuleId = $RuleId #ManagedEntityGuid = $MonitoringObjectId MonitoringObjectId = $MonitoringObjectId }) | Out-Null } ForEach ($Signature in $PerfSignatures) { Write-Verbose "`nPreparing to get data for Object:`n`t$($Signature.ObjectName)`n`tCounter: $($Signature.CounterName)`n`tRuleId: $($Signature.RuleId)`n`tMonitoringObjectId: $($Signature.MonitoringObjectId)" # Build XML list of objects for the main query/sp $SQLCMD = @" DECLARE @InstanceId NVARCHAR(MAX); --IF @InstanceId IS NULL SET @InstanceId = 'd5568001-136e-a7ac-f0f3-046198bd2c5b'; SELECT TOP (1000) [ManagedEntityRowId] ,[ManagedEntityGuid] ,[ManagedEntityTypeRowId] FROM [OperationsManagerDW].[dbo].[vManagedEntity] Where ManagedEntityGuid IN (SELECT value FROM STRING_SPLIT(@InstanceId, ',')) --String_Split is a legacy feature but it still works. "@ Try { $paramObj = [ordered]@{ Server = $DataWarehouseServerName Database = $DataWarehouseDatabaseName Query = $SQLCMD Parameters = @{ InstanceId = $Signature.MonitoringObjectId.ToString() } } } Catch { Write-Error "Failed to build SQL parameters for ManagedEntityRowId query. Error: $_" continue } $RowId = Invoke-CLSqlCmd @paramObj $OptionList_xml = "<Data><Values>`n" ForEach ($Item in $RowId.ManagedEntityRowId) { $OptionList_xml += @" <Value> <Object Use="Self">$Item</Object> <Rule>$($Signature.RuleId)</Rule> </Value> "@ } $OptionList_xml += '</Values></Data>' #endregion BuildObjectListXml $SQLCMD = @" /** -- Declare your parameters DECLARE @StartDate DATETIME = '2025-09-07 12:49:00'; DECLARE @EndDate DATETIME = '2025-09-16 12:49:00'; DECLARE @DataAggregation INT = 1; DECLARE @OptionList NVARCHAR(MAX) = N'<Data> <Objects> <Object Use="Self">249</Object> </Objects></Data>'; **/ -- Execute the procedure with the above parameters EXEC dbo.[Microsoft_SystemCenter_DataWarehouse_Report_Library_PerformaceReportDataGet] @StartDate = @StartDate, @EndDate = @EndDate, @DataAggregation = @DataAggregation, @OptionList = @OptionList, @LanguageCode = @LanguageCode "@ $paramObj = [ordered]@{ Server = $DataWarehouseServerName Database = $DataWarehouseDatabaseName Query = $SQLCMD #As = 'DataRow' Parameters = @{ StartDate = $StartDate EndDate = $EndDate DataAggregation = $AggregationMap.$AggregationType OptionList = $OptionList_xml LanguageCode = $LanguageCode } } try { $PerfDataResult = Invoke-CLSqlCmd @paramObj if ($null -eq $PerfDataResult -or $PerfDataResult.Count -eq 0) { Write-Verbose @" Perf query successful. However, no performance data found for: Object: $($Signature.ObjectName) Counter: $($Signature.CounterName) RuleId: $($Signature.RuleId) ManagedEntityGuid: $($Signature.ManagedEntityGuid)`nOptionsList:`n$OptionList_xml "@ return $null } else { $PerfDataResult | ForEach-Object { [PSCustomObject]@{ DateTime = $_.DateTime CounterName = $Signature.CounterName ObjectName = $Signature.ObjectName AverageValue = $_.AverageValue MinValue = $_.MinValue MaxValue = $_.MaxValue SampleCount = $_.SampleCount StandardDeviation = $_.StandardDeviation AggregationType = $AggregationType InstanceName = $_.InstanceName DisplayName = $_.DisplayName Path = $_.Path ManagedEntityDefaultName = $_.ManagedEntityDefaultName ManagedEntityGuid = $_.ManagedEntityGuid RuleDisplayName = $_.RuleDisplayName RuleGuid = $_.RuleGuid RuleId = $Signature.RuleId RuleRowId = $_.RuleRowId #ChartColor = #ChartScale = #ChartType = #Group = $_. #GroupTitle = $_. #Image = $_. #ManagedEntityRowId = $_. ManagementGroupDefaultName = $_.ManagementGroupDefaultName ManagementGroupGuid = $_.ManagementGroupGuid MultiInstanceInd = $_.MultiInstanceInd OptionXml = $_.OptionXml #Position = $_. } } } } catch { Write-Error "Failed to execute performance data query. Error: $_" return $null } } }#Process End { } } |