Verification improvements
This commit is contained in:
@@ -108,7 +108,7 @@ function Restore-Database {
|
|||||||
$fileList = $fullFiles | ForEach-Object { "DISK = '$($_.FullName)'" }
|
$fileList = $fullFiles | ForEach-Object { "DISK = '$($_.FullName)'" }
|
||||||
$restoreQuery = "RESTORE DATABASE [$DbName] FROM $($fileList -join ', ') WITH NORECOVERY"
|
$restoreQuery = "RESTORE DATABASE [$DbName] FROM $($fileList -join ', ') WITH NORECOVERY"
|
||||||
Write-Host "Restoring FULL backup for $DbName..."
|
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
|
# Apply DIFF backups after the FULL
|
||||||
if ($dbCatalog.ContainsKey('DIFF')) {
|
if ($dbCatalog.ContainsKey('DIFF')) {
|
||||||
@@ -118,7 +118,7 @@ function Restore-Database {
|
|||||||
$fileList = $diffFiles | ForEach-Object { "DISK = '$($_.FullName)'" }
|
$fileList = $diffFiles | ForEach-Object { "DISK = '$($_.FullName)'" }
|
||||||
$restoreQuery = "RESTORE DATABASE [$DbName] FROM $($fileList -join ', ') WITH NORECOVERY"
|
$restoreQuery = "RESTORE DATABASE [$DbName] FROM $($fileList -join ', ') WITH NORECOVERY"
|
||||||
Write-Host "Applying DIFF backup $key for $DbName..."
|
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)'" }
|
$fileList = $logFiles | ForEach-Object { "DISK = '$($_.FullName)'" }
|
||||||
$restoreQuery = "RESTORE LOG [$DbName] FROM $($fileList -join ', ') WITH NORECOVERY"
|
$restoreQuery = "RESTORE LOG [$DbName] FROM $($fileList -join ', ') WITH NORECOVERY"
|
||||||
Write-Host "Applying LOG backup $key for $DbName..."
|
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
|
# Final recovery
|
||||||
$restoreQuery = "RESTORE DATABASE [$DbName] WITH RECOVERY"
|
$restoreQuery = "RESTORE DATABASE [$DbName] WITH RECOVERY"
|
||||||
Write-Host "Finalizing restore for $DbName..."
|
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"
|
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 to verify backups
|
||||||
function Verify-Backups {
|
function Verify-Backups {
|
||||||
param([hashtable]$Catalog, [string]$Instance)
|
param([hashtable]$Catalog, [string]$Instance)
|
||||||
|
|
||||||
foreach ($db in $catalog.Keys | Sort-Object) {
|
foreach ($db in $catalog.Keys | Sort-Object) {
|
||||||
Write-Host "Verifying backups for database: $db"
|
Write-Host "Verifying backups for database: $db"
|
||||||
|
$headers = @{}
|
||||||
foreach ($type in $catalog[$db].Keys | Sort-Object) {
|
foreach ($type in $catalog[$db].Keys | Sort-Object) {
|
||||||
foreach ($key in $catalog[$db][$type].Keys | Sort-Object) {
|
foreach ($key in $catalog[$db][$type].Keys | Sort-Object) {
|
||||||
$files = $catalog[$db][$type][$key] | Sort-Object { $_.Stripe } | ForEach-Object { $_.File }
|
$files = $catalog[$db][$type][$key] | Sort-Object { $_.Stripe } | ForEach-Object { $_.File }
|
||||||
$fileList = $files | ForEach-Object { "DISK = '$($_.FullName)'" }
|
$fileList = $files | ForEach-Object { "DISK = '$($_.FullName)'" }
|
||||||
$verifyQuery = "RESTORE VERIFYONLY FROM $($fileList -join ', ')"
|
$verifyQuery = "RESTORE VERIFYONLY FROM $($fileList -join ', ')"
|
||||||
Write-Host "Verifying $type backup $key for $db..."
|
Write-Host "Verifying $type backup $key for $db..."
|
||||||
|
$verified = $false
|
||||||
try {
|
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"
|
Write-Host "Verification successful for $type $key"
|
||||||
|
$verified = $true
|
||||||
} catch {
|
} catch {
|
||||||
Write-Host "Verification failed for $type $key : $($_.Exception.Message)"
|
Write-Host "Verification failed for $type $key : $($_.Exception.Message)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get backup header information
|
# Get backup header information only if verified
|
||||||
$header = Get-BackupInfo -Files $files -Instance $Instance
|
if ($verified) {
|
||||||
if ($header) {
|
$header = Get-BackupInfo -Files $files -Instance $Instance
|
||||||
Write-Host " Backup Details:"
|
if ($header) {
|
||||||
Write-Host " Start Date: $($header.BackupStartDate)"
|
Write-Host " Backup Details:"
|
||||||
Write-Host " Finish Date: $($header.BackupFinishDate)"
|
Write-Host " Start Date: $($header.BackupStartDate)"
|
||||||
Write-Host " First LSN: $($header.FirstLSN)"
|
Write-Host " Finish Date: $($header.BackupFinishDate)"
|
||||||
Write-Host " Last LSN: $($header.LastLSN)"
|
Write-Host " First LSN: $($header.FirstLSN)"
|
||||||
if ($header.DatabaseBackupLSN) {
|
Write-Host " Last LSN: $($header.LastLSN)"
|
||||||
Write-Host " Database Backup LSN: $($header.DatabaseBackupLSN)"
|
if ($header.DatabaseBackupLSN) {
|
||||||
}
|
Write-Host " Database Backup LSN: $($header.DatabaseBackupLSN)"
|
||||||
if ($header.DifferentialBaseLSN) {
|
}
|
||||||
Write-Host " Differential Base LSN: $($header.DifferentialBaseLSN)"
|
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 ""
|
Write-Host ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,7 +262,7 @@ function Get-BackupInfo {
|
|||||||
$headerQuery = "RESTORE HEADERONLY FROM $($fileList -join ', ')"
|
$headerQuery = "RESTORE HEADERONLY FROM $($fileList -join ', ')"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$header = Invoke-Sqlcmd -ServerInstance $Instance -Query $headerQuery -QueryTimeout 0
|
$header = Invoke-Sqlcmd -ServerInstance $Instance -Query $headerQuery -QueryTimeout 0 -ErrorAction Stop
|
||||||
return $header
|
return $header
|
||||||
} catch {
|
} catch {
|
||||||
Write-Warning "Failed to get header for $($Files[0].Name): $($_.Exception.Message)"
|
Write-Warning "Failed to get header for $($Files[0].Name): $($_.Exception.Message)"
|
||||||
|
|||||||
Reference in New Issue
Block a user