diff --git a/src/windows/win-LKGC.ps1 b/src/windows/win-LKGC.ps1 index f62f7a6..762fddf 100644 --- a/src/windows/win-LKGC.ps1 +++ b/src/windows/win-LKGC.ps1 @@ -1,89 +1,94 @@ -# Welcome to the AUTO LKGC(Last Known Good Configuration) by Daniel Muñoz L! -# Contact me Daniel Muñoz L : damunozl@microsoft.com if questions. -# .SUMMARY -# Last Known Good Configuration enabler offline from rescue VM. -# Increment Last Known Good Configuration registry values by 1. -# Public docs: https://learn.microsoft.com/en-us/troubleshoot/azure/virtual-machines/windows/start-vm-last-known-good, -# https://support.microsoft.com/en-us/topic/you-receive-error-stop-error-code-0x0000007b-inaccessible-boot-device-after-you-install-windows-updates-7cc844e4-4daf-a71c-cd23-f99b50d53e31 -# -# .RESOLVES -# If Windows is not booting correctly due to recently installed software or related changes, modifying the LKGC values -# can revert the changes to attempt a successful boot. -# If you've recently installed new software or changed some Windows settings, and your Azure Windows virtual machine (VM) stops booting correctly, -# you might have to start the VM by using the Last Known Good Configuration for troubleshooting. +<# +.SYNOPSIS + Enables Last Known Good Configuration (LKGC) and logs registry state changes. + Increment Last Known Good Configuration registry values by 1. + Public docs: https://learn.microsoft.com/en-us/troubleshoot/azure/virtual-machines/windows/start-vm-last-known-good, # https://support.microsoft.com/en-us/topic/you-receive-error-stop-error-code-0x0000007b-inaccessible-boot-device-after-you-install-windows-updates-7cc844e4-4daf-a71c-cd23-f99b50d53e31 + + .RESOLVES + If Windows is not booting correctly due to recently installed software or related changes, modifying the LKGC values + can revert the changes to attempt a successful boot. + If you've recently installed new software or changed some Windows settings, and your Azure Windows virtual machine (VM) stops booting correctly, + you might have to start the VM by using the Last Known Good Configuration for troubleshooting. + Created by Tony.Mocanu@Microsoft.com +#> -out-null -cmd /c color 0A -$host.UI.RawUI.WindowTitle = " --== AUTO LKGC by Daniel Muñoz L ==--" +# 1. Import common logic +. .\src\windows\common\setup\init.ps1 +. .\src\windows\common\helpers\Get-Disk-Partitions.ps1 -# Rescue OS variable -$diska='c' +$logFile = "$env:SystemDrive\Repair-LKGC.log" -# Finder for faulty OS letter -$diskarray = "d","q","w","e","r","t","y","u","i","o","p","s","f","g","h","j","k","l","z","x","v","n","m" -$diskb="000" -foreach ($diskt in $diskarray) -{ - if (Test-Path -Path "$($diskt):\Windows") - { - $diskb=$diskt - } -} +try { + Log-Info "Starting AUTO LKGC Script..." | Tee-Object -FilePath $logFile -Append -# IN CASE OF FINDER FAILURE -if ($diskb -eq "000") {write-output "SCRIPT COULD NOT FIND A RESCUE OS DISK ATTACHED, EXITING";start-sleep 10;Exit} + # 2. Finder for faulty OS letter + $diskb = "000" + $diskarray = "d","q","w","e","r","t","y","u","i","o","p","s","f","g","h","j","k","l","z","x","v","n","m" + foreach ($diskt in $diskarray) { + if (Test-Path -Path "$($diskt):\Windows") { $diskb = $diskt; break } + } -# OS VER PEEK -reg.exe load "HKLM\BROKENSYSTEM" "$($diskb):\Windows\System32\config\software" -Start-sleep 3 -(Get-ItemProperty -path 'registry::hklm\BROKENSYSTEM\microsoft\windows nt\currentversion').ProductName | %{ [int]$winosver=$_.Split(' ')[1]; } -(Get-ItemProperty -path 'registry::hklm\BROKENSYSTEM\microsoft\windows nt\currentversion').ProductName | %{ [int]$winosver=$_.Split(' ')[2]; } -reg.exe unload "HKLM\BROKENSYSTEM" -Start-sleep 3 + if ($diskb -eq "000") { + Log-Error "SCRIPT COULD NOT FIND A RESCUE OS DISK ATTACHED" | Tee-Object -FilePath $logFile -Append + return $STATUS_ERROR + } -# Hive loader into rescue VM -reg.exe load "HKU\BROKENSYSTEM" "$($diskb):\Windows\System32\config\SYSTEM" -Start-sleep 3 + # 3. OS Version Peek + reg.exe load "HKLM\BROKENSYSTEM" "$($diskb):\Windows\System32\config\software" + Start-Sleep -Seconds 2 + $productName = (Get-ItemProperty -path 'registry::hklm\BROKENSYSTEM\microsoft\windows nt\currentversion').ProductName + $winosver = 0 + if ($productName -match '(\d+)') { $winosver = [int]$matches[1] } + reg.exe unload "HKLM\BROKENSYSTEM" -# Acquiring reg values -$currentreg = (Get-ItemProperty -path Registry::HKU\BROKENSYSTEM\Select).current -$defaultreg = (Get-ItemProperty -path Registry::HKU\BROKENSYSTEM\Select).default -$failedreg = (Get-ItemProperty -path Registry::HKU\BROKENSYSTEM\Select).failed -$lkgcreg = (Get-ItemProperty -path Registry::HKU\BROKENSYSTEM\Select).LastKnownGood + # 4. Hive loader + Log-Info "Loading System hive from $($diskb):..." | Tee-Object -FilePath $logFile -Append + reg.exe load "HKU\BROKENSYSTEM" "$($diskb):\Windows\System32\config\SYSTEM" + Start-Sleep -Seconds 2 -# FILTER IF LKGC IS ALREADY THERE FOR Windows 10, Windows Server 2016, and newer versions. -if (($winosver -eq 10) -or ($winosver -ge 2016)) -{ -if ($currentreg -ge 2) {reg.exe unload "HKU\BROKENSYSTEM";write-output "LKGC WAS ALREADY SET, NO CHANGES DONE";start-sleep 5;exit} -elseif ($defaultreg -ge 2) {reg.exe unload "HKU\BROKENSYSTEM";write-output "LKGC WAS ALREADY SET, NO CHANGES DONE";start-sleep 5;exit} -elseif ($failedreg -ge 1) {reg.exe unload "HKU\BROKENSYSTEM";write-output "LKGC WAS ALREADY SET, NO CHANGES DONE";start-sleep 5;exit} -elseif ($lkgcreg -ge 2) {reg.exe unload "HKU\BROKENSYSTEM";write-output "LKGC WAS ALREADY SET, NO CHANGES DONE";start-sleep 5;exit} -} + # 5. Capture "BEFORE" State + $selectPath = "Registry::HKU\BROKENSYSTEM\Select" + $before = Get-ItemProperty -path $selectPath + Log-Info "REGISTRY STATE [BEFORE]: Current=$($before.current), Default=$($before.default), Failed=$($before.failed), LKG=$($before.LastKnownGood)" | Tee-Object -FilePath $logFile -Append -# FILTER IF LKGC IS ALREADY THERE FOR Windows Server 2012 version. -if ($winosver -eq 2012) -{ -# CHECK IF LKGC IS ALREADY THERE AND EXIT IF SO -if ($currentreg -ge 2) {reg.exe unload "HKU\BROKENSYSTEM";write-output "LKGC WAS ALREADY SET, NO CHANGES DONE";start-sleep 5;exit} -elseif ($defaultreg -ge 2) {reg.exe unload "HKU\BROKENSYSTEM";write-output "LKGC WAS ALREADY SET, NO CHANGES DONE";start-sleep 5;exit} -elseif ($failedreg -ge 1) {reg.exe unload "HKU\BROKENSYSTEM";write-output "LKGC WAS ALREADY SET, NO CHANGES DONE";start-sleep 5;exit} -elseif ($lkgcreg -ge 3) {reg.exe unload "HKU\BROKENSYSTEM";write-output "LKGC WAS ALREADY SET, NO CHANGES DONE";start-sleep 5;exit} -} + # 6. Logic Filter + $alreadySet = $false + if (($winosver -eq 10) -or ($winosver -ge 2016)) { + if (($before.current -ge 2) -or ($before.default -ge 2) -or ($before.failed -ge 1) -or ($before.LastKnownGood -ge 2)) { $alreadySet = $true } + } + elseif ($winosver -eq 2012) { + if (($before.current -ge 2) -or ($before.default -ge 2) -or ($before.failed -ge 1) -or ($before.LastKnownGood -ge 3)) { $alreadySet = $true } + } + + if ($alreadySet) { + reg.exe unload "HKU\BROKENSYSTEM" + Log-Warning "LKGC WAS ALREADY SET, NO CHANGES DONE" | Tee-Object -FilePath $logFile -Append + return $STATUS_SUCCESS + } -# Plus 1 -$currentreg = $currentreg+1 -$defaultreg = $defaultreg+1 -$failedreg = $failedreg+1 -$lkgcreg = $lkgcreg+1 + # 7. Apply Changes + Log-Info "Applying LKGC increments..." | Tee-Object -FilePath $logFile -Append + Set-Itemproperty -path $selectPath -Name 'current' -Type DWORD -value ($before.current + 1) + Set-Itemproperty -path $selectPath -Name 'default' -Type DWORD -value ($before.default + 1) + Set-Itemproperty -path $selectPath -Name 'failed' -Type DWORD -value ($before.failed + 1) + Set-Itemproperty -path $selectPath -Name 'LastKnownGood' -Type DWORD -value ($before.LastKnownGood + 1) -# ENABLING LKGC -Set-Itemproperty -path Registry::HKU\BROKENSYSTEM\Select -Name 'current' -Type DWORD -value $currentreg -Set-Itemproperty -path Registry::HKU\BROKENSYSTEM\Select -Name 'default' -Type DWORD -value $defaultreg -Set-Itemproperty -path Registry::HKU\BROKENSYSTEM\Select -Name 'failed' -Type DWORD -value $failedreg -Set-Itemproperty -path Registry::HKU\BROKENSYSTEM\Select -Name 'LastKnownGood' -Type DWORD -value $lkgcreg + # 8. Capture "AFTER" State + $after = Get-ItemProperty -path $selectPath + Log-Info "REGISTRY STATE [AFTER]: Current=$($after.current), Default=$($after.default), Failed=$($after.failed), LKG=$($after.LastKnownGood)" | Tee-Object -FilePath $logFile -Append -# Unload Hive -reg.exe unload "HKU\BROKENSYSTEM" + # 9. Cleanup + reg.exe unload "HKU\BROKENSYSTEM" + Log-Output "SCRIPT FINISHED PROPERLY, LKGC APPLIED" | Tee-Object -FilePath $logFile -Append + return $STATUS_SUCCESS -write-output " --------------- SCRIPT FINISHED PROPERLY, LKGC APPLIED --------------- " -start-sleep 10 +} +catch { + Log-Error "An unexpected error occurred: $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append + reg.exe unload "HKU\BROKENSYSTEM" 2>$null + reg.exe unload "HKLM\BROKENSYSTEM" 2>$null + return $STATUS_ERROR +} +finally { + Log-Info "Script execution ended at $(Get-Date)" | Tee-Object -FilePath $logFile -Append +}