Skip to content

Instantly share code, notes, and snippets.

@iOnline247
Last active January 18, 2026 05:12
Show Gist options
  • Select an option

  • Save iOnline247/d1a69bfaa7d07a2d5a55d0e8620ce483 to your computer and use it in GitHub Desktop.

Select an option

Save iOnline247/d1a69bfaa7d07a2d5a55d0e8620ce483 to your computer and use it in GitHub Desktop.
Checksum the checksums!
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$LiteralPath,
[Parameter(Mandatory = $false)]
[string]$CompareCsvPath,
[Parameter(Mandatory = $false)]
[ValidateRange(1, 64)]
[int]$ThrottleLimit = 8,
[Parameter(Mandatory = $false)]
[ValidateSet("SHA256", "SHA1", "MD5", "SHA384", "SHA512")]
[string]$Algorithm = "SHA256",
[Parameter(Mandatory = $false)]
[string]$CSVDirectory
)
if (-not (Test-Path $LiteralPath)) {
throw "Path not found: $LiteralPath"
}
if ($CompareCsvPath -and -not (Test-Path $CompareCsvPath)) {
throw "Compare CSV not found: $CompareCsvPath"
}
$sep = [System.IO.Path]::DirectorySeparatorChar
$CmdletName = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)
if ([string]::IsNullOrEmpty($CSVDirectory)) {
$CSVDirectory = Join-Path ([System.IO.Path]::GetTempPath()) $CmdletName
}
if (-not (Test-Path $CSVDirectory)) {
New-Item -Path $CSVDirectory -ItemType Directory | Out-Null
}
$Algorithm = $Algorithm.ToLower();
$Timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$Guid = [guid]::NewGuid().ToString()
$fileLedgerCsvPath = Join-Path $CSVDirectory "checksums_${Algorithm}_${Timestamp}_${Guid}.csv"
$BasePath = (Resolve-Path $LiteralPath).Path.TrimEnd($sep)
function Write-LfCsv {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$Path,
[Parameter(Mandatory = $false)]
[switch]$NoBom
)
if (-not (Test-Path $Path)) {
throw "CSV not found: $Path"
}
$content = Get-Content -Path $Path -Raw
$content = $content -replace "`r`n", "`n"
if ($NoBom) {
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
[System.IO.File]::WriteAllText($Path, $content, $utf8NoBom)
}
else {
Set-Content -Path $Path -Value $content -NoNewline -Encoding UTF8
}
}
function Get-ChecksumData {
param (
[Parameter(Mandatory)]
[string]$BaseDirectory,
[Parameter(Mandatory)]
[int]$Throttle,
[Parameter(Mandatory)]
[string]$HashAlgorithm
)
$files = Get-ChildItem -Path $BaseDirectory -Recurse -File
$total = $files.Count
Write-Host "πŸ”’ Total files to process: $total"
$files | ForEach-Object -Parallel {
$hash = Get-FileHash -LiteralPath $_.FullName -Algorithm $using:HashAlgorithm
$sep = [System.IO.Path]::DirectorySeparatorChar
$relPath = $_.FullName.Substring($using:BaseDirectory.Length).TrimStart($sep)
[pscustomobject]@{
RelativePath = $relPath
FileName = $_.Name
Algorithm = $using:HashAlgorithm
Hash = $hash.Hash.ToLower()
}
} -ThrottleLimit $Throttle | Sort-Object RelativePath
}
# Main Execution
Write-Host "πŸ“ Scanning directory (PARALLEL=$ThrottleLimit, Algorithm=$Algorithm): $BasePath"
$directoryChecksumData = Get-ChecksumData -BaseDirectory $BasePath -Throttle $ThrottleLimit -HashAlgorithm $Algorithm
$directoryChecksumData | Export-Csv -Path $fileLedgerCsvPath -NoTypeInformation -Encoding UTF8
# Normalize to LF-only line endings and write without BOM
Write-LfCsv -Path $fileLedgerCsvPath -NoBom
Write-Host "βœ… File Ledger CSV generated:"
Write-Host " $fileLedgerCsvPath"
$newHash = (Get-FileHash -LiteralPath $fileLedgerCsvPath -Algorithm $Algorithm).Hash.ToLower()
# Verify against existing CSV if provided
if ($CompareCsvPath) {
$oldHash = (Get-FileHash -LiteralPath $CompareCsvPath -Algorithm $Algorithm).Hash.ToLower()
if ($oldHash -eq $newHash) {
Write-Host "βœ… CSV HASH MATCH β€” DATA IDENTICAL" -ForegroundColor Green
}
else {
Write-Host "❌ CSV HASH MISMATCH" -ForegroundColor Red
Write-Host " Expected: $oldHash"
Write-Host " Actual: $newHash"
}
}
# Output the new hash to the pipeline
$newHash
@iOnline247
Copy link
Author

Example Usages:

# Creates a CSV in $env:TEMP and outputs the checksum of that CSV to the pipeline.
.\Get-DirectoryChecksums.ps1 -Path "D:\bkp"

# Creates a CSV in $env:TEMP, verifies the checksum of the newly generated CSV matches the `-CompareCsvPath` file, and outputs the checksum of the new CSV to the pipeline.
.\Get-DirectoryChecksums.ps1 -Path "D:\bkp" -CompareCsvPath "$env:TEMP\Get-DirectoryChecksums\checksums_SHA256_20260108_225107_30768d14-6777-4705-96ff-4aebecb3a28b.csv"

# Creates a CSV in $env:TEMP using SHA512 for the checksums, and outputs the checksum of that CSV to the pipeline.
.\Get-DirectoryChecksums.ps1 -Path "D:\bkp" -Algorithm SHA512

# Creates a CSV in the "D:\temp" and outputs the checksum of that CSV to the pipeline.
.\Get-DirectoryChecksums.ps1 -Path "D:\bkp" -CSVDirectory "D:\temp"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment