Public/Get-DNSFilterStatus.ps1

function Get-DNSFilterStatus {
    <#
    .SYNOPSIS
        Reports whether the DNSFilter roaming client is installed and which DNS servers it's actually using.

    .DESCRIPTION
        DNSFilter's Windows roaming client (a.k.a. "DNS Agent") binds a local DNS
        proxy to 127.0.0.1 and forwards queries upstream to resolvers of its own
        choosing. Because Windows itself is pointed at the loopback address, tools
        like Get-DnsClientServerAddress and ipconfig /all only ever show
        127.0.0.1 - the real upstream servers are hidden inside the agent's own
        configuration file.

        This function locates that configuration file (appsettings.json, which
        lives under a few different ProgramData folder names depending on
        branding/version), reads its LoopbackProxy.LocalResolvers list, and
        reports it alongside the agent's install status. It also checks the
        Windows service as a fallback so installs can still be flagged even if
        the config file has moved.

        No administrator privileges are required; ProgramData is world-readable.

    .PARAMETER ConfigPath
        Overrides the automatic search and reads the given appsettings.json path
        directly. Useful for testing or nonstandard install locations.

    .EXAMPLE
        Get-DNSFilterStatus

        Installed ConfigPath DNSServers
        --------- ---------- ----------
             True C:\ProgramData\DNS Agent\Settings\appsettings.json {192.168.100.5, 192.168.100.10}

    .EXAMPLE
        (Get-DNSFilterStatus).DNSServers

        Returns just the list of upstream DNS servers the agent is forwarding to.

    .EXAMPLE
        Get-DNSFilterStatus -ConfigPath 'D:\backup\appsettings.json'

        Reads a config file from a nonstandard location instead of searching
        the default ProgramData paths.

    .OUTPUTS
        PSCustomObject
        Installed (bool), DNSServers (string[]), ConfigPath (string).

    .LINK
        https://github.com/DailenG/DNSFilterStatus
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter()]
        [string]$ConfigPath
    )

    process {
        $result = [PSCustomObject]@{
            PSTypeName = 'DNSFilterStatus.AgentStatus'
            Installed  = $false
            DNSServers = @()
            ConfigPath = $null
        }

        $foundPath = if ($ConfigPath) {
            if (Test-Path -LiteralPath $ConfigPath -PathType Leaf) { $ConfigPath } else { $null }
        }
        else {
            Resolve-DNSFilterConfigPath
        }

        if ($foundPath) {
            $result.ConfigPath = $foundPath
            $result.Installed = $true

            try {
                $settings = Get-Content -LiteralPath $foundPath -Raw | ConvertFrom-Json -ErrorAction Stop
                if ($settings.LoopbackProxy.LocalResolvers) {
                    $result.DNSServers = @($settings.LoopbackProxy.LocalResolvers)
                }
                else {
                    Write-Warning "Config file found at '$foundPath' but it has no LoopbackProxy.LocalResolvers entry."
                }
            }
            catch {
                Write-Warning "Config file found at '$foundPath' but could not be parsed as JSON: $($_.Exception.Message)"
            }
        }
        elseif (-not $ConfigPath) {
            # No config file at any known location. Fall back to the service so an
            # install can still be reported even though the DNS server list can't be.
            $service = Get-DNSFilterAgentService
            if ($service) {
                $result.Installed = $true
                Write-Warning "The '$($service.Name)' service is present, but its config file was not found in any known ProgramData location. DNS server list is unavailable."
            }
        }

        return $result
    }
}