Add Capture-MclCache: tree and control files of an MCL cache, SAS signatures blanked
This commit is contained in:
10
scripts/diagnostics/Capture-MclCache.cmd
Normal file
10
scripts/diagnostics/Capture-MclCache.cmd
Normal file
@@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
rem Double-click after a Media Creator Lite run finishes.
|
||||
rem Asks pro or ltsc, then runs Capture-MclCache.ps1 from this folder.
|
||||
setlocal
|
||||
set "LABEL=%~1"
|
||||
if "%LABEL%"=="" set /p "LABEL=Which MCL run just finished? Type pro or ltsc and press Enter: "
|
||||
if "%LABEL%"=="" set "LABEL=run"
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0Capture-MclCache.ps1" -Label "%LABEL%"
|
||||
echo.
|
||||
pause
|
||||
134
scripts/diagnostics/Capture-MclCache.ps1
Normal file
134
scripts/diagnostics/Capture-MclCache.ps1
Normal file
@@ -0,0 +1,134 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Captures the layout of a Media Creator Lite cache, plus its small
|
||||
control files, so the PXE import can be checked against what the
|
||||
current MCL actually writes.
|
||||
|
||||
.DESCRIPTION
|
||||
Run on the PC where Media Creator Lite just FINISHED a run. Run it once
|
||||
after the Windows 11 Pro run and once after the Windows 11 LTSC run,
|
||||
before starting the next MCL run (MCL deletes the cache when it starts).
|
||||
|
||||
.\Capture-MclCache.ps1 -Label pro
|
||||
.\Capture-MclCache.ps1 -Label ltsc
|
||||
|
||||
It writes, under the Desktop:
|
||||
mcl-capture-<label>-<timestamp>\
|
||||
tree.csv every file: path, size, last write time (UTC)
|
||||
summary.txt tag files and their contents, file counts, totals
|
||||
files\ copies of the small text files (*.json, *.xml,
|
||||
*.tag, *.txt, *.ini, *.log, *.cmd, *.ps1, *.bat)
|
||||
under 5 MB, same relative paths
|
||||
mcl-capture-<label>-<timestamp>.zip
|
||||
|
||||
Nothing large is copied: no WIM, driver, MSU or installer. Text copies
|
||||
have any Azure SAS signature (sig=...) blanked, so the zip carries no
|
||||
storage credential. Read-only on the cache; needs no admin rights.
|
||||
|
||||
.PARAMETER Label
|
||||
Short name for this run, for example pro or ltsc.
|
||||
|
||||
.PARAMETER Root
|
||||
The Media Creator folder. Default C:\ProgramData\GEAerospace\MediaCreator.
|
||||
|
||||
.PARAMETER OutDir
|
||||
Where to write the capture. Default: the current user's Desktop, or
|
||||
this script's folder when there is no Desktop.
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][ValidatePattern('^[A-Za-z0-9_-]{1,20}$')][string]$Label,
|
||||
[string]$Root = "C:\ProgramData\GEAerospace\MediaCreator",
|
||||
[string]$OutDir = [Environment]::GetFolderPath("Desktop")
|
||||
)
|
||||
$ErrorActionPreference = "Stop"
|
||||
if (-not $OutDir) { $OutDir = $PSScriptRoot } # no Desktop (e.g. run as SYSTEM)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Root -PathType Container)) {
|
||||
Write-Host "Media Creator folder not found: $Root" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
$Root = (Resolve-Path -LiteralPath $Root).ProviderPath.TrimEnd('\')
|
||||
|
||||
$stamp = Get-Date -Format "yyyyMMdd-HHmmss"
|
||||
$dest = Join-Path $OutDir "mcl-capture-$Label-$stamp"
|
||||
$filesDir = Join-Path $dest "files"
|
||||
New-Item -ItemType Directory -Path $filesDir -Force | Out-Null
|
||||
|
||||
$textExt = @(".json", ".xml", ".tag", ".txt", ".ini", ".log", ".cmd", ".ps1", ".bat")
|
||||
$maxCopy = 5MB
|
||||
# Azure SAS signature in a URL or connection string.
|
||||
$sasPattern = '(?i)(sig=)[^&"''\s<]+'
|
||||
|
||||
Write-Host "Reading $Root ..."
|
||||
$all = @(Get-ChildItem -LiteralPath $Root -Recurse -Force -File -ErrorAction SilentlyContinue)
|
||||
|
||||
$rows = foreach ($f in $all) {
|
||||
[pscustomobject]@{
|
||||
Path = $f.FullName.Substring($Root.Length + 1)
|
||||
Length = $f.Length
|
||||
LastWriteUtc = $f.LastWriteTimeUtc.ToString("yyyy-MM-ddTHH:mm:ssZ")
|
||||
}
|
||||
}
|
||||
$rows | Sort-Object Path | Export-Csv -LiteralPath (Join-Path $dest "tree.csv") -NoTypeInformation -Encoding UTF8
|
||||
|
||||
$copied = 0
|
||||
$redacted = 0
|
||||
$skippedBig = New-Object System.Collections.Generic.List[string]
|
||||
foreach ($f in $all) {
|
||||
if ($textExt -notcontains $f.Extension.ToLower()) { continue }
|
||||
$rel = $f.FullName.Substring($Root.Length + 1)
|
||||
if ($f.Length -gt $maxCopy) { $skippedBig.Add("$rel ($($f.Length) bytes)"); continue }
|
||||
$target = Join-Path $filesDir $rel
|
||||
New-Item -ItemType Directory -Path (Split-Path $target -Parent) -Force | Out-Null
|
||||
$bytes = [System.IO.File]::ReadAllBytes($f.FullName)
|
||||
$text = [System.Text.Encoding]::UTF8.GetString($bytes)
|
||||
if ($text -match $sasPattern) {
|
||||
$text = [regex]::Replace($text, $sasPattern, '${1}REDACTED')
|
||||
$bytes = [System.Text.Encoding]::UTF8.GetBytes($text)
|
||||
$redacted++
|
||||
}
|
||||
[System.IO.File]::WriteAllBytes($target, $bytes)
|
||||
$copied++
|
||||
}
|
||||
|
||||
$tags = @($all | Where-Object { $_.Extension -eq ".tag" })
|
||||
$sum = New-Object System.Collections.Generic.List[string]
|
||||
$sum.Add("Media Creator Lite cache capture")
|
||||
$sum.Add("Label: $Label")
|
||||
$sum.Add("Captured: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss zzz')")
|
||||
$sum.Add("Computer: $env:COMPUTERNAME")
|
||||
$sum.Add("Root: $Root")
|
||||
$sum.Add("PowerShell $($PSVersionTable.PSVersion)")
|
||||
$sum.Add("")
|
||||
$sum.Add("Files: $($all.Count), total $([math]::Round((($all | Measure-Object Length -Sum).Sum) / 1GB, 2)) GB")
|
||||
$sum.Add("Text files copied: $copied (SAS signatures blanked in $redacted)")
|
||||
$sum.Add("")
|
||||
$sum.Add("Tag files ($($tags.Count)):")
|
||||
foreach ($t in $tags) {
|
||||
$content = (Get-Content -LiteralPath $t.FullName -Raw -ErrorAction SilentlyContinue)
|
||||
if ($content) { $content = $content.Trim() }
|
||||
$sum.Add(" $($t.FullName.Substring($Root.Length + 1)) [$content] written $($t.LastWriteTime)")
|
||||
}
|
||||
if ($tags.Count -eq 0) { $sum.Add(" NONE - the MCL run did not finish") }
|
||||
$sum.Add("")
|
||||
$sum.Add("Top-level folders:")
|
||||
foreach ($g in ($rows | Group-Object { ($_.Path -split '\\')[0..1] -join '\' } | Sort-Object Name)) {
|
||||
$gb = [math]::Round((($g.Group | Measure-Object Length -Sum).Sum) / 1GB, 2)
|
||||
$sum.Add(" $($g.Name): $($g.Count) files, $gb GB")
|
||||
}
|
||||
if ($skippedBig.Count) {
|
||||
$sum.Add("")
|
||||
$sum.Add("Text files over 5 MB, not copied:")
|
||||
foreach ($s in $skippedBig) { $sum.Add(" $s") }
|
||||
}
|
||||
$sum | Set-Content -LiteralPath (Join-Path $dest "summary.txt") -Encoding UTF8
|
||||
|
||||
$zip = "$dest.zip"
|
||||
Compress-Archive -Path (Join-Path $dest "*") -DestinationPath $zip -Force
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Captured $($all.Count) files ($copied text files copied)." -ForegroundColor Green
|
||||
if ($tags.Count -eq 0) {
|
||||
Write-Host "WARNING: no .tag file. MCL did not finish this run." -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host "Send back: $zip"
|
||||
Reference in New Issue
Block a user