internal/Test-DbaLsnChain.ps1

function Test-DbaLsnChain
{
<#
.SYNOPSIS
Checks that a filtered array from Get-FilteredRestore contains a restorabel chain of LSNs
 
.DESCRIPTION
     
.PARAMETER FilterdRestoreFiles
     
.PARAMETER RestoreTime
Returns fewer columns for an easy overview
     
 
.NOTES
dbatools PowerShell module (https://dbatools.io, clemaire@gmail.com)
Copyright (C) 2016 Chrissy LeMaire
 
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
 
.EXAMPLE
Test-DbaLsnChain -FilteredRestoreFiles $FilteredFiles
 
Checks that the Restore chain in $FilteredFiles is complete and can be fully restored
 
.EXAMPLE
Test-DbaLsnChain -FilteredRestoreFiles $FilteredFiles -RestoreTime '23/12/2016 06:55'
 
Checks that the Restore chain in $FilteredFiles is complete and can be fully restored to the Point In Time '23/12/2016 06:55'
     
#>

    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true)]
        [object[]]$FilteredRestoreFiles,
        [DateTime]$RestoreTime = (Get-Date).addyears(1)
    )

    #Need to anchor with full backup:
    $FunctionName = "Test-DbaLsnChain"
    $FullDBAnchor = $FilteredRestoreFiles | Where-Object {$_.BackupTypeDescription -eq 'Database'}
    if (($FullDBAnchor | Measure-Object).count -ne 1)
    {
        Write-Error "$FunctionName - More than 1 full backup, or less than 1, neither supported"
        return $false
        break;
    }
    #Check all the backups relate to the full backup
    #Via RecoveryForkID:
    if (($FilteredRestoreFiles | Where-Object {$_.RecoveryForkID -ne $FullDBAnchor.RecoveryForkID}).count -gt 0)
    {
        Write-Error "$FunctionName - Multiple RecoveryForkIDs found, not supported"
        return $false
        break
    }
    #Via LSN chain:
    $BackupWrongLSN = $FilteredRestoreFiles | Where-Object {$_.DatabaseBackupLSN -ne $FullDBAnchor.CheckPointLSN}
    #Should be 0 in there, if not, lets check that they're from during the full backup
    if ($BackupWrongLSN.count -gt 0 ) 
    {
        if (($BackupWrongLSN | Where-Object {$_.LastLSN -lt $FullDBAnchor.LastSN}).count -gt 0)
        {
            Write-Error "$FunctionName - We have non matching LSNs - not supported"
            return $false
            break;
        }
    }
    $DiffAnchor = $FilteredRestoreFiles | Where-Object {$_.BackupTypeDescription -eq 'Database Differential'}
    #Check for no more than a single Differential backup
    if (($DiffAnchor | Measure-Object).count -gt 1)
    {
        Write-Error "$FunctionName - More than 1 differential backup, not supported"
        return $false
        break;        
    } 
    elseif (($DiffAnchor | Measure-Object).count -gt 1)
    {
        $TlogAnchor = $DiffAnchor
    } 
    else 
    {
        $TlogAnchor = $FullDBAnchor
    }


    #Check T-log LSNs form a chain.
    $TranLogBackups = $FilteredRestoreFiles | Where-Object {$_.BackupTypeDescription -eq 'Transaction Log' -and $_.DatabaseBackupLSN -eq $FullDBAnchor.CheckPointLSN} | Sort-Object -Propert LastLSN
    for ($i=0; $i -lt ($TranLogBackups.count)-1)
    {
        if ($i -eq 0)
        {
            if ($TranLogBackups[$i].FirstLSN -gt $TlogAnchor.LastLSN)
            {
                Write-Error "$FunctionName - Break in LSN Chain between $($TlogAnchor.BackupPath) and $($TranLogBackups[($i)].BackupPath) "
                return $false
                break
            }
        }else {
            if ($TranLogBackups[($i-1)].LastLsn -ne $TranLogBackups[($i)].FirstLSN)
            {
                Write-Error "$FunctionName - Break in transaction log between $($TranLogBackups[($i-1)].BackupPath) and $($TranLogBackups[($i)].BackupPath) "
                return $false
                break
            }
        }
        $i++

    }    
    return $true
}