Check saved ports against VLAN policy
Join expected and observed interfaces using device and port identity, then report precise drift.
Let the expected inventory drive the comparison
expected-ports.json lists the interfaces this review must assess. observed-ports.csv contains saved configuration observations. Join on both DeviceId and Port. Port names such as ge-1 repeat on multiple devices.
Start with each expected interface, then look for its observation. Starting only with observed rows would hide a missing expected interface. The supplied export contains at most one row per device/port pair; an orphan observation from D99 is outside this declared review.
Apply the policy in order
Missing observation takes precedence. Next compare Mode. Only when both modes are access should you compare numeric AccessVlan. Trunk AccessVlan is outside this simplified policy and must not create a false finding.
Get-Content '/home/user/expected-ports.json' -Raw | ConvertFrom-Json
Return Missing, ModeDrift, or VlanDrift findings, omitting compliant ports. The CSV VLAN field is text; cast it before a numeric comparison. A mode mismatch is one finding, not both a mode and VLAN finding for the same port.
This report proposes no device changes. A later reporting lesson separates proposed configuration changes from observations that need collection or investigation first.
Import-Csv '/home/user/observed-ports.csv'
Put it into practice.
Compare each expected-ports.json interface with observed-ports.csv using DeviceId and Port together. Emit DeviceId, Port, and State for non-compliant interfaces only. Priority: Missing, then ModeDrift, then VlanDrift for access ports. Ignore access-VLAN values on matching trunks and observations outside the expected list.
See an explained solution
$observed = @(Import-Csv '/home/user/observed-ports.csv')
foreach ($expected in Get-Content '/home/user/expected-ports.json' -Raw | ConvertFrom-Json) {
$actual = $observed | Where-Object { $_.DeviceId -eq $expected.DeviceId -and $_.Port -eq $expected.Port }
$state = if (-not $actual) { 'Missing' }
elseif ($actual.Mode -ne $expected.Mode) { 'ModeDrift' }
elseif ($expected.Mode -eq 'access' -and [int]$actual.AccessVlan -ne $expected.AccessVlan) { 'VlanDrift' }
else { 'Compliant' }
if ($state -ne 'Compliant') { [pscustomobject]@{ DeviceId=$expected.DeviceId; Port=$expected.Port; State=$state } }
}
A compound identity prevents a port on another switch from satisfying the join. Ordered rules avoid misleading VLAN findings when the mode differs, while missing expected observations remain explicit.