How to create a file integrity report in PowerShell
This tutorial shows how to create a file integrity report in PowerShell to check for changes in critical files. The solution generates and stores hashes (SHA256) of files, compares later versions and highlights files added, removed or modified — useful for audits, detecting unauthorized changes and verified backups.
Prerequisites
- Windows with PowerShell 5.1 or PowerShell 7+
- Read permissions for the folder to be checked
- A report folder where to store the reference file (JSON or CSV)
Step 1: Concept and report structure
It is important to understand what we will record: for each file we store the relative path, the size, the last modification date and the SHA256 hash. The JSON format is easy to read and simple to compare. The reference file will be named file-integrity.json.
Step 2: Script to generate the initial report
This script creates the initial report with all files in the target folder. Run it from the parent folder or provide a full path to $RootPath.
$RootPath = 'C:\PastaASeguir' # Alterar para a pasta a verificar
$ReportFile = 'C:\Relatorios\file-integrity.json' # Onde guardar o relatório
Get-ChildItem -Path $RootPath -Recurse -File | ForEach-Object {
$relative = $_.FullName.Substring($RootPath.Length).TrimStart('\')
$hash = Get-FileHash -Path $_.FullName -Algorithm SHA256
[PSCustomObject]@{
Path = $relative
Size = $_.Length
LastWriteTime = $_.LastWriteTimeUtc
Hash = $hash.Hash
}
} | ConvertTo-Json -Depth 4 | Out-File -FilePath $ReportFile -Encoding UTF8
Write-Output "Relatório inicial gerado em: $ReportFile"
Step 3: Script to compare the current state with the report
This script loads the previous report, inspects the current state of the folder and identifies files Added, Removed and Modified. It also saves a new report with a timestamp for history.
$RootPath = 'C:\PastaASeguir'
$ReportFile = 'C:\Relatorios\file-integrity.json'
$NowReport = "C:\Relatorios\file-integrity-$(Get-Date -Format 'yyyyMMdd-HHmmss').json"
# Carregar relatório anterior
if (-Not (Test-Path $ReportFile)) { throw "Relatório anterior não encontrado: $ReportFile" }
$old = Get-Content $ReportFile -Raw | ConvertFrom-Json
$oldIndex = @{ }
foreach ($item in $old) { $oldIndex[$item.Path] = $item }
# Criar relatório atual em memória
$current = @()
Get-ChildItem -Path $RootPath -Recurse -File | ForEach-Object {
$relative = $_.FullName.Substring($RootPath.Length).TrimStart('\')
$hash = Get-FileHash -Path $_.FullName -Algorithm SHA256
$current += [PSCustomObject]@{
Path = $relative
Size = $_.Length
LastWriteTime = $_.LastWriteTimeUtc
Hash = $hash.Hash
}
}
# Index current
$currentIndex = @{ }
foreach ($item in $current) { $currentIndex[$item.Path] = $item }
# Detetar Added, Removed, Modified
$added = @()
$removed = @()
$modified = @()
foreach ($path in $currentIndex.Keys) {
if (-Not $oldIndex.ContainsKey($path)) { $added += $currentIndex[$path] }
else {
$oldItem = $oldIndex[$path]
$curItem = $currentIndex[$path]
if ($oldItem.Hash -ne $curItem.Hash -or $oldItem.Size -ne $curItem.Size) {
$modified += [PSCustomObject]@{
Path = $path
OldHash = $oldItem.Hash
NewHash = $curItem.Hash
OldSize = $oldItem.Size
NewSize = $curItem.Size
}
}
}
}
foreach ($path in $oldIndex.Keys) {
if (-Not $currentIndex.ContainsKey($path)) { $removed += $oldIndex[$path] }
}
# Guardar relatório atual para histórico
$current | ConvertTo-Json -Depth 4 | Out-File -FilePath $NowReport -Encoding UTF8
# Output resumido
$summary = [PSCustomObject]@{
Timestamp = (Get-Date).ToString('o')
Added = $added.Count
Removed = $removed.Count
Modified = $modified.Count
NewReport = $NowReport
}
$summary | ConvertTo-Json -Depth 3
# Também podemos guardar um log simples
$logFile = 'C:\Relatorios\file-integrity-log.txt'
Add-Content -Path $logFile -Value ("$((Get-Date).ToString('s')) - Added:$($added.Count) Removed:$($removed.Count) Modified:$($modified.Count)")
# Opcional: sobrescrever o relatório de referência para próxima comparação
$current | ConvertTo-Json -Depth 4 | Out-File -FilePath $ReportFile -Encoding UTF8
Step 4: Schedule or automate the check
To run automatically, use Task Scheduler. Create a task that runs PowerShell with the script path and parameters if needed. Configure it to run with appropriate permissions and with a trigger (e.g.: daily or hourly).
Verify the result
Check the JSON files and the log: the summary in JSON shows Added, Removed and Modified. Open the file file-integrity-log.txt for a quick history. In case of Modified, compare OldHash vs NewHash to confirm changes. Common errors: paths with insufficient permissions or exceptions on locked files — fix permissions or run with an account that has access.
Conclusion
You now have a simple solution to generate and compare file integrity reports with PowerShell; next steps include email notification when changes occur, excluding specific file types or integrating with SIEM. Tip: test first in a small folder to validate hashes and permissions before applying in production.