1. #requires -version 2
  2. #CompareFolder.ps1
  3. # Compares the contents of two folders.
  4. # Usage:
  5. #   CompareFolder.ps1 "<Known Good Folder>" "<Folder to Check>"
  6. # Created by sylver_dragon (reddit.com) 9/21/2010
  7. # Version History:
  8. #   0.1 - File Creation (sylver_dragon) 9/21/2010
  9. #   1.0 - Script functional (sylver_dragon) 9/21/2010
  10. Param(
  11.   [switch]$Recurse,
  12.   [switch]$Repair,
  13.   [switch]$RemoveExtras,
  14.   [Parameter(Position=0, Mandatory=$true)]
  15.     [string]$KnownGood = $(throw "Please provide the Known Good folder"),
  16.   [Parameter(Position=1, Mandatory=$true)]
  17.     [string]$CheckFolder = $(throw "Please provide the folder to check"),
  18.   [Parameter(Position=2, Mandatory=$false)]
  19.     [string]$ReportLocation = (Get-Location).Path + "CompareFolderReport_" + (Get-Date).ToFileTime() + ".xml"
  20.   )
  21.  
  22. $ErrorActionPreference = "Stop"
  23.  
  24. function MD5Hash($sFileName)
  25. {
  26.   $inFile = New-Object System.IO.StreamReader($sFileName)
  27.   $Crypto = [System.Security.Cryptography.MD5]::Create()
  28.   $hashArray = $Crypto.ComputeHash($inFile.BaseStream)
  29.   $inFile.Close()
  30.   foreach ($byte in $hashArray) { $rtn +={0:X2}-f $byte}
  31.   return $rtn
  32. }
  33.  
  34. # ********** Main Function **********
  35. # Check for both folders existance
  36. if(!(test-path $KnownGood))
  37. {
  38.   throw "Unable to connect to $KnownGood, Please check the folder and try again"
  39. }
  40. if(!(test-path $CheckFolder))
  41. {
  42.   throw "Unable to connect to $CheckFolder, Please check the folder and try again"
  43. }
  44.  
  45. # Make sure that both paths end with a backslash
  46. if($KnownGood[-1] -ne "\")
  47. {
  48.   $KnownGood += "\"
  49. }
  50. if($CheckFolder[-1] -ne "\")
  51. {
  52.   $CheckFolder += "\"
  53. }
  54.  
  55. # Get the file lists
  56. if($Recurse)
  57. {
  58.   $GoodList = Get-ChildItem -Recurse $KnownGood | Where{$_.PSIsContainer -eq $false} | ForEach{$_.FullName -replace $KnownGood.replace("\","\\").replace("$","\$"),""}
  59.   $CheckList = Get-ChildItem -Recurse $CheckFolder | Where{$_.PSIsContainer -eq $false} | ForEach{$_.FullName -replace $CheckFolder.replace("\","\\").replace("$","\$"),""}
  60. }
  61. else
  62. {
  63.   $GoodList = Get-ChildItem $KnownGood | Where{$_.PSIsContainer -eq $false} | ForEach{$_.Name}
  64.   $CheckList = Get-ChildItem $CheckFolder | Where{$_.PSIsContainer -eq $false} | ForEach{$_.Name}
  65. }
  66.  
  67. # Build the report framework and add the settings
  68. $xmlReport = new-object System.xml.xmlDataDocument
  69. $xeRoot =  $xmlReport.CreateElement("report")
  70. $result = $xmlReport.AppendChild($xeRoot)
  71. $xeSettings = $xmlReport.CreateElement("settings")
  72. $xeSettings.SetAttribute("KnownGood",$KnownGood)
  73. $xeSettings.SetAttribute("CheckFolder",$CheckFolder)
  74. $xeSettings.SetAttribute("Recurse",$Recurse)
  75. $xeSettings.SetAttribute("Repair",$Repair)
  76. $xeSettings.SetAttribute("Runtime",(Get-Date).ToString())
  77. $result = $xeRoot.AppendChild($xeSettings)
  78. $xeMissing = $xmlReport.CreateElement("missing")
  79. $result = $xeRoot.AppendChild($xeMissing)
  80. $xeExtra = $xmlReport.CreateElement("extra")
  81. $result = $xeRoot.AppendChild($xeExtra)
  82. $xeMismatch = $xmlReport.CreateElement("mismatch")
  83. $result = $xeRoot.AppendChild($xeMismatch)
  84. $xeMatch = $xmlReport.CreateElement("match")
  85. $result = $xeRoot.AppendChild($xeMatch)
  86. $xeErrors = $xmlReport.CreateElement("errors")
  87. $result = $xeRoot.AppendChild($xeErrors)
  88.  
  89. ForEach($sFile in $GoodList)
  90. {
  91.   if($CheckList -contains $sFile)
  92.   {
  93.     $GoodFullName = $KnownGood + $sFile
  94.     $CheckFullName = $CheckFolder + $sFile
  95.     $GoodHash = MD5Hash($GoodFullName)
  96.     $CheckHash = MD5Hash($CheckFullName)
  97.     if($CheckHash -eq $GoodHash) # Handles matches
  98.     {
  99.       $xeFile = $xmlReport.CreateElement("file")
  100.       $xeFile.SetAttribute("Name",$sFile)
  101.       $result = $xeMatch.AppendChild($xeFile)
  102.     }
  103.     else # Handles mismatches
  104.     {
  105.       $xeFile = $xmlReport.CreateElement("file")
  106.       $xeFile.SetAttribute("Name",$sFile)
  107.       $xeFile.SetAttribute("GoodHash",$GoodHash)
  108.       $xeFile.SetAttribute("CheckHash",$CheckHash)
  109.       if($Repair)
  110.       {
  111.         Copy-Item $GoodFullName $CheckFullName
  112.         $ReCheckHash = MD5Hash($CheckFullName)
  113.         if($ReCheckHash -ne $GoodHash)
  114.         {
  115.           $xeFile.SetAttribute("repaired",$false)
  116.           $xeError = $xmlReport.CreateElement("error")
  117.           $xeError.SetAttribute("type","Mismatch Repair")
  118.           $xeError.SetAttribute("filename",$sFile)
  119.           $xeError.SetAttribute("GoodHash",$GoodHash)
  120.           $xeError.SetAttribute("CheckHash",$CheckHash)
  121.           $xeError.SetAttribute("ReCheckHash",$ReCheckHash)
  122.           $result = $xeErrors.AppendChild($xeError)
  123.         }
  124.         else
  125.         {
  126.           $xeFile.SetAttribute("repaired",$true)
  127.         }
  128.       }
  129.       else
  130.       {
  131.         $xeFile.SetAttribute("repaired",$false)
  132.       }
  133.       $result = $xeMismatch.AppendChild($xeFile)
  134.     }
  135.   }
  136.   else # handles missing
  137.   {
  138.     $xeFile = $xmlReport.CreateElement("file")
  139.     $xeFile.SetAttribute("name",$sFile)
  140.     if($Repair)
  141.     {
  142.       $GoodFullName = $KnownGood + $sFile
  143.       $CheckFullName = $CheckFolder + $sFile
  144.       $sFullPath = ""
  145.       For($i = 0;$i -lt ($CheckFullName.split("\").length) - 1;$i++)
  146.       {
  147.         $sFullPath += ($CheckFullName.split("\"))[$i] + "\"
  148.       }
  149.       if(!(test-path $sFullPath))
  150.       {
  151.         $result = New-Item -Type Directory -Path $sFullPath
  152.       }
  153.       Copy-Item $GoodFullName $CheckFullName
  154.       $GoodHash = MD5Hash($GoodFullName)
  155.       $ReCheckHash = MD5Hash($CheckFullName)
  156.       if($ReCheckHash -ne $GoodHash)
  157.       {
  158.         $xeFile.SetAttribute("repaired",$false)
  159.         $xeError = $xmlReport.CreateElement("error")
  160.         $xeError.SetAttribute("type","Missing Repair")
  161.         $xeError.SetAttribute("filename",$sFile)
  162.         $xeError.SetAttribute("GoodHash",$GoodHash)
  163.         $xeError.SetAttribute("ReCheckHash",$ReCheckHash)
  164.         $result = $xeErrors.AppendChild($xeError)
  165.       }
  166.       else
  167.       {
  168.         $xeFile.SetAttribute("repaired",$true)
  169.       }
  170.     }
  171.     else
  172.     {
  173.       $xeFile.SetAttribute("repaired",$false)
  174.     }
  175.     $result = $xeMissing.AppendChild($xeFile)
  176.   }
  177. }
  178.  
  179. ForEach($sFile in $CheckList)
  180. {
  181.   if(!($GoodList -contains $sFile)) # Handles extra files
  182.   {
  183.     $xeFile = $xmlReport.CreateElement("file")
  184.     $xeFile.SetAttribute("name",$sFile)
  185.     if($RemoveExtras)
  186.     {
  187.       $RemoveFile = $CheckFolder + $sFile
  188.       Remove-Item $RemoveFile -Force
  189.       $xeFile.SetAttribute("removed",$true)
  190.     }
  191.     else
  192.     {
  193.       $xeFile.SetAttribute("removed",$false)
  194.     }
  195.     $result = $xeExtra.AppendChild($xeFile)
  196.   }
  197. }
  198.  
  199. $xmlReport.Save($ReportLocation)