178 lines
6.7 KiB
PowerShell
178 lines
6.7 KiB
PowerShell
<#
|
|
LXS - System Restore Point (Windows)
|
|
Description: Enable System Protection, create checkpoints, list them and
|
|
roll back. The safety net for harden.ps1 and debloat.ps1.
|
|
Repo: https://git.hyko.cx/hykocx/lxs
|
|
#>
|
|
|
|
# Load LXS common library (colors, UI helpers, spinner, loggers, guards).
|
|
# Prefers the sibling ..\lib\common.ps1 (repo checkout or installed layout) and
|
|
# only hits the network when this script is run standalone.
|
|
$LxsRawBase = if ($env:LXS_RAW_BASE) { $env:LXS_RAW_BASE } else { 'https://git.hyko.cx/hykocx/lxs/raw/branch/main' }
|
|
if (-not $env:LXS_RAW_PLATFORM_BASE) { $env:LXS_RAW_PLATFORM_BASE = "$LxsRawBase/windows" }
|
|
$LxsLibPath = if ($PSScriptRoot) { Join-Path $PSScriptRoot '..\lib\common.ps1' } else { $null }
|
|
if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
|
|
. $LxsLibPath
|
|
} else {
|
|
try {
|
|
$LxsLibSource = Invoke-RestMethod -Uri "$env:LXS_RAW_PLATFORM_BASE/lib/common.ps1" -UseBasicParsing -ErrorAction Stop
|
|
} catch {
|
|
Write-Error 'Failed to fetch lib/common.ps1'
|
|
exit 1
|
|
}
|
|
. ([scriptblock]::Create($LxsLibSource))
|
|
}
|
|
|
|
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_restore_point.log'
|
|
|
|
if (-not (Assert-LxsWindows)) { exit 1 }
|
|
Assert-LxsAdmin -ScriptPath $PSCommandPath -Arguments $args
|
|
|
|
function Get-LxsRestorePoints {
|
|
try {
|
|
return @(Get-ComputerRestorePoint -ErrorAction Stop)
|
|
} catch {
|
|
return @()
|
|
}
|
|
}
|
|
|
|
function Show-LxsRestorePoints {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'RESTORE POINTS'
|
|
Write-Host ''
|
|
$points = Get-LxsRestorePoints
|
|
if ($points.Count -eq 0) {
|
|
Write-LxsWarn 'No restore points found (System Protection may be disabled).'
|
|
return
|
|
}
|
|
$points | Sort-Object SequenceNumber -Descending |
|
|
Format-Table -AutoSize `
|
|
@{ Name = 'Seq'; Expression = { $_.SequenceNumber } },
|
|
@{ Name = 'Created'; Expression = { $_.ConvertToDateTime($_.CreationTime).ToString('yyyy-MM-dd HH:mm') } },
|
|
@{ Name = 'Type'; Expression = { $_.RestorePointType } },
|
|
@{ Name = 'Description'; Expression = { $_.Description } } |
|
|
Out-String -Width 160 | Write-Host
|
|
}
|
|
|
|
function Show-LxsProtectionStatus {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'SYSTEM PROTECTION'
|
|
Write-Host ''
|
|
$drive = "$env:SystemDrive\"
|
|
try {
|
|
# Volumes listed here have shadow storage configured, i.e. protection on.
|
|
$shadow = @(Get-CimInstance Win32_ShadowStorage -ErrorAction Stop)
|
|
if ($shadow.Count -gt 0) {
|
|
Write-LxsOk "System Protection appears to be ON for $($shadow.Count) volume(s)"
|
|
foreach ($s in $shadow) {
|
|
$maxGB = [math]::Round($s.MaxSpace / 1GB, 1)
|
|
$usedGB = [math]::Round($s.UsedSpace / 1GB, 2)
|
|
Write-Host " Shadow storage: $usedGB GB used of $maxGB GB allocated"
|
|
}
|
|
} else {
|
|
Write-LxsWarn "System Protection looks OFF for $drive — no restore points can be created."
|
|
}
|
|
} catch {
|
|
Write-LxsWarn 'Could not read the shadow storage configuration.'
|
|
}
|
|
Write-Host ''
|
|
Write-Host "$($script:Gray)Restore points found: $((Get-LxsRestorePoints).Count)$($script:NC)"
|
|
Write-Host ''
|
|
}
|
|
|
|
function Enable-LxsProtection {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'ENABLE SYSTEM PROTECTION'
|
|
Write-Host ''
|
|
$drive = "$env:SystemDrive\"
|
|
try {
|
|
Enable-ComputerRestore -Drive $drive -ErrorAction Stop
|
|
Write-LxsOk "System Protection enabled on $drive"
|
|
} catch {
|
|
Write-LxsErr "Failed to enable System Protection: $($_.Exception.Message)"
|
|
return
|
|
}
|
|
# Default shadow storage can be as low as 1%; 5% leaves room for a few points.
|
|
try {
|
|
& vssadmin.exe Resize ShadowStorage /For=$drive /On=$drive /MaxSize=5% | Out-String | Write-Host
|
|
} catch {
|
|
Write-LxsWarn 'Could not resize the shadow storage allocation.'
|
|
}
|
|
}
|
|
|
|
function New-LxsCheckpoint {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'CREATE RESTORE POINT'
|
|
Write-Host ''
|
|
$desc = Read-Host -Prompt 'Description (default: LXS manual checkpoint)'
|
|
if ([string]::IsNullOrWhiteSpace($desc)) { $desc = 'LXS manual checkpoint' }
|
|
Write-Host ''
|
|
Write-LxsInfo 'Creating the restore point (this can take a minute)...'
|
|
New-LxsRestorePoint -Description $desc | Out-Null
|
|
}
|
|
|
|
function Restore-LxsCheckpoint {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'ROLL BACK' -Right 'REBOOTS'
|
|
Write-Host ''
|
|
$points = Get-LxsRestorePoints
|
|
if ($points.Count -eq 0) {
|
|
Write-LxsWarn 'No restore points to roll back to.'
|
|
return
|
|
}
|
|
Show-LxsRestorePoints
|
|
Write-Host "$($script:Red)Restoring reboots the machine immediately and reverts system files,$($script:NC)"
|
|
Write-Host "$($script:Red)drivers, registry and installed programs to that point in time.$($script:NC)"
|
|
Write-Host "$($script:Gray)Your documents are not touched.$($script:NC)"
|
|
Write-Host ''
|
|
$seq = Read-Host -Prompt 'Sequence number to restore (empty to cancel)'
|
|
if ([string]::IsNullOrWhiteSpace($seq)) { Write-LxsInfo 'Cancelled.'; return }
|
|
if ($seq -notmatch '^\d+$') { Write-LxsErr 'Sequence number must be numeric.'; return }
|
|
if (-not ($points | Where-Object { $_.SequenceNumber -eq [int]$seq })) {
|
|
Write-LxsErr "No restore point with sequence number $seq."
|
|
return
|
|
}
|
|
if (-not (Confirm-LxsAction -Question "Restore to point $seq and reboot NOW?")) {
|
|
Write-LxsInfo 'Cancelled.'
|
|
return
|
|
}
|
|
try {
|
|
Restore-Computer -RestorePoint ([int]$seq) -Confirm:$false -ErrorAction Stop
|
|
} catch {
|
|
Write-LxsErr "Restore failed: $($_.Exception.Message)"
|
|
Write-Host "$($script:Gray) You can also use: rstrui.exe$($script:NC)"
|
|
}
|
|
}
|
|
|
|
function Show-LxsRestoreMenu {
|
|
while ($true) {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'RESTORE POINT' -Right 'ADMIN'
|
|
Write-Host ''
|
|
Show-LxsMenuItem '1' 'Show protection status'
|
|
Show-LxsMenuItem '2' 'Enable System Protection'
|
|
Show-LxsMenuItem '3' 'Create a restore point'
|
|
Show-LxsMenuItem '4' 'List restore points'
|
|
Show-LxsMenuItem '5' 'Roll back to a restore point' 'reboots the machine'
|
|
Show-LxsMenuItem '0' 'Back' '' -Exit
|
|
Write-Host ''
|
|
Show-LxsBoxBottom
|
|
Write-Host ''
|
|
$choice = Read-LxsChoice
|
|
Write-Host ''
|
|
|
|
switch ($choice) {
|
|
'1' { Show-LxsProtectionStatus; Read-LxsEnter }
|
|
'2' { Enable-LxsProtection; Read-LxsEnter }
|
|
'3' { New-LxsCheckpoint; Read-LxsEnter }
|
|
'4' { Show-LxsRestorePoints; Read-LxsEnter }
|
|
'5' { Restore-LxsCheckpoint; Read-LxsEnter }
|
|
'0' { return }
|
|
default { Write-LxsErr 'Invalid protocol. Select 0-5.'; Start-Sleep -Seconds 1 }
|
|
}
|
|
}
|
|
}
|
|
|
|
Show-LxsRestoreMenu
|
|
exit 75
|