Private/Get-NSPEntraConnectReadScript.ps1

function Get-NSPEntraConnectReadScript {
    # Self-contained: the remote server needs ADSync, not NSP modules or Gallery installs.
    {
        $ErrorActionPreference = 'Stop'
        $module = Get-Module -ListAvailable -Name ADSync | Sort-Object Version -Descending | Select-Object -First 1
        if (-not $module) {
            $manifest = Join-Path $env:ProgramFiles 'Microsoft Azure AD Sync\Bin\ADSync\ADSync.psd1'
            if (Test-Path -LiteralPath $manifest) { $module = Get-Item -LiteralPath $manifest }
        }
        if (-not $module) { throw 'ADSync was not found. Choose the Entra Connect Sync server, or import a saved diagnostic.' }
        $modulePath = if ($module.Path) { $module.Path } else { $module.FullName }
        $loaded = Import-Module -Name $modulePath -PassThru -ErrorAction Stop
        $parameters = @()
        if (Get-Command Get-ADSyncGlobalSettingsParameter -ErrorAction SilentlyContinue) {
            try { $parameters = @(Get-ADSyncGlobalSettingsParameter) } catch { $parameters = @() }
        } elseif (Get-Command Get-ADSyncGlobalSettings -ErrorAction SilentlyContinue) {
            try { $parameters = @((Get-ADSyncGlobalSettings).Parameters) } catch { $parameters = @() }
        }
        # Project flows before remoting serialization; never return connector secret fields.
        $rules = @(foreach ($rule in @(Get-ADSyncRule -ErrorAction Stop)) {
            [pscustomobject]@{
                Name = [string]$rule.Name; Disabled = [bool]$rule.Disabled
                Direction = [string]$rule.Direction; Connector = [string]$rule.Connector
                SourceObjectType = [string]$rule.SourceObjectType; TargetObjectType = [string]$rule.TargetObjectType
                Precedence = $rule.Precedence
                AttributeFlowMappings = @(foreach ($flow in @($rule.AttributeFlowMappings)) {
                    if ($null -eq $flow -or [string]$flow.Destination -notin @('sourceAnchor', 'sourceAnchorBinary')) { continue }
                    [pscustomobject]@{ Destination = [string]$flow.Destination; FlowType = [string]$flow.FlowType
                        Source = @($flow.Source | ForEach-Object { [string]$_ }); Expression = [string]$flow.Expression }
                })
            }
        })
        $connectors = @(Get-ADSyncConnector -ErrorAction Stop | ForEach-Object {
            [pscustomobject]@{ Identifier = [string]$_.Identifier; Name = [string]$_.Name; ConnectorTypeName = [string]$_.ConnectorTypeName }
        })
        $state = [pscustomobject]@{
            Rule = $rules; Connector = $connectors
            GlobalParameter = @($parameters | Where-Object Name -eq 'Microsoft.SynchronizationOption.AnchorAttribute' |
                ForEach-Object { [pscustomobject]@{ Name = [string]$_.Name; Value = [string]$_.Value } })
            ModuleVersion = [string]$loaded.Version; ComputerName = $env:COMPUTERNAME
        }
        # Transport one JSON string so WinRM's object serialization depth cannot truncate flows.
        $state | ConvertTo-Json -Depth 20 -Compress
    }
}