Public/Get-sqmServiceBrokerHealth.ps1

<#
.SYNOPSIS
    Creates a health report for SQL Server Service Broker configuration and status.
 
.DESCRIPTION
    Retrieves Service Broker information from a SQL Server instance:
    - Service Broker status (enabled/disabled per database)
    - Endpoints on port 4022 (SSBEndpoint)
    - Queue status and message counts
    - Undeliverable messages in transmission queue
    - Service pairs and their contracts
    - Replica status (if AlwaysOn AG is configured)
 
    Results are saved as a TXT report in the specified directory.
    The function automatically detects single-instance or AlwaysOn configurations.
 
.PARAMETER SqlInstance
    SQL Server instance. Default: current computer name.
 
.PARAMETER SqlCredential
    Optional PSCredential for the connection.
 
.PARAMETER OutputPath
    Output directory for report files. Default: C:\System\WinSrvLog\MSSQL
 
.PARAMETER ContinueOnError
    Continue on error (otherwise the error is thrown).
 
.PARAMETER EnableException
    Throw exceptions immediately (overrides ContinueOnError).
 
.EXAMPLE
    Get-sqmServiceBrokerHealth
 
.EXAMPLE
    Get-sqmServiceBrokerHealth -SqlInstance "SQL01" -OutputPath "D:\Reports"
 
.NOTES
    Author: sqmSQLTool
    Prerequisites: dbatools
    Default output path: C:\System\WinSrvLog\MSSQL
#>

function Get-sqmServiceBrokerHealth
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'None')]
    [OutputType([PSCustomObject])]
    param (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string]$SqlInstance = $env:COMPUTERNAME,
        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]$SqlCredential,
        [Parameter(Mandatory = $false)]
        [string]$OutputPath = "C:\System\WinSrvLog\MSSQL",
        [Parameter(Mandatory = $false)]
        [switch]$ContinueOnError,
        [Parameter(Mandatory = $false)]
        [switch]$EnableException,
        [Parameter(Mandatory = $false)]
        [switch]$NoOpen
    )

    begin
    {
        $functionName = $MyInvocation.MyCommand.Name

        if (-not (Get-Module -ListAvailable -Name dbatools))
        {
            $errMsg = "dbatools-Modul nicht gefunden."
            Invoke-sqmLogging -Message $errMsg -FunctionName $functionName -Level "ERROR"
            throw $errMsg
        }

        Invoke-sqmLogging -Message "Starte $functionName mit OutputPath: $OutputPath" -FunctionName $functionName -Level "INFO"
    }

    process
    {
        try
        {
            # Verbindung herstellen
            $server = Connect-DbaInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential -ErrorAction Stop

            # Ausgabeverzeichnis erstellen falls nicht vorhanden
            if (-not (Test-Path $OutputPath))
            {
                $null = New-Item -ItemType Directory -Path $OutputPath -Force
                Invoke-sqmLogging -Message "Verzeichnis erstellt: $OutputPath" -FunctionName $functionName -Level "INFO"
            }

            # Report-Metadaten
            $timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
            $cleanServerName = $server.Name -replace '\\', '-'
            $reportFile = Join-Path $OutputPath ("ServiceBrokerHealth_" + $cleanServerName + "_" + $timestamp + ".txt")
            $reportContent = [System.Collections.Generic.List[string]]::new()

            # Header
            $reportContent.Add("=" * 80) | Out-Null
            $reportContent.Add("SQL Server Service Broker Health Report") | Out-Null
            $reportContent.Add("=" * 80) | Out-Null
            $reportContent.Add("Server: $($server.Name)") | Out-Null
            $reportContent.Add("Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')") | Out-Null
            $reportContent.Add("") | Out-Null

            # 1. AlwaysOn Auto-Detect
            $agQuery = "SELECT COUNT(*) as AGCount FROM sys.availability_groups"
            $agResult = $server.Query($agQuery)
            $isAlwaysOn = $agResult.AGCount -gt 0
            $reportContent.Add("Configuration: $(if ($isAlwaysOn) { 'AlwaysOn Availability Group' } else { 'Single System' })") | Out-Null
            $reportContent.Add("") | Out-Null

            # 2. Broker Endpoints
            $reportContent.Add("-" * 80) | Out-Null
            $reportContent.Add("Service Broker Endpoints") | Out-Null
            $reportContent.Add("-" * 80) | Out-Null

            $epQuery = @"
SELECT
    name,
    state_desc,
    protocol_desc,
    type_desc
FROM sys.endpoints
WHERE type = 3
ORDER BY name
"@


            $endpoints = $server.Query($epQuery)
            if ($endpoints)
            {
                foreach ($ep in $endpoints)
                {
                    $reportContent.Add(" Endpoint: $($ep.name)") | Out-Null
                    $reportContent.Add(" State: $($ep.state_desc)") | Out-Null
                    $reportContent.Add(" Protocol: $($ep.protocol_desc)") | Out-Null
                    $reportContent.Add(" Type: $($ep.type_desc)") | Out-Null
                    $reportContent.Add("") | Out-Null
                }
            }
            else
            {
                $reportContent.Add(" No Service Broker endpoints found") | Out-Null
                $reportContent.Add("") | Out-Null
            }

            # 3. Broker Status per Database
            $reportContent.Add("-" * 80) | Out-Null
            $reportContent.Add("Service Broker Status per Database") | Out-Null
            $reportContent.Add("-" * 80) | Out-Null

            $brokerQuery = @"
SELECT
    name,
    is_broker_enabled,
    state_desc
FROM sys.databases
WHERE state_desc = 'ONLINE'
ORDER BY name
"@


            $brokerDbs = $server.Query($brokerQuery)
            $enabledCount = ($brokerDbs | Where-Object { $_.is_broker_enabled -eq 1 }).Count
            $disabledCount = ($brokerDbs | Where-Object { $_.is_broker_enabled -eq 0 }).Count

            $reportContent.Add(" Enabled: $enabledCount") | Out-Null
            $reportContent.Add(" Disabled: $disabledCount") | Out-Null
            $reportContent.Add("") | Out-Null

            foreach ($db in $brokerDbs)
            {
                $status = if ($db.is_broker_enabled) { "ENABLED" } else { "DISABLED" }
                $reportContent.Add(" $($db.name): $status") | Out-Null
            }
            $reportContent.Add("") | Out-Null

            # 4. Service Queues
            $reportContent.Add("-" * 80) | Out-Null
            $reportContent.Add("Service Queues") | Out-Null
            $reportContent.Add("-" * 80) | Out-Null

            $queueQuery = @"
SELECT
    name,
    is_activation_enabled,
    is_enqueue_enabled,
    is_receive_enabled,
    is_retention_enabled
FROM sys.service_queues
ORDER BY name
"@


            foreach ($db in $brokerDbs | Where-Object { $_.is_broker_enabled -eq 1 })
            {
                $queues = $server.Query($queueQuery, $db.name)
                if ($queues)
                {
                    $reportContent.Add(" Database: $($db.name)") | Out-Null
                    foreach ($q in $queues)
                    {
                        $reportContent.Add(" Queue: $($q.name)") | Out-Null
                        $reportContent.Add(" Enqueue: $(if ($q.is_enqueue_enabled) { 'ON' } else { 'OFF' })") | Out-Null
                        $reportContent.Add(" Receive: $(if ($q.is_receive_enabled) { 'ON' } else { 'OFF' })") | Out-Null
                        $reportContent.Add(" Activation: $(if ($q.is_activation_enabled) { 'ON' } else { 'OFF' })") | Out-Null
                        $reportContent.Add("") | Out-Null
                    }
                }
            }

            # 5. Undeliverable Messages
            $reportContent.Add("-" * 80) | Out-Null
            $reportContent.Add("Undeliverable Messages (Transmission Queue)") | Out-Null
            $reportContent.Add("-" * 80) | Out-Null

            $undeliverableQuery = @"
SELECT COUNT(*) as UndeliverableCount FROM sys.transmission_queue
"@


            foreach ($db in $brokerDbs | Where-Object { $_.is_broker_enabled -eq 1 })
            {
                $undeliverable = $server.Query($undeliverableQuery, $db.name)
                $count = $undeliverable[0].UndeliverableCount
                $reportContent.Add(" Database: $($db.name)") | Out-Null
                if ($count -gt 0)
                {
                    $reportContent.Add(" Undeliverable: $count messages") | Out-Null
                }
                else
                {
                    $reportContent.Add(" Undeliverable: 0 messages") | Out-Null
                }
                $reportContent.Add("") | Out-Null
            }

            # 6. Service Pairs and Contracts
            $reportContent.Add("-" * 80) | Out-Null
            $reportContent.Add("Service Pairs and Contracts") | Out-Null
            $reportContent.Add("-" * 80) | Out-Null

            $serviceQuery = @"
SELECT
    s.name as ServiceName,
    sc.name as ContractName
FROM sys.services s
LEFT JOIN sys.service_contract_usages scu ON s.service_id = scu.service_id
LEFT JOIN sys.service_contracts sc ON scu.service_contract_id = sc.service_contract_id
ORDER BY s.name
"@


            foreach ($db in $brokerDbs | Where-Object { $_.is_broker_enabled -eq 1 })
            {
                $services = $server.Query($serviceQuery, $db.name)
                if ($services)
                {
                    $reportContent.Add(" Database: $($db.name)") | Out-Null
                    foreach ($svc in $services)
                    {
                        $contract = if ($svc.ContractName) { $svc.ContractName } else { "N/A" }
                        $reportContent.Add(" Service: $($svc.ServiceName)") | Out-Null
                        $reportContent.Add(" Contract: $contract") | Out-Null
                        $reportContent.Add("") | Out-Null
                    }
                }
            }

            # 7. AlwaysOn Replica Status (falls AG vorhanden)
            if ($isAlwaysOn)
            {
                $reportContent.Add("-" * 80) | Out-Null
                $reportContent.Add("AlwaysOn Replica Status") | Out-Null
                $reportContent.Add("-" * 80) | Out-Null

                $replicaQuery = @"
SELECT
    ag.name as AGName,
    ar.replica_server_name,
    ar.availability_mode_desc,
    ars.role_desc,
    ars.operational_state_desc
FROM sys.availability_groups ag
JOIN sys.availability_replicas ar ON ag.group_id = ar.group_id
JOIN sys.dm_hadr_availability_replica_states ars ON ar.replica_id = ars.replica_id
ORDER BY ag.name, ar.replica_server_name
"@


                $replicas = $server.Query($replicaQuery)
                if ($replicas)
                {
                    foreach ($r in $replicas)
                    {
                        $reportContent.Add(" AG: $($r.AGName)") | Out-Null
                        $reportContent.Add(" Replica: $($r.replica_server_name)") | Out-Null
                        $reportContent.Add(" Role: $($r.role_desc)") | Out-Null
                        $reportContent.Add(" State: $($r.operational_state_desc)") | Out-Null
                        $reportContent.Add("") | Out-Null
                    }
                }
            }

            # Footer
            $reportContent.Add("=" * 80) | Out-Null
            $reportContent.Add("End of Report") | Out-Null

            # Report schreiben
            $reportContent -join "`n" | Out-File -FilePath $reportFile -Encoding UTF8 -Force
            Invoke-sqmLogging -Message "Report erstellt: $reportFile" -FunctionName $functionName -Level "INFO"

            # Return
            $result = [PSCustomObject]@{
                ComputerName = $server.Name
                ReportPath   = $reportFile
                Timestamp    = $timestamp
                IsAlwaysOn   = $isAlwaysOn
            }

            if (-not $NoOpen)
            {
                try
                {
                    & notepad.exe $reportFile
                }
                catch
                {
                    Invoke-sqmLogging -Message "Konnte Report nicht öffnen: $_" -FunctionName $functionName -Level "WARN"
                }
            }

            return $result
        }
        catch
        {
            $errMsg = "Fehler in $functionName`:`n$_"
            Invoke-sqmLogging -Message $errMsg -FunctionName $functionName -Level "ERROR"

            if ($EnableException)
            {
                throw
            }
            elseif (-not $ContinueOnError)
            {
                throw
            }
        }
    }
}