04 / RELIABLE NETWORK AUTOMATION · LESSON 16

Write a reviewable network change report

Export proposed access-VLAN changes separately from missing observations, with an honest coverage summary.

15 min · learn at your pace

A proposal needs a reason and a known starting point

The supplied assessments describe expected access ports as Compliant, Drift, or Unknown. For Drift, propose SetAccessVlan with the observed CurrentVlan and DesiredVlan. For Unknown, propose Investigate: there is no observed starting configuration to support a direct change proposal.

Compliant ports produce no proposal, but they still contribute to assessment coverage. Return a CSV with DeviceId, Port, Action, CurrentVlan, and DesiredVlan. These are structured review records, not executable vendor commands.

Report coverage alongside changes

summary.json must retain PortCount, ChangeCount, UnknownCount, Coverage, and a sorted unique Devices array. Coverage is Empty when there were no expected ports, Incomplete when any observation is Unknown, and Complete otherwise. Complete does not mean zero drift; it means the required observations were available.

Get-Content '/home/user/assessments.json' -Raw | ConvertFrom-Json | Group-Object State

Keep empty CSV headers and preserve Devices as an array for zero or one device. Repeated runs replace the saved reports so stale proposals do not accumulate. Re-import the actual output files to check their structure.

This workflow changes only the virtual report files. Device access, credentials, approvals, vendor command generation, application, and rollback belong to a separately designed operational workflow.

Try an example.ps1
Get-Content '/home/user/assessments.json' -Raw | ConvertFrom-Json
YOUR TURN

Put it into practice.

Define Write-NetworkChangeReport -Assessments -OutputDirectory. Write proposals.csv and summary.json with the contracts above. Drift proposes SetAccessVlan; Unknown proposes Investigate; Compliant generates no proposal. Preserve array/header shapes, create the destination, and replace outputs on repeated calls. Return ProposalCount and Coverage.

See an explained solution
One way to solve it.ps1
function Write-NetworkChangeReport {
    param([AllowEmptyCollection()][object[]]$Assessments,[string]$OutputDirectory)
    $rows = @($Assessments)
    $proposals = @(foreach ($row in $rows) {
        if ($row.State -eq 'Compliant') { continue }
        [pscustomobject]@{ DeviceId=$row.DeviceId; Port=$row.Port; Action=$(if ($row.State -eq 'Unknown') { 'Investigate' } else { 'SetAccessVlan' }); CurrentVlan=$row.CurrentVlan; DesiredVlan=$row.DesiredVlan }
    })
    $unknown = @($rows | Where-Object State -EQ Unknown).Count
    $changes = @($rows | Where-Object State -EQ Drift).Count
    $coverage = if ($rows.Count -eq 0) { 'Empty' } elseif ($unknown -gt 0) { 'Incomplete' } else { 'Complete' }
    $summary = [pscustomobject]@{ PortCount=$rows.Count; ChangeCount=$changes; UnknownCount=$unknown; Coverage=$coverage; Devices=@($rows.DeviceId | Sort-Object -Unique) }
    New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null
    $path = Join-Path $OutputDirectory 'proposals.csv'
    if ($proposals.Count) { $proposals | Export-Csv -LiteralPath $path -NoTypeInformation }
    else { 'DeviceId,Port,Action,CurrentVlan,DesiredVlan' | Set-Content -LiteralPath $path }
    $summary | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json')
    [pscustomobject]@{ ProposalCount=$proposals.Count; Coverage=$coverage }
}

Only observed drift becomes a proposed configuration change. Missing observations become investigation work, while the summary keeps coverage separate from the number of changes.