Private/Invoke-InfraPulseDnsCheck.ps1

function Invoke-InfraPulseDnsCheck {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [psobject]$Context,

        [Parameter(Mandatory)]
        [System.Collections.IDictionary]$Settings
    )

    $targets = @($Settings.Targets)
    if ($targets.Count -eq 0) {
        return New-InfraPulseResult -Status 'Skipped' -CheckName 'Dns' -Category 'Connectivity' -ComputerName $Context.ComputerName -Target 'DNS targets' -Message 'No DNS targets are configured.' -Recommendation 'Add required names under Checks.Dns.Targets to validate name resolution from the target host.'
    }

    $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
    $scriptBlock = {
        param($CheckSettings)

        foreach ($configuredTarget in @($CheckSettings.Targets)) {
            if ($configuredTarget -is [string]) {
                $name = [string]$configuredTarget
                $queryType = [string]$CheckSettings.QueryType
                $server = [string]$CheckSettings.Server
            }
            else {
                $name = [string]$configuredTarget.Name
                $queryType = if ($configuredTarget.Contains('Type')) { [string]$configuredTarget.Type } else { [string]$CheckSettings.QueryType }
                $server = if ($configuredTarget.Contains('Server')) { [string]$configuredTarget.Server } else { [string]$CheckSettings.Server }
            }

            $timer = [System.Diagnostics.Stopwatch]::StartNew()
            try {
                $answers = @()
                if (Get-Command -Name Resolve-DnsName -ErrorAction SilentlyContinue) {
                    $parameters = @{
                        Name        = $name
                        Type        = $queryType
                        DnsOnly     = $true
                        ErrorAction = 'Stop'
                    }
                    if (-not [string]::IsNullOrWhiteSpace($server)) {
                        $parameters.Server = $server
                    }
                    $records = @(Resolve-DnsName @parameters)
                    foreach ($record in $records) {
                        # Record types carry different answer properties (a CNAME
                        # has no IPAddress), and the local execution path runs
                        # under the module's StrictMode, so every property must
                        # be probed before it is read.
                        foreach ($propertyName in @('IPAddress', 'NameHost', 'NameExchange', 'NameTarget', 'Strings', 'PrimaryServer')) {
                            $property = $record.PSObject.Properties[$propertyName]
                            if ($null -eq $property -or -not $property.Value) {
                                continue
                            }
                            if ($propertyName -eq 'Strings') {
                                $answers += (@($property.Value) -join ' ')
                            }
                            else {
                                $answers += [string]$property.Value
                            }
                            break
                        }
                    }
                }
                else {
                    if (-not [string]::IsNullOrWhiteSpace($server)) {
                        throw 'A custom DNS server requires Resolve-DnsName on the target.'
                    }
                    if ($queryType -notin @('A', 'AAAA')) {
                        throw "Query type '$queryType' requires Resolve-DnsName on the target."
                    }
                    $addressFamily = if ($queryType -eq 'AAAA') {
                        [System.Net.Sockets.AddressFamily]::InterNetworkV6
                    }
                    else {
                        [System.Net.Sockets.AddressFamily]::InterNetwork
                    }
                    $addresses = [System.Net.Dns]::GetHostAddresses($name) |
                        Where-Object { $_.AddressFamily -eq $addressFamily }
                    $answers = @($addresses | ForEach-Object { $_.IPAddressToString })
                }
                $timer.Stop()

                [pscustomobject]@{
                    Name        = $name
                    QueryType   = $queryType
                    Server      = $server
                    Success     = $answers.Count -gt 0
                    Answers     = @($answers | Select-Object -Unique)
                    DurationMs  = [math]::Round($timer.Elapsed.TotalMilliseconds, 2)
                    FailureKind = $null
                    Error       = $null
                }
            }
            catch {
                $timer.Stop()
                $errorMessage = $_.Exception.Message
                $failureKind = if ($errorMessage -like '*requires Resolve-DnsName*') { 'UnsupportedQuery' } else { 'ResolutionFailure' }
                [pscustomobject]@{
                    Name        = $name
                    QueryType   = $queryType
                    Server      = $server
                    Success     = $false
                    Answers     = @()
                    DurationMs  = [math]::Round($timer.Elapsed.TotalMilliseconds, 2)
                    FailureKind = $failureKind
                    Error       = $errorMessage
                }
            }
        }
    }

    $raw = @(Invoke-InfraPulseCommand -Context $Context -ScriptBlock $scriptBlock -ArgumentList @($Settings))
    $stopwatch.Stop()

    $results = @()
    foreach ($query in $raw) {
        $failureKind = if ($null -ne $query.PSObject.Properties['FailureKind']) { [string]$query.FailureKind } else { '' }
        if ([bool]$query.Success) {
            $status = 'Healthy'
            $message = "Resolved $($query.Name) [$($query.QueryType)] to $(@($query.Answers) -join ', ') in $($query.DurationMs) ms."
            $recommendation = ''
        }
        elseif ($failureKind -eq 'UnsupportedQuery') {
            $status = 'Unknown'
            $message = "DNS query $($query.Name) [$($query.QueryType)] is not supported by the target runtime."
            $recommendation = 'Run the check on a target that provides Resolve-DnsName, or use A/AAAA resolution without a custom DNS server.'
        }
        else {
            $status = 'Critical'
            $message = "DNS resolution failed for $($query.Name) [$($query.QueryType)]."
            $recommendation = 'Validate DNS client configuration, resolver reachability, suffix/search behavior, and the authoritative record.'
        }

        $evidence = [ordered]@{
            Name        = [string]$query.Name
            QueryType   = [string]$query.QueryType
            Server      = [string]$query.Server
            Answers     = @($query.Answers)
            DurationMs  = [double]$query.DurationMs
            FailureKind = $failureKind
            Error       = [string]$query.Error
        }

        $criticalThreshold = if ($status -eq 'Unknown') { $null } else { 'Resolution failure' }
        $results += New-InfraPulseResult -Status $status -CheckName 'Dns' -Category 'Connectivity' -ComputerName $Context.ComputerName -Target ([string]$query.Name) -Message $message -ObservedValue (@($query.Answers) -join ', ') -WarningThreshold $null -CriticalThreshold $criticalThreshold -Recommendation $recommendation -Evidence $evidence -DurationMs ([double]$query.DurationMs) -ErrorMessage ([string]$query.Error)
    }

    return $results
}