#requires -version 2
#CompareFolder.ps1
# Compares the contents of two folders.
# Usage:
# CompareFolder.ps1 "<Known Good Folder>" "<Folder to Check>"
# Created by sylver_dragon (reddit.com) 9/21/2010
# Version History:
# 0.1 - File Creation (sylver_dragon) 9/21/2010
# 1.0 - Script functional (sylver_dragon) 9/21/2010
Param(
[switch]$Recurse,
[switch]$Repair,
[switch]$RemoveExtras,
[Parameter(Position=0, Mandatory=$true)]
[string]$KnownGood = $(throw "Please provide the Known Good folder"),
[Parameter(Position=1, Mandatory=$true)]
[string]$CheckFolder = $(throw "Please provide the folder to check"),
[Parameter(Position=2, Mandatory=$false)]
[string]$ReportLocation = (Get-Location).Path + "CompareFolderReport_" + (Get-Date).ToFileTime() + ".xml"
)
$ErrorActionPreference = "Stop"
function MD5Hash($sFileName)
{
$inFile = New-Object System.IO.StreamReader($sFileName)
$Crypto = [System.Security.Cryptography.MD5]::Create()
$hashArray = $Crypto.ComputeHash($inFile.BaseStream)
$inFile.Close()
foreach ($byte in $hashArray) { $rtn += “{0:X2}” -f $byte}
return $rtn
}
# ********** Main Function **********
# Check for both folders existance
if(!(test-path $KnownGood))
{
throw "Unable to connect to $KnownGood, Please check the folder and try again"
}
if(!(test-path $CheckFolder))
{
throw "Unable to connect to $CheckFolder, Please check the folder and try again"
}
# Make sure that both paths end with a backslash
if($KnownGood[-1] -ne "\")
{
$KnownGood += "\"
}
if($CheckFolder[-1] -ne "\")
{
$CheckFolder += "\"
}
# Get the file lists
if($Recurse)
{
$GoodList = Get-ChildItem -Recurse $KnownGood | Where{$_.PSIsContainer -eq $false} | ForEach{$_.FullName -replace $KnownGood.replace("\","\\").replace("$","\$"),""}
$CheckList = Get-ChildItem -Recurse $CheckFolder | Where{$_.PSIsContainer -eq $false} | ForEach{$_.FullName -replace $CheckFolder.replace("\","\\").replace("$","\$"),""}
}
else
{
$GoodList = Get-ChildItem $KnownGood | Where{$_.PSIsContainer -eq $false} | ForEach{$_.Name}
$CheckList = Get-ChildItem $CheckFolder | Where{$_.PSIsContainer -eq $false} | ForEach{$_.Name}
}
# Build the report framework and add the settings
$xmlReport = new-object System.xml.xmlDataDocument
$xeRoot = $xmlReport.CreateElement("report")
$result = $xmlReport.AppendChild($xeRoot)
$xeSettings = $xmlReport.CreateElement("settings")
$xeSettings.SetAttribute("KnownGood",$KnownGood)
$xeSettings.SetAttribute("CheckFolder",$CheckFolder)
$xeSettings.SetAttribute("Recurse",$Recurse)
$xeSettings.SetAttribute("Repair",$Repair)
$xeSettings.SetAttribute("Runtime",(Get-Date).ToString())
$result = $xeRoot.AppendChild($xeSettings)
$xeMissing = $xmlReport.CreateElement("missing")
$result = $xeRoot.AppendChild($xeMissing)
$xeExtra = $xmlReport.CreateElement("extra")
$result = $xeRoot.AppendChild($xeExtra)
$xeMismatch = $xmlReport.CreateElement("mismatch")
$result = $xeRoot.AppendChild($xeMismatch)
$xeMatch = $xmlReport.CreateElement("match")
$result = $xeRoot.AppendChild($xeMatch)
$xeErrors = $xmlReport.CreateElement("errors")
$result = $xeRoot.AppendChild($xeErrors)
ForEach($sFile in $GoodList)
{
if($CheckList -contains $sFile)
{
$GoodFullName = $KnownGood + $sFile
$CheckFullName = $CheckFolder + $sFile
$GoodHash = MD5Hash($GoodFullName)
$CheckHash = MD5Hash($CheckFullName)
if($CheckHash -eq $GoodHash) # Handles matches
{
$xeFile = $xmlReport.CreateElement("file")
$xeFile.SetAttribute("Name",$sFile)
$result = $xeMatch.AppendChild($xeFile)
}
else # Handles mismatches
{
$xeFile = $xmlReport.CreateElement("file")
$xeFile.SetAttribute("Name",$sFile)
$xeFile.SetAttribute("GoodHash",$GoodHash)
$xeFile.SetAttribute("CheckHash",$CheckHash)
if($Repair)
{
Copy-Item $GoodFullName $CheckFullName
$ReCheckHash = MD5Hash($CheckFullName)
if($ReCheckHash -ne $GoodHash)
{
$xeFile.SetAttribute("repaired",$false)
$xeError = $xmlReport.CreateElement("error")
$xeError.SetAttribute("type","Mismatch Repair")
$xeError.SetAttribute("filename",$sFile)
$xeError.SetAttribute("GoodHash",$GoodHash)
$xeError.SetAttribute("CheckHash",$CheckHash)
$xeError.SetAttribute("ReCheckHash",$ReCheckHash)
$result = $xeErrors.AppendChild($xeError)
}
else
{
$xeFile.SetAttribute("repaired",$true)
}
}
else
{
$xeFile.SetAttribute("repaired",$false)
}
$result = $xeMismatch.AppendChild($xeFile)
}
}
else # handles missing
{
$xeFile = $xmlReport.CreateElement("file")
$xeFile.SetAttribute("name",$sFile)
if($Repair)
{
$GoodFullName = $KnownGood + $sFile
$CheckFullName = $CheckFolder + $sFile
$sFullPath = ""
For($i = 0;$i -lt ($CheckFullName.split("\").length) - 1;$i++)
{
$sFullPath += ($CheckFullName.split("\"))[$i] + "\"
}
if(!(test-path $sFullPath))
{
$result = New-Item -Type Directory -Path $sFullPath
}
Copy-Item $GoodFullName $CheckFullName
$GoodHash = MD5Hash($GoodFullName)
$ReCheckHash = MD5Hash($CheckFullName)
if($ReCheckHash -ne $GoodHash)
{
$xeFile.SetAttribute("repaired",$false)
$xeError = $xmlReport.CreateElement("error")
$xeError.SetAttribute("type","Missing Repair")
$xeError.SetAttribute("filename",$sFile)
$xeError.SetAttribute("GoodHash",$GoodHash)
$xeError.SetAttribute("ReCheckHash",$ReCheckHash)
$result = $xeErrors.AppendChild($xeError)
}
else
{
$xeFile.SetAttribute("repaired",$true)
}
}
else
{
$xeFile.SetAttribute("repaired",$false)
}
$result = $xeMissing.AppendChild($xeFile)
}
}
ForEach($sFile in $CheckList)
{
if(!($GoodList -contains $sFile)) # Handles extra files
{
$xeFile = $xmlReport.CreateElement("file")
$xeFile.SetAttribute("name",$sFile)
if($RemoveExtras)
{
$RemoveFile = $CheckFolder + $sFile
Remove-Item $RemoveFile -Force
$xeFile.SetAttribute("removed",$true)
}
else
{
$xeFile.SetAttribute("removed",$false)
}
$result = $xeExtra.AppendChild($xeFile)
}
}
$xmlReport.Save($ReportLocation)