Reconcile neighbour observations
Compare declared links with a supplied neighbour export while preserving missing observations.
A neighbour observation is evidence from one endpoint
The supplied export models normalised neighbour data, similar to information a collector could obtain from LLDP. It records the local device/port and the observed remote device/port. This lesson reads saved files; it does not query LLDP or contact devices.
Compare both remote identity fields. Seeing the expected device on the wrong remote port still differs from the declared link. Keep the local compound identity in every result because interface names repeat across devices.
Distinguish mismatch from missing evidence
Return Matches when both peer fields agree, UnexpectedNeighbour when an observation differs, and Missing when no observation exists for the declared local endpoint. Preserve every expected link, including successful matches.
Import-Csv '/home/user/neighbours.csv' | Select-Object DeviceId, Port, PeerDeviceId, PeerPort
Missing data does not prove a cable is disconnected. Discovery may be disabled, an entry may have aged out, or collection may be incomplete. Likewise, a matching neighbour record does not prove bidirectional forwarding or application connectivity.
The fixtures deliberately contain one record per observed local endpoint. Real exports can contain multiple neighbours or stale observations, so a production workflow would need explicit freshness and ambiguity rules before treating the result as authoritative.
Get-Content '/home/user/expected-links.json' -Raw | ConvertFrom-Json
Put it into practice.
For each expected-links.json entry, look up neighbours.csv by DeviceId and Port. Emit DeviceId, Port, ExpectedPeer (remote DeviceId/Port), ObservedPeer (null if absent), and State: Matches, UnexpectedNeighbour, or Missing. A wrong peer port is a mismatch even when the peer device is correct.
See an explained solution
$observed = @(Import-Csv '/home/user/neighbours.csv')
foreach ($link in Get-Content '/home/user/expected-links.json' -Raw | ConvertFrom-Json) {
$found = $observed | Where-Object { $_.DeviceId -eq $link.DeviceId -and $_.Port -eq $link.Port }
$state = if (-not $found) { 'Missing' }
elseif ($found.PeerDeviceId -eq $link.PeerDeviceId -and $found.PeerPort -eq $link.PeerPort) { 'Matches' }
else { 'UnexpectedNeighbour' }
[pscustomobject]@{ DeviceId=$link.DeviceId; Port=$link.Port; ExpectedPeer=($link.PeerDeviceId + '/' + $link.PeerPort); ObservedPeer=$(if ($found) { $found.PeerDeviceId + '/' + $found.PeerPort } else { $null }); State=$state }
}
Matching both peer fields catches the wrong-port observation. The missing link remains a collection question, and all four declared endpoints stay in the report.