Private/Get-SqlRsInstance.ps1

# =============================================================================
# Script : Private/Get-SqlRsInstance.ps1
# Author : Keith Ramsey
# Created : 2026-06-08
# =============================================================================
# Change Log
# -----------------------------------------------------------------------------
# 2026-06-08 Keith Ramsey Initial: enumerate WMI child namespaces under
# root\Microsoft\SqlServer\ReportServer, match by
# instance name, resolve the version sub-namespace,
# and return the MSReportServer_ConfigurationSetting
# CIM instance. DR-013 (WMI mechanism) + DR-014
# (enumerate/auto-detect strategy).
# =============================================================================
# Decision Contract:
# - Never throws. All errors caught; $null returned on any failure.
# - No hostnames, IPs, or service accounts in source (air-gap discipline).
# - Instance name matching: prefer exact 'RS_<InstanceName>'; fall back to
# any namespace whose Name contains the InstanceName (case-insensitive).
# - Version sub-namespace is enumerated, not hard-coded (DR-014 Option A).
# - Remote access uses -ComputerName on Get-CimInstance, or a CimSession
# when -Credential is supplied.
# =============================================================================

function Get-SqlRsInstance {
    param(
        [string]      $InstanceName  = 'MSSQLSERVER',
        [string]      $ComputerName  = $env:COMPUTERNAME,
        [PSCredential]$Credential
    )

    # ------------------------------------------------------------------
    # Helper: build a consistent CIM parameter hashtable for every query.
    # A CimSession is created once when -Credential is supplied; otherwise
    # -ComputerName is passed directly (no session overhead for local use).
    # ------------------------------------------------------------------

    $session   = $null
    $cimParams = @{}

    $isLocal = (
        $ComputerName -eq $env:COMPUTERNAME -or
        $ComputerName -eq 'localhost'        -or
        $ComputerName -eq '127.0.0.1'
    )

    try {
        if (-not $isLocal) {
            if ($Credential) {
                $sessionOpt = New-CimSessionOption -Protocol Wsman
                $session    = New-CimSession -ComputerName $ComputerName `
                                             -Credential   $Credential   `
                                             -SessionOption $sessionOpt  `
                                             -ErrorAction Stop
                $cimParams['CimSession'] = $session
            }
            else {
                $cimParams['ComputerName'] = $ComputerName
            }
        }

        # ------------------------------------------------------------------
        # Step 1 — Enumerate child namespaces under the RS root namespace.
        # Each child namespace name follows the pattern RS_<EncodedInstanceName>.
        # ------------------------------------------------------------------

        $rsRoot      = 'root\Microsoft\SqlServer\ReportServer'
        $childNs     = Get-CimInstance @cimParams `
                           -Namespace $rsRoot     `
                           -ClassName '__Namespace' `
                           -ErrorAction Stop

        if (-not $childNs) { return $null }

        # ------------------------------------------------------------------
        # Step 2 — Match the child namespace to the requested instance name.
        # Preference order:
        # 1. Exact match on 'RS_<InstanceName>' (case-insensitive).
        # 2. Any namespace whose Name contains the InstanceName.
        # ------------------------------------------------------------------

        $exactTarget = "RS_$InstanceName"
        $matched     = $childNs | Where-Object { $_.Name -ieq $exactTarget }

        if (-not $matched) {
            $matched = $childNs |
                       Where-Object { $_.Name -ilike "*$InstanceName*" } |
                       Select-Object -First 1
        }

        if (-not $matched) { return $null }

        # ------------------------------------------------------------------
        # Step 3 — Enumerate version sub-namespaces under the matched namespace
        # to find the vN layer (e.g. v13, v14, v15). Pick the first one that
        # also contains an 'Admin' sub-namespace — that is the Admin namespace
        # referenced by DR-013.
        # ------------------------------------------------------------------

        $nsBase    = "$rsRoot\$($matched.Name)"
        $versionNs = Get-CimInstance @cimParams `
                         -Namespace $nsBase     `
                         -ClassName '__Namespace' `
                         -ErrorAction Stop

        if (-not $versionNs) { return $null }

        $adminNs = $null
        foreach ($ver in ($versionNs | Where-Object { $_.Name -imatch '^v\d+$' })) {
            $candidate = "$nsBase\$($ver.Name)"
            $adminChild = Get-CimInstance @cimParams `
                              -Namespace $candidate  `
                              -ClassName '__Namespace' `
                              -ErrorAction SilentlyContinue |
                          Where-Object { $_.Name -ieq 'Admin' }

            if ($adminChild) {
                $adminNs = "$candidate\Admin"
                break
            }
        }

        if (-not $adminNs) { return $null }

        # ------------------------------------------------------------------
        # Step 4 — Return the MSReportServer_ConfigurationSetting CIM instance
        # from the resolved Admin namespace.
        # ------------------------------------------------------------------

        $configInstance = Get-CimInstance @cimParams              `
                              -Namespace  $adminNs               `
                              -ClassName  'MSReportServer_ConfigurationSetting' `
                              -ErrorAction Stop

        return $configInstance
    }
    catch {
        return $null
    }
    finally {
        if ($session) {
            Remove-CimSession -CimSession $session -ErrorAction SilentlyContinue
        }
    }
}