Skip to content

Fix various win-toggle-safe-mode errors and confirm gen2 compatible - #106

Open
Josh S (Vorsku) wants to merge 2 commits into
Azure:mainfrom
Vorsku:main
Open

Fix various win-toggle-safe-mode errors and confirm gen2 compatible#106
Josh S (Vorsku) wants to merge 2 commits into
Azure:mainfrom
Vorsku:main

Conversation

@Vorsku

Copy link
Copy Markdown

Hi,

Thanks for this project!

I've encountered various issues with the win-toggle-safe-mode script which this PR fixes:

  1. Missing comments on line 48-49 which stop the script starting entirely
  2. AZ CLI always passes parameters as strings so the current DC switch does not work
  3. Any partitions without a drive letter get ignored (which includes the EFI partition we care about for safe mode which normally doesn't have a drive letter) so refactored to also check access paths
  4. Script error if it can't start up the ProblemVM - I've found by default my repair VM doesn't have enough memory to power up the nested VM, but we don't need the ProblemVM to be started to perform the safe mode changes so I've changed the error action to SilentlyContinue

I've tested this updated version successfully against a 2016 domain controller running in Azure as a Gen2:

az vm repair create -g rg-adrestore-tst01 -n vm-adrestore-tst01 --yes --repair-username local_admin --repair-password 'password!234' --enable-nested --verbose

az vm repair run -g rg-adrestore-tst01 -n vm-adrestore-tst01 --preview "https://github.com/Vorsku/repair-script-library/blob/main/map.json" --run-id win-toggle-safe-mode --parameters safeModeSwitch=on DC=yes --verbose --run-on-repair

az vm repair restore -g rg-adrestore-tst01 -n vm-adrestore-tst01 --verbose

Thanks,
Josh

@Vorsku

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@Vorsku

Copy link
Copy Markdown
Author

Adam Sandor (@Sandido) hoping you can review please? The current script doesn't work anyway due to the missing comments

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Fixes four issues in win-toggle-safe-mode.ps1:

  1. Broken comment syntax (lines 48-49) — Missing # on version history lines causes PowerShell parse failure, making the entire script unusable.
  2. [switch]$DC incompatible with AZ CLIaz vm repair run --parameters passes all values as strings; [switch] never activates. Changed to [ValidateSet("Yes","No")][string].
  3. Gen2 EFI partition not detected — EFI System Partition has no drive letter. New code falls back to AccessPaths (volume GUID) for partition scanning.
  4. Error handling adjustmentsStart-VM changed to SilentlyContinue; catch block simplified.

What's Good

Area Detail
Comment syntax fix (lines 48-49) Critical fix. The current main has v0.4: and v0.3: without # — PowerShell treats these as statements and the script fails to parse. This alone justifies the PR
$DC switch → string Correct. az vm repair run --parameters DC=$true passes "$true" as a literal string, not a boolean. [switch] never activates. ValidateSet("Yes","No") is the right pattern
Volume GUID access paths Gen2 VMs have an EFI System Partition without a drive letter. The new $driveCandidates logic correctly falls back to AccessPaths (e.g., \\?\Volume{...}\), enabling BCD discovery at \efi\microsoft\boot\bcd
$osDrive tracking Fixes a real bug — the old code used $drive (loop variable) for registry operations after the loop, meaning it would reference the last iterated partition, not the one containing winload.exe
$root normalization Clean handling of both "C:" and \\?\Volume{...}\ path formats for BCD/OS path construction

Issues Found

🔴 Bug: $osDrive used with drive-letter syntax for registry paths

After the loop, the code uses $osDrive with drive-letter syntax:

# Line 167 (PR)
$regPath = $osDrive + ':\Windows\System32\config\'

# Line 176 (PR)
reg load "HKLM\BROKENSYSTEM" "$($osDrive):\Windows\System32\config\SYSTEM"

If $osDrive is a volume GUID path (e.g., \\?\Volume{abc}\), appending :\Windows\... produces an invalid path like \\?\Volume{abc}\:\Windows\....

In practice this is low-risk because winload.exe lives on the OS partition which almost always has a drive letter (the letterless partition is typically just EFI). But for correctness, the fix should store a normalized root alongside $osDrive:

if ($isOsPath) {
    $osDrive = $drive
    $osRoot = $root   # <-- store the normalized root too
}

Then use $osRoot for all path construction after the loop:

$regPath = "${osRoot}\Windows\System32\config\"
reg load "HKLM\BROKENSYSTEM" "${osRoot}\Windows\System32\config\SYSTEM"

🟡 Concern: Start-VM with -ErrorAction SilentlyContinue

start-vm $guestHyperVVirtualMachine -ErrorAction SilentlyContinue
#Sometimes the repair VM doesn't have enough memory to power it on

This silently swallows the failure, then returns $STATUS_SUCCESS. The user sees "success" but the nested VM is still stopped. A better approach:

try {
    Start-VM $guestHyperVVirtualMachine -ErrorAction Stop
}
catch {
    Log-Output "WARNING: Could not start nested VM (may need more memory). BCD changes were applied successfully." |
        Tee-Object -FilePath $logFile -Append
}

This still doesn't block the script but gives the user visibility.


🟡 Concern: Catch block no longer restarts the VM

The old catch block attempted to restart the nested VM after an error. The PR removes this:

# REMOVED:
# Start Hyper-V VM again
# Log-Output "#06 - Starting VM"
# start-vm $guestHyperVVirtualMachine -ErrorAction Stop

If the script fails mid-operation, the nested VM is left stopped with its disk offline. The old behavior at least tried to restore the VM to a running state. Consider keeping this (with SilentlyContinue or a try/catch) so the user's nested environment isn't left broken.


🟢 Minor: Missing newline at end of file

The diff shows No newline at end of file on the last line. Should add a trailing newline.


🟢 Minor: Catch block log step number

Log-Output "#99 - Bringing disk offline to restart Hyper-V VM"

Changing #05 to #99 is fine for disambiguation but #99 is arbitrary. Consider a naming convention like #ERR-01 to clearly indicate it's an error-path step.


@Vorsku

Copy link
Copy Markdown
Author

Edwin Bernal Microsoft (@EdwinBernal1) could you review the changes please?

@glimoli

Copy link
Copy Markdown
Contributor

VMRepair Script Test Report: win-toggle-safe-mode.ps1

Summary

Overall Score: 30/100 (Grade: F) — BLOCK

Category Score Notes
Functional Correctness 0/20 Script failed: Operation not supported on a critical disk — disk filter includes rescue VM boot disk
Code Quality 16/20 20 PSScriptAnalyzer info-level issues (cmdlet aliases: where, select, group)
Safety & Rollback 6/20 Catch block crashes on same disk filter bug; no boot disk exclusion
Telemetry Coverage 0/20 No Application Insights instrumentation
Test Coverage 8/20 PR author verified on WS2016 DC Gen2; our Gen2 test failed

Fault Injection Results

Phase Gen2 (EFI) Status
Pre-injection (baseline) No safeboot flag Baseline
Post-injection (breaker) safeboot = Network CORRUPTED
Post-repair (script) Script error — no BCD change FAILED
Post-restore + boot Not reached

Testing Performed

Strategy: Native az vm repair flow with fault injection
Fault Injection: ✅ Breaker: break-win-toggle-safe-mode.ps1
Region: westus2
Date: 2026-08-20

Dimension Configuration Result
Gen2 Standard Win2022Datacenter, Standard_D4s_v3, --enable-nested ❌ FAIL

Opportunities for Improvement

70 points recoverable (current 30 → potential 100)

  • Critical (blocking): Fix disk filter to exclude rescue VM boot disk ($_.IsBoot -eq $false or $_.Number -ne 0)
  • Critical (blocking): Fix catch block (line 293) — same $disk variable causes secondary crash
  • High: Replace cmdlet aliases (whereWhere-Object, selectSelect-Object, groupGroup-Object)
  • Medium: Add Application Insights telemetry for safeboot toggle success/failure
  • Low: Add explicit error exit when no BCD store is found on any disk (currently falls through silently with exit 0)

How to Reach 100/100

Action Category Impact New Score
Fix disk filter to exclude boot disk Functional 0 → 20, Safety 6 → 16 60/100
Fix silent exit-0 when no BCD found Safety 16 → 20 64/100
Replace cmdlet aliases Code Quality 16 → 20 68/100
Add telemetry instrumentation Telemetry 0 → 14 82/100
Test Gen1 + Gen2 with fix Test Coverage 8 → 20 100/100

Validation Evidence

Breaker Output (Confirmed Corrupted):

[BREAKER] bcdedit result: The operation completed successfully.
[BREAKER] VERIFICATION: Safeboot flag confirmed set
[BREAKER] Value: safeboot                Network
[BREAKER]::SUCCESS - Safe mode injection complete

Repair Script Output (Failed):

[Output] START: Running script win-toggle-safe-mode
[Output] #02 - Bringing disk online
[Output] ERR-01 - Bringing disk offline to restart Hyper-V VM
[Error]  Operation not supported on a critical disk.

Root Cause Detail

The disk filter at line 89:

$disk = get-disk -ErrorAction Stop | where { $_.FriendlyName -eq 'Msft Virtual Disk' }

matches all Azure virtual disks including the repair VM's OS disk (Disk 0). When set-disk -IsOffline is called, Windows blocks the operation on the boot disk. The catch block (line 293) attempts the same operation, causing a secondary failure.

Suggested fix:

$disk = Get-Disk -ErrorAction Stop | Where-Object {
    $_.FriendlyName -eq 'Msft Virtual Disk' -and $_.IsBoot -eq $false
}

Review Checklist

  • Fault injection verified (inject → repair → validate) — injection succeeded, repair failed
  • Code quality reviewed (PSScriptAnalyzer — info-level only)
  • Telemetry coverage reviewed (no instrumentation)
  • Safety features verified (catch block crashes on same bug)
  • Multi-generation tested (Gen2 failed; Gen1 not reached)

Test Artifacts

  • Test Report: Output/TestReports/PR106-win-toggle-safe-mode/2026-08-20/
  • Breaker Script: Skills/.breaker-scripts/break-win-toggle-safe-mode.ps1

Note: The PR's individual changes (DC parameter string conversion, EFI AccessPaths, Start-VM error handling) are sound in isolation. The disk filter issue may be pre-existing in the main branch but surfaces in the standard az vm repair environment where both OS and data disks are "Msft Virtual Disk".


Generated by VMRepairMint Script Testing Agent | Test ID: win-toggle-safe-mode-20260820-222958

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants