diff --git a/RestoreScript.ps1 b/RestoreScript.ps1 index 8dbc526..d615d8c 100644 --- a/RestoreScript.ps1 +++ b/RestoreScript.ps1 @@ -108,7 +108,7 @@ function Restore-Database { $fileList = $fullFiles | ForEach-Object { "DISK = '$($_.FullName)'" } $restoreQuery = "RESTORE DATABASE [$DbName] FROM $($fileList -join ', ') WITH NORECOVERY" Write-Host "Restoring FULL backup for $DbName..." - Invoke-Sqlcmd -ServerInstance $Instance -Query $restoreQuery -QueryTimeout 0 + Invoke-Sqlcmd -ServerInstance $Instance -Query $restoreQuery -QueryTimeout 0 -ErrorAction Stop # Apply DIFF backups after the FULL if ($dbCatalog.ContainsKey('DIFF')) { @@ -118,7 +118,7 @@ function Restore-Database { $fileList = $diffFiles | ForEach-Object { "DISK = '$($_.FullName)'" } $restoreQuery = "RESTORE DATABASE [$DbName] FROM $($fileList -join ', ') WITH NORECOVERY" Write-Host "Applying DIFF backup $key for $DbName..." - Invoke-Sqlcmd -ServerInstance $Instance -Query $restoreQuery -QueryTimeout 0 + Invoke-Sqlcmd -ServerInstance $Instance -Query $restoreQuery -QueryTimeout 0 -ErrorAction Stop } } @@ -130,54 +130,126 @@ function Restore-Database { $fileList = $logFiles | ForEach-Object { "DISK = '$($_.FullName)'" } $restoreQuery = "RESTORE LOG [$DbName] FROM $($fileList -join ', ') WITH NORECOVERY" Write-Host "Applying LOG backup $key for $DbName..." - Invoke-Sqlcmd -ServerInstance $Instance -Query $restoreQuery -QueryTimeout 0 + Invoke-Sqlcmd -ServerInstance $Instance -Query $restoreQuery -QueryTimeout 0 -ErrorAction Stop } } # Final recovery $restoreQuery = "RESTORE DATABASE [$DbName] WITH RECOVERY" Write-Host "Finalizing restore for $DbName..." - Invoke-Sqlcmd -ServerInstance $Instance -Query $restoreQuery -QueryTimeout 0 + Invoke-Sqlcmd -ServerInstance $Instance -Query $restoreQuery -QueryTimeout 0 -ErrorAction Stop Write-Host "Restore completed for $DbName" } +# Function to print backup summary +function Print-BackupSummary { + param([string]$DbName, [hashtable]$Headers) + + Write-Host "Backup Summary for database: $DbName" + Write-Host "===================================" + + # FULL backups + if ($Headers.ContainsKey('FULL')) { + Write-Host "FULL Backups:" + foreach ($item in $Headers['FULL'] | Sort-Object { $_.Key }) { + $header = $item.Header + Write-Host " Date: $($header.BackupFinishDate) | LSN Range: $($header.FirstLSN) - $($header.LastLSN)" + } + } + + # DIFF backups + if ($Headers.ContainsKey('DIFF')) { + Write-Host "DIFFERENTIAL Backups:" + foreach ($item in $Headers['DIFF'] | Sort-Object { $_.Key }) { + $header = $item.Header + Write-Host " Date: $($header.BackupFinishDate) | Base LSN: $($header.DifferentialBaseLSN) | LSN Range: $($header.FirstLSN) - $($header.LastLSN)" + } + } + + # LOG backups + if ($Headers.ContainsKey('LOG')) { + $logItems = $Headers['LOG'] | Sort-Object { $_.Key } + if ($logItems.Count -gt 0) { + $firstLog = $logItems[0].Header + $lastLog = $logItems[-1].Header + Write-Host "LOG Backups:" + Write-Host " Point-in-Time Range: $($firstLog.BackupStartDate) to $($lastLog.BackupFinishDate)" + Write-Host " LSN Range: $($firstLog.FirstLSN) - $($lastLog.LastLSN)" + + # Check for gaps + $gaps = @() + for ($i = 1; $i -lt $logItems.Count; $i++) { + $prevLast = $logItems[$i-1].Header.LastLSN + $currFirst = $logItems[$i].Header.FirstLSN + if ($prevLast -ne $currFirst) { + $gaps += "Gap between $($logItems[$i-1].Key) (LSN $($prevLast)) and $($logItems[$i].Key) (LSN $($currFirst))" + } + } + if ($gaps.Count -gt 0) { + Write-Host " *** MISSING RANGES ***" + foreach ($gap in $gaps) { + Write-Host " $gap" + } + } else { + Write-Host " No gaps detected in LOG sequence" + } + } + } + + if (-not ($Headers.ContainsKey('FULL') -or $Headers.ContainsKey('DIFF') -or $Headers.ContainsKey('LOG'))) { + Write-Host "No backup headers retrieved" + } +} + # Function to verify backups function Verify-Backups { param([hashtable]$Catalog, [string]$Instance) foreach ($db in $catalog.Keys | Sort-Object) { Write-Host "Verifying backups for database: $db" + $headers = @{} foreach ($type in $catalog[$db].Keys | Sort-Object) { foreach ($key in $catalog[$db][$type].Keys | Sort-Object) { $files = $catalog[$db][$type][$key] | Sort-Object { $_.Stripe } | ForEach-Object { $_.File } $fileList = $files | ForEach-Object { "DISK = '$($_.FullName)'" } $verifyQuery = "RESTORE VERIFYONLY FROM $($fileList -join ', ')" Write-Host "Verifying $type backup $key for $db..." + $verified = $false try { - Invoke-Sqlcmd -ServerInstance $Instance -Query $verifyQuery -QueryTimeout 0 + Invoke-Sqlcmd -ServerInstance $Instance -Query $verifyQuery -QueryTimeout 0 -ErrorAction Stop Write-Host "Verification successful for $type $key" + $verified = $true } catch { Write-Host "Verification failed for $type $key : $($_.Exception.Message)" } - # Get backup header information - $header = Get-BackupInfo -Files $files -Instance $Instance - if ($header) { - Write-Host " Backup Details:" - Write-Host " Start Date: $($header.BackupStartDate)" - Write-Host " Finish Date: $($header.BackupFinishDate)" - Write-Host " First LSN: $($header.FirstLSN)" - Write-Host " Last LSN: $($header.LastLSN)" - if ($header.DatabaseBackupLSN) { - Write-Host " Database Backup LSN: $($header.DatabaseBackupLSN)" - } - if ($header.DifferentialBaseLSN) { - Write-Host " Differential Base LSN: $($header.DifferentialBaseLSN)" + # Get backup header information only if verified + if ($verified) { + $header = Get-BackupInfo -Files $files -Instance $Instance + if ($header) { + Write-Host " Backup Details:" + Write-Host " Start Date: $($header.BackupStartDate)" + Write-Host " Finish Date: $($header.BackupFinishDate)" + Write-Host " First LSN: $($header.FirstLSN)" + Write-Host " Last LSN: $($header.LastLSN)" + if ($header.DatabaseBackupLSN) { + Write-Host " Database Backup LSN: $($header.DatabaseBackupLSN)" + } + if ($header.DifferentialBaseLSN) { + Write-Host " Differential Base LSN: $($header.DifferentialBaseLSN)" + } + + # Collect headers for summary + if (-not $headers.ContainsKey($type)) { $headers[$type] = @() } + $headers[$type] += @{ Key = $key; Header = $header } } } } } + + # Print summary + Print-BackupSummary -DbName $db -Headers $headers Write-Host "" } } @@ -190,7 +262,7 @@ function Get-BackupInfo { $headerQuery = "RESTORE HEADERONLY FROM $($fileList -join ', ')" try { - $header = Invoke-Sqlcmd -ServerInstance $Instance -Query $headerQuery -QueryTimeout 0 + $header = Invoke-Sqlcmd -ServerInstance $Instance -Query $headerQuery -QueryTimeout 0 -ErrorAction Stop return $header } catch { Write-Warning "Failed to get header for $($Files[0].Name): $($_.Exception.Message)"