Files
PowerShell-scripts/Misc/Fix-VSSBackup.ps1
2025-09-15 15:00:10 +02:00

217 lines
7.4 KiB
PowerShell

<#
.SYNOPSIS
Herstart relevante services wanneer een VSS back-up is mislukt.
.DESCRIPTION
- Zoekt in de Event Logs (Application/System) naar recente VSS-fouten.
- (Optioneel) Controleert VSS-writers via 'vssadmin list writers' op Failed.
- Herstart een configureerbare set van VSS-gerelateerde services.
- Logt acties naar bestand en uitvoer; verifieert na afloop de writer-status.
.PARAMETER LookbackMinutes
Periode (minuten) om terug te kijken voor VSS-fouten in Event Logs.
.PARAMETER RequireFailedWriter
Alleen services herstarten als er daadwerkelijk één of meer VSS-writers in Failed staan.
.PARAMETER Force
Stop-Service gebruikt -Force wanneer nodig.
.PARAMETER LogPath
Pad naar logbestand.
.PARAMETER ServiceList
Overschrijf de standaardlijst met te herstarten services (servicenamen, niet weergavenamen).
.NOTES
- Run als Administrator.
- Pas de standaard ServiceList aan voor de betreffende serverrol(len).
#>
[CmdletBinding()]
param(
[int]$LookbackMinutes = 60,
[switch]$RequireFailedWriter,
[switch]$Force,
[string]$LogPath = "C:\Windows\Temp\VSS-Restart-$(Get-Date -Format 'yyyyMMdd_HHmmss').log",
[string[]]$ServiceList = @(
# Kern VSS
'VSS', # Volume Shadow Copy
'SwPrv', # Microsoft Software Shadow Copy Provider
'EventSystem', # COM+ Event System
'COMSysApp', # COM+ System Application (soms niet nodig/aanwezig)
# Veelvoorkomende writers (pas aan op rol)
'SQLWriter', # SQL Server VSS Writer (indien aanwezig)
'IISADMIN', # IIS Admin Service (indien aanwezig)
'W3SVC', # World Wide Web Publishing (indien aanwezig)
'BITS', # Background Intelligent Transfer Service (soms storend bij backups)
'wbengine', # Windows Backup Engine (indien gebruikt)
# Hyper-V (host of guest; alleen herstarten als aanwezig)
'vmicvss', # Hyper-V Volume Shadow Copy Requestor (Guest service)
'vmms' # Hyper-V Virtual Machine Management (Host)
)
)
# ---------- Helpers ----------
function Write-Log {
param([string]$Message)
$stamp = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
$line = "[$stamp] $Message"
Write-Output $line
try { Add-Content -Path $LogPath -Value $line -Encoding UTF8 -ErrorAction Stop } catch {}
}
function Test-VssErrors {
param([int]$Minutes)
$since = (Get-Date).AddMinutes(-$Minutes)
$filters = @(
@{ LogName='Application'; ProviderName='VSS' },
@{ LogName='System'; ProviderName='VolSnap' }, # Volsnap driver errors
@{ LogName='Application'; ProviderName='Backup'; }, # Sommige backupapps loggen onder 'Backup'
@{ LogName='Application'; ProviderName='Windows Server Backup' } # WSB
)
$events = foreach ($f in $filters) {
try {
Get-WinEvent -FilterHashtable @{
LogName = $f.LogName
ProviderName = $f.ProviderName
Level = 2 # Error
StartTime = $since
} -ErrorAction SilentlyContinue
} catch {}
}
$hasErrors = $false
if ($events) {
$hasErrors = $true
Write-Log "VSS/VOLSNAP/Backup fouten gevonden sinds ${since}:"
$events | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
ForEach-Object {
Write-Log (" {0:u} {1} {2}: {3}" -f $_.TimeCreated, $_.ProviderName, $_.Id, ($_.Message -replace '\r?\n',' '))
}
} else {
Write-Log "Geen VSS-gerelateerde fouten gevonden sinds $since."
}
return $hasErrors
}
function Get-VssWriterStatus {
# Parse 'vssadmin list writers' op Failed writers
$failed = @()
$all = @()
$output = & vssadmin list writers 2>$null
if (-not $output) {
Write-Log "Kon 'vssadmin list writers' niet uitvoeren of geen output."
return ,@(),@()
}
$current = [ordered]@{ Writer=''; State=''; LastError='' }
foreach ($line in $output) {
if ($line -match "^\s*Writer name:\s+'(.+?)'") {
if ($current.Writer) { $all += [pscustomobject]$current }
$current = [ordered]@{ Writer=$matches[1]; State=''; LastError='' }
} elseif ($line -match '^\s*State:\s+\[(\d+)\]\s+(.+)$') {
$current.State = "$($matches[1]) $($matches[2])"
} elseif ($line -match '^\s*Last error:\s+(.+)$') {
$current.LastError = $matches[1]
}
}
if ($current.Writer) { $all += [pscustomobject]$current }
foreach ($w in $all) {
if ($w.State -match '^\s*9\b|Failed') {
$failed += $w
}
}
return ,$failed,$all
}
function Restart-ServiceSafe {
param(
[string]$Name,
[switch]$ForceStop
)
try {
$svc = Get-Service -Name $Name -ErrorAction Stop
} catch {
Write-Log "Service '$Name' niet gevonden; overslaan."
return
}
# Sommige services zijn al gestopt of disabled
if ($svc.Status -eq 'Running') {
Write-Log "Stoppen service '$Name'..."
try {
if ($ForceStop) {
Stop-Service -Name $Name -Force -ErrorAction Stop
} else {
Stop-Service -Name $Name -ErrorAction Stop
}
$svc.WaitForStatus('Stopped','00:00:20')
} catch {
Write-Log "Kon service '$Name' niet stoppen: $($_.Exception.Message)"
}
} else {
Write-Log "Service '$Name' is niet Running (status=$($svc.Status)); stop stap overslaan."
}
Write-Log "Starten service '$Name'..."
try {
Start-Service -Name $Name -ErrorAction Stop
(Get-Service -Name $Name).WaitForStatus('Running','00:00:20')
Write-Log "Service '$Name' draait."
} catch {
Write-Log "Kon service '$Name' niet starten: $($_.Exception.Message)"
}
}
# ---------- Main ----------
Write-Log "=== VSS Service Restart Script gestart op $(hostname) ==="
Write-Log "Logbestand: $LogPath"
$hasVssErrors = Test-VssErrors -Minutes $LookbackMinutes
$failedWriters = @()
$allWriters = @()
$failedWriters, $allWriters = Get-VssWriterStatus
if ($failedWriters.Count -gt 0) {
Write-Log "VSS-writers in FAILED staat:"
$failedWriters | ForEach-Object { Write-Log (" {0} | State={1} | LastError={2}" -f $_.Writer, $_.State, $_.LastError) }
} else {
Write-Log "Geen FAILED VSS-writers gedetecteerd."
}
$shouldAct = $hasVssErrors -or ($failedWriters.Count -gt 0)
if ($RequireFailedWriter -and $failedWriters.Count -eq 0) {
$shouldAct = $false
Write-Log "RequireFailedWriter is ingeschakeld en er zijn geen failed writers; geen services herstart."
}
if ($shouldAct) {
Write-Log "Services worden herstart. Aantal in lijst: $($ServiceList.Count)."
foreach ($svcName in $ServiceList) {
Restart-ServiceSafe -Name $svcName -ForceStop:$Force.IsPresent
}
Start-Sleep -Seconds 5
Write-Log "Hercontrole VSS-writers na herstart..."
$failedAfter, $allAfter = Get-VssWriterStatus
if ($failedAfter.Count -eq 0) {
Write-Log "Alle VSS-writers zijn nu OK."
exit 0
} else {
Write-Log "Nog steeds FAILED VSS-writers na herstart:"
$failedAfter | ForEach-Object { Write-Log (" {0} | State={1} | LastError={2}" -f $_.Writer, $_.State, $_.LastError) }
exit 2
}
} else {
Write-Log "Geen actie ondernomen; geen relevante fouten binnen de lookback of policy verhindert actie."
exit 0
}