This matters when today's inventory has one qualifying item. A consumer written to loop over [] should not have to guess whether a single {...} response means the contract changed.
Rehearse with in-memory records
This original exercise reads no files and makes no system changes. Save it as inventory-shape.ps1:
$records = @(
[pscustomobject]@{ Name = 'North'; Active = $true }
[pscustomobject]@{ Name = 'South'; Active = $false }
)
$none = @($records | Where-Object { $_.Name -eq 'Missing' })
$one = @($records | Where-Object { $_.Active })
$many = @($records)
Write-Output ('none=' + (ConvertTo-Json -InputObject $none -Depth 3 -Compress))
Write-Output ('one=' + (ConvertTo-Json -InputObject $one -Depth 3 -Compress))
Write-Output ('many=' + (ConvertTo-Json -InputObject $many -Depth 3 -Compress))
The expected shapes are:
none=[]
one=[{"Name":"North","Active":true}]
many=[{"Name":"North","Active":true},{"Name":"South","Active":false}]
This exact example produced array lengths 0, 1 and 2 under Windows PowerShell 5.1 during preparation. Property ordering is not the report's contract. The checks that matter are the top-level array, its length and the record values. @(...) collects pipeline output, including an empty result, while the named InputObject parameter supplies that collected array as the value being serialized.
Microsoft's array guide and ConvertTo-Json reference, checked October 24, 2026, describe these boundaries. PowerShell 7 also documents -AsArray; do not assume that switch exists on an older Windows PowerShell installation. The explicit array-and-InputObject form keeps this exercise clear about the object being supplied.
Check the actual shell before adapting the report
Run $PSVersionTable.PSVersion in the terminal that will execute the script. Use that shell's script invocation, such as pwsh -NoProfile -File ./inventory-shape.ps1 when PowerShell 7 is installed, or run ./inventory-shape.ps1 inside the intended PowerShell session.
When replacing the fictional records with a command, keep collection and serialization separate. Avoid piping through Format-Table first: formatting objects are for display and are not the original inventory records. Select the intended data properties before conversion. For nested data, choose a depth that represents the report and inspect truncation warnings in versions that provide them.
Request a shape contract, then test all three counts
Draft a read-only PowerShell report; do not run system changes.
Actual PowerShell version and permitted input command: [ENTER]
Required properties and nested fields: [ENTER]
Consumer contract: the JSON top-level value is always an array.
Collect selected objects and serialize the collected array explicitly.
Show zero-result, one-result and two-result fixtures. Do not convert
records to display-format objects or wrap JSON in a quoted string.
Choose a justified JSON depth. Keep version-specific alternatives
separate and show expected shapes without inventing actual execution.
The Windows environment guide helps when the wrong shell is launching a command. Once this script runs in the intended shell, parse all three outputs with the receiving program and check array lengths 0, 1 and 2. Keep those fixtures with the report so a later filtering change cannot quietly change the consumer's input type.