Capture-MclCache: robocopy to Z: and verify by hash instead of zipping

This commit is contained in:
cproudlock
2026-10-18 09:31:04 -04:00
parent 902ce5780a
commit 716d4eee6b

View File

@@ -19,11 +19,16 @@
files\ copies of the small text files (*.json, *.xml, files\ copies of the small text files (*.json, *.xml,
*.tag, *.txt, *.ini, *.log, *.cmd, *.ps1, *.bat) *.tag, *.txt, *.ini, *.log, *.cmd, *.ps1, *.bat)
under 5 MB, same relative paths under 5 MB, same relative paths
mcl-capture-<label>-<timestamp>.zip SHA256SUMS hash of every file above, for checking the copy
Then it robocopies that folder to -CopyTo (default Z:\mcl-capture, the
dev box pxe-images share), hashes every copied file back through the
share and compares with SHA256SUMS. No zip: archives arrive empty on
that share. The dev box can re-check with: sha256sum -c SHA256SUMS
Nothing large is copied: no WIM, driver, MSU or installer. Text copies Nothing large is copied: no WIM, driver, MSU or installer. Text copies
have any Azure SAS signature (sig=...) blanked, so the zip carries no have any Azure SAS signature (sig=...) blanked, so the capture carries
storage credential. Read-only on the cache; needs no admin rights. no storage credential. Read-only on the cache; needs no admin rights.
.PARAMETER Label .PARAMETER Label
Short name for this run, for example pro or ltsc. Short name for this run, for example pro or ltsc.
@@ -34,11 +39,16 @@
.PARAMETER OutDir .PARAMETER OutDir
Where to write the capture. Default: the current user's Desktop, or Where to write the capture. Default: the current user's Desktop, or
this script's folder when there is no Desktop. this script's folder when there is no Desktop.
.PARAMETER CopyTo
Folder to copy the capture into and verify. Default Z:\mcl-capture.
Pass -CopyTo none to keep it on this PC only.
#> #>
param( param(
[Parameter(Mandatory = $true)][ValidatePattern('^[A-Za-z0-9_-]{1,20}$')][string]$Label, [Parameter(Mandatory = $true)][ValidatePattern('^[A-Za-z0-9_-]{1,20}$')][string]$Label,
[string]$Root = "C:\ProgramData\GEAerospace\MediaCreator", [string]$Root = "C:\ProgramData\GEAerospace\MediaCreator",
[string]$OutDir = [Environment]::GetFolderPath("Desktop") [string]$OutDir = [Environment]::GetFolderPath("Desktop"),
[string]$CopyTo = "Z:\mcl-capture"
) )
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
if (-not $OutDir) { $OutDir = $PSScriptRoot } # no Desktop (e.g. run as SYSTEM) if (-not $OutDir) { $OutDir = $PSScriptRoot } # no Desktop (e.g. run as SYSTEM)
@@ -146,8 +156,13 @@ if ($skippedBig.Count) {
} }
$sum | Set-Content -LiteralPath (Join-Path $dest "summary.txt") -Encoding UTF8 $sum | Set-Content -LiteralPath (Join-Path $dest "summary.txt") -Encoding UTF8
$zip = "$dest.zip" # Relative path with forward slashes, the way sha256sum -c reads it.
Compress-Archive -Path (Join-Path $dest "*") -DestinationPath $zip -Force function Get-RelPath($full, $base) { $full.Substring($base.Length + 1).Replace('\', '/') }
$sumLines = foreach ($f in (Get-ChildItem -LiteralPath $dest -Recurse -File | Sort-Object FullName)) {
"$((Get-FileHash -LiteralPath $f.FullName -Algorithm SHA256).Hash.ToLower()) $(Get-RelPath $f.FullName $dest)"
}
[System.IO.File]::WriteAllText((Join-Path $dest "SHA256SUMS"), (($sumLines -join "`n") + "`n"))
Write-Host "" Write-Host ""
Write-Host "Captured $($all.Count) files ($copied text files copied)." -ForegroundColor Green Write-Host "Captured $($all.Count) files ($copied text files copied)." -ForegroundColor Green
@@ -157,4 +172,45 @@ if ($tags.Count -eq 0) {
if ($unreadable.Count) { if ($unreadable.Count) {
Write-Host "$($unreadable.Count) file(s) could not be read; listed in summary.txt." -ForegroundColor Yellow Write-Host "$($unreadable.Count) file(s) could not be read; listed in summary.txt." -ForegroundColor Yellow
} }
Write-Host "Send back: $zip" Write-Host "Capture folder: $dest"
if (-not $CopyTo -or $CopyTo -eq "none") { exit 0 }
$drive = Split-Path -Qualifier $CopyTo -ErrorAction SilentlyContinue
if (($drive -and -not (Test-Path -LiteralPath "$drive\")) -or -not (Test-Path -LiteralPath (Split-Path $CopyTo -Parent))) {
Write-Host ""
Write-Host "$CopyTo is not reachable, so nothing was copied." -ForegroundColor Yellow
Write-Host "Map Z: to the dev box pxe-images share and run again, or copy the folder above by hand."
exit 1
}
$target = Join-Path $CopyTo (Split-Path $dest -Leaf)
Write-Host ""
Write-Host "Copying to $target ..."
& robocopy.exe $dest $target /E /COPY:DT /R:2 /W:5 /NP /NFL /NDL /NJH /NJS | Out-Null
$rc = $LASTEXITCODE
if ($rc -ge 8) {
Write-Host "Copy FAILED (robocopy exit $rc)." -ForegroundColor Red
exit 2
}
# Read every file back through the share and compare with SHA256SUMS.
$bad = New-Object System.Collections.Generic.List[string]
foreach ($line in $sumLines) {
$hash = $line.Substring(0, 64)
$rel = $line.Substring(66)
$copy = Join-Path $target ($rel.Replace('/', '\'))
if (-not (Test-Path -LiteralPath $copy -PathType Leaf)) { $bad.Add("missing $rel"); continue }
$got = (Get-FileHash -LiteralPath $copy -Algorithm SHA256).Hash.ToLower()
if ($got -ne $hash) { $bad.Add("differs $rel ($((Get-Item -LiteralPath $copy).Length) bytes)") }
}
if ((Get-FileHash -LiteralPath (Join-Path $target "SHA256SUMS") -Algorithm SHA256).Hash -ne
(Get-FileHash -LiteralPath (Join-Path $dest "SHA256SUMS") -Algorithm SHA256).Hash) {
$bad.Add("differs SHA256SUMS")
}
if ($bad.Count) {
Write-Host "Copy NOT verified: $($bad.Count) of $($sumLines.Count + 1) files wrong." -ForegroundColor Red
foreach ($b in $bad) { Write-Host " $b" }
exit 2
}
Write-Host "Copied and verified: all $($sumLines.Count + 1) files match." -ForegroundColor Green
Write-Host "On the share: $target"