Public/Get-tpcAvailableCounterConfig.ps1

     function Get-tpcAvailableCounterConfig {
     <#
     .SYNOPSIS
          Retrieves, validates, and displays detailed information about available performance
          counter configurations including JSON schema validation and optional counter availability testing.
 
     .DESCRIPTION
          This function scans the config directory for all JSON files with the 'tpc_' prefix
          and provides detailed information about each configuration including counter details,
          validation status, and availability checks.
 
     .PARAMETER ConfigPath
          Optional custom path to configuration files. If not specified, uses the module's
          config directory.
 
     .PARAMETER Raw
          If specified, returns raw PSCustomObject array instead of formatted output.
 
     .PARAMETER TestCounters
          If specified, tests each counter for availability. This can be slow with many counters.
 
     .EXAMPLE
          Get-tpcAvailableCounterConfig
 
          Shows formatted overview of all available configurations (without counter testing).
 
     .EXAMPLE
          Get-tpcAvailableCounterConfig -TestCounters
 
          Shows formatted overview with counter availability testing.
 
     .EXAMPLE
          Get-tpcAvailableCounterConfig -ConfigPath "C:\MyConfigs" -TestCounters
 
          Shows formatted overview of configurations from a custom directory with counter testing.
 
     .EXAMPLE
          Get-tpcAvailableCounterConfig -Raw
 
          Returns raw configuration objects for further processing.
 
     .OUTPUTS
          Formatted output by default, PSCustomObject[] when -Raw is used.
     #>


     [CmdletBinding()]
     [OutputType([PSCustomObject[]])]
     param(
          [string]    $ConfigPath =$(Join-Path $PSScriptRoot "..\Config"),
          [string]    $SchemaPath = $(Join-Path $ConfigPath "schema.json"),
          [switch]    $Raw,
          [switch]    $TestCounters

     )

     try {

          # required module for JSON schema validation
          $skipSchemaValidation = $false

          if ( -not (Get-Module -Name GripDevJsonSchemaValidator -ListAvailable) ) {
               Write-Warning "Module 'GripDevJsonSchemaValidator' not found. Please install it with: Install-Module -Name GripDevJsonSchemaValidator"
               Write-Warning "JSON schema validation will be skipped."
               $skipSchemaValidation = $true
          }

          if ( -not (Test-Path $ConfigPath) ) {
               throw "Configuration directory not found: $ConfigPath"
          }

          if ( -not (Test-Path $SchemaPath) ) {
               Write-Warning "Schema file not found at: $SchemaPath"
               $skipSchemaValidation = $true
          }

          $ConfigFiles = Get-ChildItem -Path $ConfigPath -Filter "tpc_*.json" -File | Where-Object { $_.BaseName -notlike "*template*" }

          if ( $ConfigFiles.Count -eq 0 ) {
               Write-Warning "No configuration files found with 'tpc_' prefix in: $ConfigPath"
               return @()
          }

          $Results = @()

          foreach ( $ConfigFile in $ConfigFiles ) {

               try {

                    $ConfigName    = $ConfigFile.BaseName -replace '^tpc_', ''
                    $JsonContent   = Get-Content -Path $ConfigFile.FullName -Raw -ErrorAction Stop
                    $JsonConfig    = $JsonContent | ConvertFrom-Json -ErrorAction Stop


                    $SchemaValidation = @{IsValid = $true; Errors = @()}

                    if ( -not $skipSchemaValidation ) {

                         try {

                              $ValidationResult = Test-JsonSchema -SchemaPath $SchemaPath -JsonPath $ConfigFile.FullName -ErrorAction Stop 6>$null
                              $SchemaValidation.IsValid = $ValidationResult.Valid
                              if ( -not $ValidationResult.Valid ) {
                                   $SchemaValidation.Errors = @($ValidationResult.Errors | ForEach-Object { "$($_.Message) | Path: $($_.Path) | Line: $($_.LineNumber)" })
                              }

                         } catch {

                              $SchemaValidation.IsValid = $false
                              $SchemaValidation.Errors = @("Schema validation failed: $($_.Exception.Message)")
                         }

                    } else {

                         Write-Warning "Skipping schema validation for $ConfigName due to missing module or Schema file."

                    }

                    $CounterDetails = @()

                    foreach ( $CounterConfig in $JsonConfig.counters ) {

                         try {

                              $Counter = [PerformanceCounter]::new(
                                   $CounterConfig.counterID,
                                   $CounterConfig.counterSetType,
                                   $CounterConfig.counterInstance,
                                   $CounterConfig.title,
                                   $CounterConfig.type,
                                   $CounterConfig.format,
                                   $CounterConfig.unit,
                                   $CounterConfig.colorMap,
                                   $CounterConfig.graphConfiguration
                              )

                              $IsAvailable = if ($TestCounters) { $Counter.TestAvailability() } else { $null }

                              $CounterDetail = [PSCustomObject]@{
                                   Title          = $Counter.Title
                                   CounterId      = $Counter.counterID
                                   CounterPath    = $Counter.CounterPath
                                   Unit           = $Counter.Unit
                                   Format         = $Counter.Format
                                   Valid          = $IsAvailable
                                   ErrorMessage   = if ($TestCounters -and -not $IsAvailable) { $Counter.LastError } else { $null }
                                   InstancePath   = $Counter.CounterPath
                              }

                              $CounterDetails += $CounterDetail

                         } catch {

                              $CounterDetail = [PSCustomObject]@{
                                   Title          = $CounterConfig.title
                                   CounterId      = $CounterConfig.counterID
                                   CounterPath    = "Invalid"
                                   Unit           = $CounterConfig.unit
                                   Format         = $CounterConfig.format
                                   Valid          = $false
                                   ErrorMessage   = $_.Exception.Message
                                   InstancePath   = $($_.Exception.Message)
                              }

                              $CounterDetails += $CounterDetail
                         }
                    }

                    $ConfigOverview = [PSCustomObject]@{
                         ConfigName               = $ConfigName
                         Description              = $JsonConfig.description
                         ConfigFile               = $ConfigFile.FullName
                         JsonValid                = $SchemaValidation.IsValid
                         JsonValidationErrors     = $SchemaValidation.Errors
                         CounterCount             = $CounterDetails.Count
                         ValidCounters            = if ($TestCounters) { ($CounterDetails | Where-Object { $_.Valid -eq $true }).Count }  else { "Not tested" }
                         InvalidCounters          = if ($TestCounters) { ($CounterDetails | Where-Object { $_.Valid -eq $false }).Count } else { "Not tested" }
                         Counters                 = $CounterDetails
                    }

                    $Results += $ConfigOverview

               } catch {

                    Write-Error "Error processing configuration file '$($ConfigFile.Name)': $($_.Exception.Message)"

                    $ErrorConfig = [PSCustomObject]@{
                         ConfigName               = $ConfigFile.BaseName -replace '^tpc_', ''
                         Description              = "Error loading configuration"
                         ConfigFile               = $ConfigFile.FullName
                         JsonValid                = $false
                         JsonValidationErrors     = @($_.Exception.Message)
                         CounterCount             = 0
                         ValidCounters            = if ($TestCounters) { 0 } else { "Not tested" }
                         InvalidCounters          = if ($TestCounters) { 0 } else { "Not tested" }
                         Counters                 = @()
                    }

                    $Results += $ErrorConfig

               }
          }

          if ( $Raw ) {

               return $Results

          } else {

               foreach ( $result in $Results ) {
                    Write-Host "`n=== $($result.ConfigName) ===" -ForegroundColor Cyan
                    Write-Host "Description: $($result.Description)" -ForegroundColor Gray
                    $JsonStatus    = if ( $result.JsonValid )  { "Valid JSON Schema" } else { "Invalid JSON Schema" }
                    $CounterStatus = if ( $TestCounters ) { "Counters: $($result.ValidCounters)/$($result.CounterCount)" } else { "Counters: $($result.CounterCount) (not tested)" }

                    if ( $result.JsonValid ) {
                         Write-Host "$JsonStatus, $CounterStatus" -ForegroundColor Green
                    } else {
                         Write-Host "$JsonStatus, $CounterStatus" -ForegroundColor Red
                         Write-Host "Schema Validation Errors:" -ForegroundColor Yellow
                         ForEach ( $errorMessage in $result.JsonValidationErrors ) {
                              Write-Host " - $errorMessage" -ForegroundColor Red
                         }
                    }

                    if ( $result.Counters.Count -gt 0)  {
                         if ( $TestCounters ) {
                              $result.Counters | Format-Table Title, CounterId, Unit, Format, Valid, InstancePath -AutoSize -Wrap
                         } else {
                              $result.Counters | Format-Table Title, CounterId, Unit, Format, InstancePath -AutoSize -Wrap
                         }

                    } else {

                         Write-Host " No counters found" -ForegroundColor Yellow

                    }
               }

          }

     } catch {

          Write-Error "Error in Get-tpcAvailableCounterConfig: $($_.Exception.Message)"

     }

}