YOUR CAPSTONE PROJECT · LESSON 21

Build your inventory reporting tool

Build a reusable, tested report from inventory data.

30 min · learn at your pace

Your brief

The operations team has an inventory export and needs a focused patch-age report. They want a repeatable tool that accepts a source path and an output path, rather than a script tied to one file.

The supplied inventory is fictional. Patch age here is a simple practice rule, not a complete measure of system security.

Define the behaviour

Create New-InventoryReport with parameters Path and OutFile. Keep devices whose numeric PatchAgeDays is at least 30. Sort them by that number, largest first. Each output record must contain Name, Owner, PatchAgeDays, and Priority.

Set Priority to High at 60 days or more, otherwise Medium. Save the records as CSV to OutFile and also return them as objects. Throw a terminating error when the input file does not exist.

Build in small steps

  1. Inspect inventory.csv and import its records.
  2. Filter and sort, casting PatchAgeDays to an integer.
  3. Create the report objects and export them.
  4. Wrap the work in a function with parameters.
  5. Handle a missing source file and check your results.

Run your function against the supplied inventory and write report.csv. The checks also call it with an alternate dataset, including 29-, 30-, and 60-day boundaries. This verifies that the function processes its input instead of relying on one fixed report.

Take it further

After you finish, consider how you would document the function, validate malformed rows, or write additional tests. Those are useful next steps as you move into a specialist path.

Try an example.ps1
Import-Csv './inventory.csv'
YOUR TURN

Put it into practice.

Implement New-InventoryReport using the rules in the brief. Call it with ./inventory.csv and ./report.csv. The lab checks the returned objects, saved CSV, boundary values, and a missing input file.

See an explained solution
One way to solve it.ps1
function New-InventoryReport {
    param([string]$Path, [string]$OutFile)
    if (-not (Test-Path $Path -PathType Leaf)) {
        throw "Inventory file not found: $Path"
    }
    $report = @(Import-Csv $Path |
        Where-Object { [int]$_.PatchAgeDays -ge 30 } |
        Sort-Object { [int]$_.PatchAgeDays } -Descending |
        ForEach-Object {
            [pscustomobject]@{
                Name = $_.Name
                Owner = $_.Owner
                PatchAgeDays = [int]$_.PatchAgeDays
                Priority = if ([int]$_.PatchAgeDays -ge 60) { 'High' } else { 'Medium' }
            }
        })
    $report | Export-Csv $OutFile -NoTypeInformation
    $report
}
New-InventoryReport -Path './inventory.csv' -OutFile './report.csv'

The function has a small interface, performs numeric comparisons explicitly, and preserves structured data until export. Its output is useful both as a CSV and in a subsequent pipeline.