This script is a rewritten and updated versoin of Paul Fragale's DFSR validation script, which can be found here: http://gallery.technet.microsoft.com/scriptcenter/1de44cc1-ce79-4e98-9283-92548fc02af9.

Requirements

For any questions, comments or feedback, please e-mail mrcrassic@gmail.com. 

 

PowerShell
Edit|Remove
#requires -version 2 
 
# ============================================================================== 
# 
#    TITLE:         File Replication Utilities - File Replication Validator 
#    VERSION:    1.0 
#    AUTHOR:        Carlos Nunez (mrcrassic@gmail.com) 
#    CREATED ON:    6 January, 2012 
#    UPDATED ON:    - 
# 
#    This utility compares files copied from one location at another 
#    location. It has been designed to enumerate file information 
#    quickly for locations connected by latent or long-distance 
#    network links. 
# 
#    NOTE: Due to technical limitations, files are collected by this script in 
#         
# ============================================================================== 
 
# ============================================================================== 
# 
# CHANGELOG 
# ---------- 
#    MAJOR:    1.0 (6 January, 2012)    -    First release 
# 
# ============================================================================== 
 
# ----------------------------------------------------- 
#    FLY TRAP 
# ----------------------------------------------------- 
trap    { 
    Write-Error "An error occurred with this script: $($_)" 
    Write-Progress -Completed -Activity "`0" -Status "`0"            # Kill any progress bars. 
    exit 
} 
 
 
# ----------------------------------------------------- 
# Internal variables 
# ----------------------------------------------------- 
 
# FLAGS 
# ------- 
$_randomSampleRequested = $false 
 
# ----------------------------------------------------- 
# Output variables 
# ----------------------------------------------------- 
$files            = @() 
$filesStored    = @() 
 
# ----------------------------------------------------- 
#    MAIN 
# ----------------------------------------------------- 
 
if (!(Test-Path md5sum.exe))     
    { throw "md5sum.exe is missing. Please ensure that MD5sum is present in working directory."} 
 
# Collect information 
# TECH NOTE: Use System.IO.Directory::GetDirectories to test the path. This will 
#             throw an exception, which, unlike Test-Path, can be handled by  
#             try-catch 
# ------------------------------------------------------------------------------ 
try { 
    $source         = (Read-Host "Enter source directory: ").trim() -replace "(\\|\/)$","" 
    [void]([System.IO.Directory]::GetDirectories($source,'*',[System.IO.SearchOption]::TopDirectoryOnly)) 
    $target            = (Read-Host "Enter target directory: ").trim() -replace "(\\|\/)$","" 
    [void]([System.IO.Directory]::GetDirectories($target,'*',[System.IO.SearchOption]::TopDirectoryOnly))         
} catch { 
    throw "Directory does not exist." 
} 
 
# Obviously, the count must be a number 
# -------------------------------------- 
$count            =     try     {     [Int](Read-Host "Enter number of files to compare (or enter 0 for a random sample): ") }  
                    catch     {    throw "This must be a number." } 
 
$results        =     Read-Host "Enter filename to store results (in your current directory)" 
     
Clear-Host 
 
 
# If a random sample is requested, script will collect as many files as it can  
# within 2 to 10 seconds 
# ----------------------------------------------------------------------------- 
if ($count -eq 0)    { 
    Write-Host "`nRandom sample requested." 
    $_randomSampleRequested = $true 
} 
 
# Get file list.  
# TECH NOTE:     Using get-object for this is fine for querying nearby 
#                 computers (anything reachable within 5-10ms)  but becomes 
#                 increasingly laggy for anything farther than that because 
#                of each listing's additional properties (each listing is 
#                an independent object). 
# 
#                dir included in cmd.exe gets the raw text. We'll use this 
#                 to our advantage here. 
# ------------------------------------------------------------------------- 
 
if ($_randomSampleRequested)    { 
    Write-Host "`n`nGathering all files from $source. NOTE: This can take several minutes depending on network speed.`n" 
    $_EnumerationJob = Start-Job -InputObject $source {& cmd /"dir /a:-d /s /b $input"} 
    while ($true)     { 
     
        # Prepare status line 
        # --------------------- 
        if ($filesStored.Count -eq 1)     
            {    $_statusLine = '1 file received.'    } 
        else 
            {    $_statusLine = "$($filesStored.Count) files received."     } 
         
        # Write status 
        # ------------- 
        [System.Console]::SetCursorPosition(0,[System.Console]::CursorTop-1) 
        Write-Host $_statusLine 
         
        # If job is finished, print and break 
        # ------------------------------------ 
        if ($_EnumerationJob.State -eq' Completed')        { 
            [System.Console]::SetCursorPosition($_statusLine.length,[System.Console]::CursorTop-1) 
            Write-Host "..completed." 
            $count = $filesStored.Count 
            break 
        } 
         
        # Otherwise, collect the files and continue 
        # ------------------------------------------ 
        $_temp = "" 
        $_temp = Receive-Job $_EnumerationJob 
        if ($_temp.count -gt 0) {$filesStored +$_temp} 
        sleep 1 
         
    } 
} else { 
    try { 
        Write-Host "`n`nGathering $count files. This can take several moments depending on the source server's location. Please be patient." 
        $_job = Start-Job -InputObject $source {& cmd /dir /a:-//$input} 
        do { 
            if ($_job.State -eq "Completed")     {break} 
             
            $_temp = "" 
            $_temp = Receive-Job $_job 
            if ($_temp.count -gt 0) {$filesStored +$_temp} 
            sleep 1 
             
            # Display progress. 
            # ----------------- 
            $_enumStatus = [math]::round(($filesStored.Count/$count)*100,2) 
             
            # Might go over 100% if job collects more files than the count 
            # This will trigger an error, so set it back to 100 
            # ------------------------------------------------------------ 
            if ($_enumStatus -gt 100) {$_enumStatus = 100} 
             
            Write-Progress -Activity "File Enumeration Status" -Status "$($_enumStatus)% completed." -percentcomplete $_enumStatus 
        } while ($filesStored.count -lt $count) 
    } catch { 
        throw "Job could not complete successfully: $($_)" 
    } 
    Write-Progress -Completed -Activity "`0" -Status "`0" 
    Stop-Job $_job 
} 
 
# Exit if there are no files to work on. 
# --------------------------------------- 
if ($fileStored.Count -eq 0) 
    { throw "No files received" } 
 
# Remove any blank entries. 
# ------------------------- 
$filesStored = $filesStored | where {$_ -ne ''} 
 
# Start the output file. 
# ----------------------- 
Write-Host "Retrieving and comparing hashes." 
Write-Output "FileName,Source Hash,Destination Hash,Match Success" | Out-File $results 
$_rndDone = "" 
for ($i = 0; $i -le $count$i++)    { 
    # Find a random index 
    # --------------------- 
    while ($true)     { 
        $_rnd = get-random -max ($count-1) 
        if (!($_rndDone -contains $_rnd)) {break;} 
    } 
    $_rndDone +$_rnd 
     
    # Some more vars 
    # --------------- 
    $sourceFile = $filesStored[$_rnd] 
    $_progStatus = [math]::round(($i/$count)*100,2) 
     
    # File vars  
    # --------- 
    $targetFile    = "" 
    $_filesToCheck = @() 
     
    # Hash vars 
    # ---------- 
    $_hashResult = "" 
    $_sourceHash = ""     
    $_targetHash = "" 
     
    # Determine the target file. 
    # -------------------------- 
    $targetFile = $sourceFile.replace($source,$target) 
         
    # Verify that destination file exists. 
    # TECH NOTE: Windows is not able to check files whose paths are longer than 
    # 255 characters. Script will flag these files for manual checking later. 
    # -------------------------------------------------------------------------- 
    if ($targetFile.length -gt 255)    { 
        $_hashResult = "Manual check required" 
        Write-host "$($targetFile.substring(0,$(if ($targetFile.length -gt 30) {30} else {$targetFile.length})))$(if ($targetFile.length -lt 30) {} else {"...$($targetFile.substring($targetFile.lastIndexOf('\')+1,$targetFile.length-$targetFile.lastIndexOf('\')-1))"}):`t" -nonewline; Write-Host "Path too long. Check manually." -foregroundcolor Yellow 
        $_filesToCheck +$targetFile 
    } elseif (!(Test-Path $targetFile))        { 
        $_hashResult = "Not found" 
        Write-host "$($targetFile.substring(0,$(if ($targetFile.length -gt 30) {30} else {$targetFile.length})))$(if ($targetFile.length -lt 30) {} else {"...$($targetFile.substring($targetFile.lastIndexOf('\')+1,$targetFile.length-$targetFile.lastIndexOf('\')-1))"}):`t" -nonewline; Write-Host "Not found." -foregroundcolor Red 
        Write-Output "$sourceFile,$_hashResult" | Out-File $results -Append 
    } else { 
        # Retrieve the hashes 
        # -------------------- 
        $_sourceHash         = $($(./md5sum $sourceFile-replace "\*.*","").replace('\','') 
        $_targetHash        = $($(./md5sum $targetFile-replace "\*.*","").replace('\','') 
        Write-host "$($targetFile.substring(0,$(if ($targetFile.length -gt 30) {30} else {$targetFile.length})))$(if ($targetFile.length -lt 30) {} else {"...$($targetFile.substring($targetFile.lastIndexOf('\')+1,$targetFile.length-$targetFile.lastIndexOf('\')-1))"}): $_targetHash " -nonewline 
        if ($_sourceHash -match $_targetHash) 
            { write-host "(Source: $_sourceHash)" -foregroundcolor Green; $_hashResult = "TRUE" } 
        else 
            { write-host "(Source: $_sourceHash)" -foregroundcolor Red; $_hashResult = "FALSE" } 
             
        # Write results 
        # -------------- 
        Write-Output "$sourceFile,$_sourceHash,$_targetHash,$_hashResult" | Out-File $results -Append 
    } 
     
    # Progress bar 
    # ------------- 
    Write-Progress -Activity "Hash comparison status" -Status "$i files completed." -percentcomplete $_progStatus 
} 
Write-Progress -Completed -Activity "`0" -Status "`0" 
 
# Done! 
# ------ 
$Output = cat $results 
$FMatches = ([array]($Output | where {$_ -match "TRUE"})).count | %{ if ($_) {$_else {'0'} } 
$NMatches = ([array]($Output | where {$_ -match "FALSE"})).count | %{ if ($_) {$_else {'0'} } 
$NFounds  = ([array]($Output | where {$_ -match "not found"})).count | %{ if ($_) {$_else {'0'} } 
$CheckM      = ([array]($Output | where {$_ -match "Manual check"})).count | %{ if ($_) {$_else {'0'} } 
Write-Host "`n`nScript complete!`n`n`tMatches:`t$FMatches`n`tNon-matches:`t$NMatches`n`tNot found:`t$NFounds`n" 
 
if ($CheckM -gt 0)    { 
    Write-Host "`nSome files had paths that were too long for the script to validate. Please check the following files manually." -ForegroundColor Yellow 
    foreach ($_file in $_filesToCheck)     
        { Write-Host $_file } 
}