Files
zf-sql/RestoreScript.ps1
2025-10-30 11:00:36 +00:00

183 lines
6.7 KiB
PowerShell

param(
[Parameter(Mandatory=$true)]
[string]$FolderPath,
[Parameter(Mandatory=$false)]
[string]$DatabaseName,
[Parameter(Mandatory=$true)]
[ValidateSet("catalog", "restore", "verify")]
[string]$Action,
[Parameter(Mandatory=$false)]
[string]$SqlInstance = "sqlfcsql\TESTINST"
)
# Function to catalog backups
function Catalog-Backups {
param([string]$Path)
$backupFiles = Get-ChildItem -Path $Path -Recurse -File | Where-Object { $_.Extension -eq '.bak' -or $_.Extension -eq '.trn' }
$catalog = @{}
foreach ($file in $backupFiles) {
$baseName = $file.BaseName
$parts = $baseName -split '_'
if ($parts.Length -lt 4) { continue }
$dbName = $parts[1]
$type = $parts[2]
$dateStr = $parts[3]
$timeStr = $parts[4]
$stripe = 0
if ($parts.Length -gt 5) {
$stripe = [int]$parts[5]
}
$key = "$dateStr$timeStr"
if (-not $catalog.ContainsKey($dbName)) { $catalog[$dbName] = @{} }
if (-not $catalog[$dbName].ContainsKey($type)) { $catalog[$dbName][$type] = @{} }
if (-not $catalog[$dbName][$type].ContainsKey($key)) { $catalog[$dbName][$type][$key] = @() }
$catalog[$dbName][$type][$key] += @{File = $file; Stripe = $stripe}
}
return $catalog
}
# Function to report catalog
function Report-Catalog {
param([hashtable]$Catalog)
Write-Host "Database Backups Catalog:"
Write-Host "========================="
foreach ($db in $catalog.Keys | Sort-Object) {
Write-Host "Database: $db"
foreach ($type in $catalog[$db].Keys | Sort-Object) {
Write-Host " Type: $type"
foreach ($key in $catalog[$db][$type].Keys | Sort-Object -Descending) {
Write-Host " Backup: $key"
$files = $catalog[$db][$type][$key] | Sort-Object { $_.Stripe }
foreach ($item in $files) {
Write-Host " $($item.File.FullName)"
}
}
}
Write-Host ""
}
}
# Function to restore database
function Restore-Database {
param([string]$DbName, [hashtable]$Catalog, [string]$Instance)
if (-not $catalog.ContainsKey($DbName)) {
Write-Error "Database $DbName not found in catalog"
return
}
$dbCatalog = $catalog[$DbName]
# Find the latest FULL backup
if (-not $dbCatalog.ContainsKey('FULL')) {
Write-Error "No FULL backup found for database $DbName"
return
}
$latestFullKey = $dbCatalog['FULL'].Keys | Sort-Object -Descending | Select-Object -First 1
$fullFiles = $dbCatalog['FULL'][$latestFullKey] | Sort-Object { $_.Stripe } | ForEach-Object { $_.File }
# Restore FULL with NORECOVERY
$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
# Apply DIFF backups after the FULL
if ($dbCatalog.ContainsKey('DIFF')) {
$diffKeys = $dbCatalog['DIFF'].Keys | Where-Object { $_ -gt $latestFullKey } | Sort-Object
foreach ($key in $diffKeys) {
$diffFiles = $dbCatalog['DIFF'][$key] | Sort-Object { $_.Stripe } | ForEach-Object { $_.File }
$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
}
}
# Apply LOG backups after the FULL
if ($dbCatalog.ContainsKey('LOG')) {
$logKeys = $dbCatalog['LOG'].Keys | Where-Object { $_ -gt $latestFullKey } | Sort-Object
foreach ($key in $logKeys) {
$logFiles = $dbCatalog['LOG'][$key] | Sort-Object { $_.Stripe } | ForEach-Object { $_.File }
$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
}
}
# Final recovery
$restoreQuery = "RESTORE DATABASE [$DbName] WITH RECOVERY"
Write-Host "Finalizing restore for $DbName..."
Invoke-Sqlcmd -ServerInstance $Instance -Query $restoreQuery -QueryTimeout 0
Write-Host "Restore completed for $DbName"
}
# 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"
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..."
try {
Invoke-Sqlcmd -ServerInstance $Instance -Query $verifyQuery -QueryTimeout 0
Write-Host "Verification successful for $type $key"
} catch {
Write-Host "Verification failed for $type $key : $($_.Exception.Message)"
}
}
}
Write-Host ""
}
}
# Main script
if ($Action -eq "catalog") {
$catalog = Catalog-Backups -Path $FolderPath
if ($DatabaseName) {
$filteredCatalog = @{}
if ($catalog.ContainsKey($DatabaseName)) {
$filteredCatalog[$DatabaseName] = $catalog[$DatabaseName]
}
Report-Catalog -Catalog $filteredCatalog
} else {
Report-Catalog -Catalog $catalog
}
} elseif ($Action -eq "restore") {
if (-not $DatabaseName) {
Write-Error "DatabaseName is required for restore action"
exit 1
}
$catalog = Catalog-Backups -Path $FolderPath
Restore-Database -DbName $DatabaseName -Catalog $catalog -Instance $SqlInstance
} elseif ($Action -eq "verify") {
$catalog = Catalog-Backups -Path $FolderPath
if ($DatabaseName) {
$filteredCatalog = @{}
if ($catalog.ContainsKey($DatabaseName)) {
$filteredCatalog[$DatabaseName] = $catalog[$DatabaseName]
}
} else {
$filteredCatalog = $catalog
}
Verify-Backups -Catalog $filteredCatalog -Instance $SqlInstance
}