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 {

    }
}