Public/Test-IntuneExport.ps1

# Auto-generated by module/Build-Module.ps1 from en/scripts/Verify-IntuneExport.ps1
# Edit the source script, then re-run Build-Module.ps1. Do not edit this file directly.

function Test-IntuneExport {
<#
.SYNOPSIS
    Offline integrity check of an export against its checksums.json (SHA-256).

.DESCRIPTION
    Detects files that were MODIFIED, MISSING, or UNTRACKED since the export was sealed. Works fully
    offline (no Graph connection). The exporter writes checksums.json automatically; use -Write to
    (re)seal an export you edited on purpose. Exit code 0 = clean, 1 = differences found.

.PARAMETER Path
    The export folder (contains the NN_* families and checksums.json).

.PARAMETER Write
    (Re)generate checksums.json for the folder instead of verifying.

.EXAMPLE
    .\Verify-IntuneExport.ps1 -Path .\input\Export_2026-07-15
    .\Verify-IntuneExport.ps1 -Path .\FixedExport -Write
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory)][string]$Path,
    [switch]$Write
)

$ErrorActionPreference = 'Stop'
if (-not (Test-Path -LiteralPath $Path)) { throw "Export folder not found: $Path" }
$root = (Resolve-Path -LiteralPath $Path).Path
$checkFile = Join-Path $root 'checksums.json'

function Get-RelHash {
    Get-ChildItem $root -Recurse -File | Where-Object { $_.Name -ne 'checksums.json' } | ForEach-Object {
        $rel = ($_.FullName.Substring($root.Length).TrimStart('\','/')) -replace '\\','/'
        [pscustomobject]@{ Rel = $rel; Hash = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash }
    }
}

if ($Write) {
    $sums = [ordered]@{}
    Get-RelHash | Sort-Object Rel | ForEach-Object { $sums[$_.Rel] = $_.Hash }
    ($sums | ConvertTo-Json -Depth 5) | Set-Content -LiteralPath $checkFile -Encoding UTF8
    Write-Host ("Sealed: {0} file(s) -> checksums.json" -f $sums.Count) -ForegroundColor Green
    return
}

if (-not (Test-Path -LiteralPath $checkFile)) { throw "checksums.json not found in $root. Run with -Write to create it." }
$expected = @{}
(Get-Content -LiteralPath $checkFile -Raw | ConvertFrom-Json).PSObject.Properties | ForEach-Object { $expected[$_.Name] = [string]$_.Value }
$actual = @{}
Get-RelHash | ForEach-Object { $actual[$_.Rel] = $_.Hash }

$modified = @(); $missing = @(); $untracked = @()
foreach ($rel in $expected.Keys) {
    if (-not $actual.ContainsKey($rel)) { $missing += $rel }
    elseif ($actual[$rel] -ne $expected[$rel]) { $modified += $rel }
}
foreach ($rel in $actual.Keys) { if (-not $expected.ContainsKey($rel)) { $untracked += $rel } }

Write-Host ""
Write-Host ("Verify: {0}" -f $root) -ForegroundColor Cyan
Write-Host (" tracked: {0} | modified: {1} | missing: {2} | untracked: {3}" -f $expected.Count, $modified.Count, $missing.Count, $untracked.Count)
foreach ($x in $modified)  { Write-Host (" [MODIFIED] {0}" -f $x) -ForegroundColor Yellow }
foreach ($x in $missing)   { Write-Host (" [MISSING] {0}" -f $x) -ForegroundColor Red }
foreach ($x in $untracked) { Write-Host (" [UNTRACKED] {0}" -f $x) -ForegroundColor Magenta }

if ($modified.Count + $missing.Count + $untracked.Count -eq 0) {
    Write-Host " OK - export integrity verified." -ForegroundColor Green
    return 0
} else {
    Write-Host " Differences found." -ForegroundColor Red
    return 1
}
}