← Back to articles Intune

Fixing HP/Dell/Lenovo UEFI Boot Failures with Intune Proactive Remediation

Fixing HP/Dell/Lenovo UEFI Boot Failures with Intune Proactive Remediation
🔴

Active incident: HP EliteBook, ProBook, Dell Latitude and Lenovo ThinkPad running Windows 11 23H2/24H2 can become unbootable after a standard Windows Update. This post covers the root cause and a production-tested Intune Proactive Remediation fix.

01 The root cause — two problems collide

The symptom is brutal: after a reboot following Windows Update, the PC shows "Boot Device Not Found" or drops straight into the UEFI shell. No hardware failure, no disk death — the UEFI boot loader was corrupted silently during the update process.

This isn't a single bug. It's the collision of two independent issues that together take down the entire boot chain.

EFI Boot Failure — Causal Chain
HP / DELL / LENOVO EFI PARTITION WINDOWS UPDATE BIOS Update runs Writes firmware files to EFI\HP\DEVFW\ *.BIN files accumulate Firmware.BIN ~39 MB MeCurrent.BIN ~12 MB Secure Boot CA 2023 Writes new bootmgfw.efi + BCD to EFI partition EFI partition FULL 100 MB — <10 MB free Write FAILS No space — partial write BCD corrupted / incomplete Windows boot entry invalid BOOT DEVICE NOT FOUND UEFI entry missing after reboot Disk appears dead / undetectable THE FIX Delete *.BIN from DEVFW Free space → write succeeds

The diagram shows why this is insidious: neither issue alone is fatal. An EFI partition with 50 MB free and a BIOS update history would survive indefinitely. Secure Boot CA 2023 would also apply cleanly on a machine with a fresh 260 MB EFI partition. It's only the combination that causes the crash.

🔗
This is tracked in GitHub issue microsoft/secureboot_objects #396. Many HP firmware versions still don't ship the UEFI CA 2023 keys natively — making Windows push harder to update them via the EFI partition.

02 What the remediation script does

The script runs as SYSTEM in 64-bit PowerShell via Intune Proactive Remediation. Here's the execution flow in four steps:

Step 1 Mount the EFI partition

Uses Add-PartitionAccessPath (native PowerShell cmdlet, no timing issues) to assign a temporary drive letter to the hidden EFI FAT32 partition. Falls back to diskpart /s tmpfile if the cmdlet fails — writing a temp file instead of piping avoids the classic pipeline encoding issues.

⚠️
Why not pipe to diskpart? Piping commands to diskpart.exe via PowerShell is unreliable — the process may not receive the newlines correctly in all contexts. Writing a temp .txt script and using diskpart /s is the production-safe approach.
Step 2 Measure free space before cleanup

Uses [System.IO.DriveInfo] instead of Get-PSDrive — the former reads directly from the OS and doesn't require a cache refresh. Falls back to WMI Win32_LogicalDisk if DriveInfo.IsReady returns false.

Step 3 Delete vendor staging files

Targets per-brand paths. These are firmware staging copies — not the active firmware itself, which lives on a separate protected partition:

BrandPaths cleanedTypical size freed
HPEFI\HP\DEVFW · EFI\HP\BIOS\Previous40–70 MB
DellEFI\Dell\UpdateCapsule · EFI\Dell\BIOS20–50 MB
LenovoEFI\Lenovo\UpdateCapsule15–40 MB
Is it safe to delete MeCurrent.BIN? Yes. This is the Intel Management Engine firmware staged for the next flash — not the running ME firmware. The active ME firmware lives on a dedicated hidden partition inaccessible from the OS. Deleting the staging copy is completely safe.
Step 4 Unmount, verify, report

The finally{} block always runs — even on exception. It unmounts the EFI partition, then runs two post-cleanup checks:

  • Secure Boot state via Confirm-SecureBootUEFI — confirms the firmware is still reporting boot as secure
  • BCD integrity via bcdedit /enum '{bootmgr}' — confirms the Windows Boot Manager entry is present and valid

A single Write-Output line is emitted at the very end — this is what Intune captures in the Post-remediation detection output column.

03 The scripts

Detection script

Exit 0 = OK, exit 1 = remediation required. The single Write-Output line is what appears in the Pre-remediation detection output column in Intune.

Detect-EFIBootRisk.ps1
# Run as : SYSTEM (64-bit) · exit 0 = OK · exit 1 = Remediate
$ErrorActionPreference = 'SilentlyContinue'

function Write-Log($Msg, $Level = 'INFO') {
    # Write-Host ONLY — never Write-Output — Intune ignores Write-Host
    $c = switch ($Level) { 'WARN'{'Yellow'} 'ERROR'{'Red'} 'OK'{'Green'} default{'Cyan'} }
    Write-Host "[$(Get-Date -f 'HH:mm:ss')][$Level] $Msg" -ForegroundColor $c
}

$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
           ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
    Start-Process powershell "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs
    exit 0
}

$TempLetter = 'Q'; $Mounted = $false
$Brand = 'Unknown'; $Model = 'Unknown'; $RiskLevel = 'UNKNOWN'
$EFIFreeMB = 0; $EFITotalMB = 0; $VendorSizeMB = 0; $VendorFiles = 0

try {
    $cs = Get-WmiObject Win32_ComputerSystem
    $Brand = switch -Wildcard ($cs.Manufacturer.Trim()) {
        '*HP*'{'HP'} '*Hewlett*'{'HP'} '*Dell*'{'Dell'} '*Lenovo*'{'Lenovo'} default{'Other'}
    }
    $Model = $cs.Model.Trim()
    Write-Log "Brand=$Brand | Model=$Model"

    $efiPart = Get-Partition |
        Where-Object { $_.GptType -eq '{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}' } |
        Select-Object -First 1

    if (-not $efiPart) {
        Write-Output "RISK=SKIP;REASON=NO_EFI_PARTITION;BRAND=$Brand;MODEL=$Model"
        exit 0
    }

    $DiskNumber = $efiPart.DiskNumber; $PartNumber = $efiPart.PartitionNumber

    if (Test-Path "${TempLetter}:\") { $TempLetter = 'P' }
    if (Test-Path "${TempLetter}:\") { $TempLetter = 'X' }

    try {
        Add-PartitionAccessPath -DiskNumber $DiskNumber -PartitionNumber $PartNumber `
            -AccessPath "${TempLetter}:\" -ErrorAction Stop
        Start-Sleep -Milliseconds 800; $Mounted = $true
    } catch {
        $dpFile = Join-Path $env:TEMP "efi_$(Get-Random).txt"
        @"
select disk $DiskNumber
select partition $PartNumber
assign letter=$TempLetter
exit
"@ | Set-Content $dpFile -Encoding ASCII -Force
        Start-Process diskpart.exe -ArgumentList "/s `"$dpFile`"" -Wait -WindowStyle Hidden
        Remove-Item $dpFile -Force -EA SilentlyContinue
        $w=0; while(-not(Test-Path "${TempLetter}:\") -and $w-lt 10){Start-Sleep 1;$w++}
        if(Test-Path "${TempLetter}:\"){$Mounted=$true}else{throw 'EFI mount failed'}
    }

    $d = [System.IO.DriveInfo]"${TempLetter}:"
    if($d.IsReady){ $EFIFreeMB=[math]::Round($d.AvailableFreeSpace/1MB,1); $EFITotalMB=[math]::Round($d.TotalSize/1MB,1) }

    $vendorPaths = switch($Brand){
        'HP'     { @("${TempLetter}:\EFI\HP\DEVFW", "${TempLetter}:\EFI\HP\BIOS\Current") }
        'Dell'   { @("${TempLetter}:\EFI\Dell", "${TempLetter}:\EFI\DELL") }
        'Lenovo' { @("${TempLetter}:\EFI\Lenovo") }
        default  { @() }
    }
    foreach($p in $vendorPaths){
        if(Test-Path $p){
            $i=Get-ChildItem $p -Recurse -File -EA SilentlyContinue
            $VendorFiles+=$i.Count
            $VendorSizeMB+=[math]::Round(($i|Measure-Object Length -Sum).Sum/1MB,1)
        }
    }

    $RiskLevel = if($EFIFreeMB-lt 20){'CRITICAL'}
                 elseif($EFIFreeMB-lt 35-or($VendorSizeMB-gt 30-and $EFIFreeMB-lt 55)){'HIGH'}
                 elseif($VendorFiles-gt 0-and $EFIFreeMB-lt 80){'MEDIUM'}
                 else{'LOW'}

    # ── SINGLE Write-Output line → Intune Pre-remediation detection output ──
    Write-Output "RISK=$RiskLevel | FREE=${EFIFreeMB}MB/${EFITotalMB}MB | VENDOR=${VendorSizeMB}MB(${VendorFiles}f) | $Brand $Model"
    if($RiskLevel -in 'CRITICAL','HIGH'){ exit 1 } else { exit 0 }

} catch {
    Write-Output "RISK=ERROR | $($_.Exception.Message) | $Brand $Model"; exit 0
} finally {
    if($Mounted){
        Remove-PartitionAccessPath -DiskNumber $DiskNumber -PartitionNumber $PartNumber `
            -AccessPath "${TempLetter}:\" -EA SilentlyContinue
    }
}

Remediation script (v4 — production tested)

Verified on HP EliteBook 840 G11 (256 MB EFI). Freed 54.4 MB across 6 staging files in under 3 seconds.

Remediate-EFIBootRisk.ps1 — v4
# Run as : SYSTEM (64-bit) · Intune Proactive Remediation
# Log    : C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\EFI-Remediation.log
$ErrorActionPreference = 'SilentlyContinue'
$LogFile    = 'C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\EFI-Remediation.log'
$TempLetter = 'Q'; $Mounted = $false; $Cleaned = 0; $FreedMB = 0
$Brand = 'Unknown'; $Model = 'Unknown'; $SecureBoot = 'Unknown'

function Write-Log($Msg, $Level = 'INFO') {
    # Write-Host + file log only — NEVER Write-Output here
    $line = "[$(Get-Date -f 'HH:mm:ss')][$Level] $Msg"
    Add-Content -Path $LogFile -Value $line -Force
    $c = switch($Level){'WARN'{'Yellow'}'ERROR'{'Red'}'FIX'{'Green'}'OK'{'Green'}default{'Cyan'}}
    Write-Host $line -ForegroundColor $c
}

Write-Log '=== EFI Remediation START === v4'

try {
    # 1. Brand & model ─────────────────────────────────────────────
    $cs = Get-WmiObject Win32_ComputerSystem
    $Brand = switch -Wildcard ($cs.Manufacturer.Trim()){
        '*HP*'{'HP'}'*Hewlett*'{'HP'}'*Dell*'{'Dell'}'*Lenovo*'{'Lenovo'}default{'Other'}
    }
    $Model = $cs.Model.Trim()
    Write-Log "Brand=$Brand | Model=$Model"

    # 2. EFI partition ─────────────────────────────────────────────
    $efiPart = Get-Partition |
        Where-Object { $_.GptType -eq '{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}' } |
        Select-Object -First 1
    if(-not $efiPart){ Write-Output "STATUS=SKIP|REASON=NO_EFI|$Brand $Model"; exit 0 }
    $DiskNumber = $efiPart.DiskNumber; $PartNumber = $efiPart.PartitionNumber
    Write-Log "EFI partition : Disk $DiskNumber / Partition $PartNumber"

    # 3. Mount EFI ─────────────────────────────────────────────────
    if(Test-Path "${TempLetter}:\"){$TempLetter='P'}
    if(Test-Path "${TempLetter}:\"){$TempLetter='X'}
    try {
        Add-PartitionAccessPath -DiskNumber $DiskNumber -PartitionNumber $PartNumber `
            -AccessPath "${TempLetter}:\" -ErrorAction Stop
        Start-Sleep -Milliseconds 800; $Mounted=$true
        Write-Log "EFI mounted via Add-PartitionAccessPath at ${TempLetter}:"
    } catch {
        Write-Log "Add-PartitionAccessPath failed — diskpart fallback" 'WARN'
        $dpFile = Join-Path $env:TEMP "efi_rem_$(Get-Random).txt"
        @"
select disk $DiskNumber
select partition $PartNumber
assign letter=$TempLetter
exit
"@ | Set-Content $dpFile -Encoding ASCII -Force
        Start-Process diskpart.exe -ArgumentList "/s `"$dpFile`"" -Wait -WindowStyle Hidden
        Remove-Item $dpFile -Force -EA SilentlyContinue
        $w=0; while(-not(Test-Path "${TempLetter}:\")-and $w-lt 10){Start-Sleep 1;$w++}
        if(Test-Path "${TempLetter}:\"){$Mounted=$true}else{throw "EFI mount impossible after ${w}s"}
    }

    # 4. Free space BEFORE ─────────────────────────────────────────
    $di = [System.IO.DriveInfo]"${TempLetter}:"
    $FreeBefore = [math]::Round($di.AvailableFreeSpace/1MB,1)
    $TotalMB    = [math]::Round($di.TotalSize/1MB,1)
    Write-Log "EFI before : Free=${FreeBefore}MB / Total=${TotalMB}MB"

    # 5. Delete vendor staging files ───────────────────────────────
    $targetPaths = switch($Brand){
        'HP'     { @("${TempLetter}:\EFI\HP\DEVFW", "${TempLetter}:\EFI\HP\BIOS\Previous") }
        'Dell'   { @("${TempLetter}:\EFI\Dell\UpdateCapsule", "${TempLetter}:\EFI\Dell\BIOS") }
        'Lenovo' { @("${TempLetter}:\EFI\Lenovo\UpdateCapsule") }
        default  { @() }
    }
    foreach($path in $targetPaths){
        if(Test-Path $path){
            Get-ChildItem $path -Recurse -File -EA SilentlyContinue | ForEach-Object {
                $sz = [math]::Round($_.Length/1MB,2)
                try{
                    Remove-Item $_.FullName -Force -ErrorAction Stop
                    $FreedMB+=$sz; $Cleaned++
                    Write-Log "Deleted : $($_.Name) (${sz}MB)" 'FIX'
                } catch { Write-Log "Cannot delete $($_.Name) : $($_.Exception.Message)" 'WARN' }
            }
        } else { Write-Log "Path not found (skipped) : $path" }
    }
    $FreedMB = [math]::Round($FreedMB,1)
    Write-Log "Cleanup done : $Cleaned file(s) deleted, ${FreedMB}MB freed"

    # 6. Free space AFTER ──────────────────────────────────────────
    $di = [System.IO.DriveInfo]"${TempLetter}:"
    $FreeAfter = [math]::Round($di.AvailableFreeSpace/1MB,1)
    Write-Log "EFI after : Free=${FreeAfter}MB / Total=${TotalMB}MB"
    if($FreeAfter-lt 30){
        Write-Log "WARNING : ${FreeAfter}MB free — EFI resize recommended (target 260MB)" 'WARN'
    }

} catch {
    Write-Log "FATAL : $($_.Exception.Message)" 'ERROR'
    Write-Output "STATUS=ERROR | $($_.Exception.Message) | $Brand $Model"; exit 1

} finally {
    # 7. Unmount EFI (always runs) ─────────────────────────────────
    if($Mounted){
        try{
            Remove-PartitionAccessPath -DiskNumber $DiskNumber -PartitionNumber $PartNumber `
                -AccessPath "${TempLetter}:\" -EA SilentlyContinue
            Write-Log 'EFI unmounted (Remove-PartitionAccessPath)'
        } catch {
            $dpFile = Join-Path $env:TEMP "efi_umount_$(Get-Random).txt"
            @"
select disk $DiskNumber
select partition $PartNumber
remove letter=$TempLetter
exit
"@ | Set-Content $dpFile -Encoding ASCII -Force
            Start-Process diskpart.exe -ArgumentList "/s `"$dpFile`"" -Wait -WindowStyle Hidden
            Remove-Item $dpFile -Force -EA SilentlyContinue
            Write-Log 'EFI unmounted (diskpart fallback)'
        }
    }

    # 8. SecureBoot state (post-cleanup, outside EFI partition) ───
    $SecureBoot = (Confirm-SecureBootUEFI -ErrorAction SilentlyContinue)
    Write-Log "SecureBoot state : $SecureBoot"

    # 9. BCD integrity check ───────────────────────────────────────
    $bcd = & bcdedit /enum '{bootmgr}' 2>&1
    if($bcd -match 'Windows Boot Manager'){
        Write-Log 'BCD integrity : OK (Windows Boot Manager entry present)' 'OK'
    } else {
        Write-Log 'BCD integrity : WARNING — Boot Manager entry not found' 'WARN'
    }

    # 10. SINGLE Write-Output line → Intune Post-remediation output
    if($Cleaned-gt 0){
        Write-Output "STATUS=FIXED | FREED=${FreedMB}MB | FILES=$Cleaned | FREE_NOW=${FreeAfter}MB/${TotalMB}MB | SB=$SecureBoot | $Brand $Model"
    } else {
        Write-Output "STATUS=CLEAN | NOTHING_TO_DELETE | FREE=${FreeAfter}MB/${TotalMB}MB | SB=$SecureBoot | $Brand $Model"
    }
    Write-Log '=== EFI Remediation END ==='
}

04 Intune configuration

SettingValueWhy
Run as logged-on credentials ✕ No Add-PartitionAccessPath requires SYSTEM. User context → Access Denied.
Enforce script signature check ✕ No Script is unsigned. Only enable if you have an internal PKI and sign your scripts.
Run in 64-bit PowerShell ✓ Yes The Storage module (Get-Partition, Add-PartitionAccessPath) only exists in 64-bit. This is the most critical setting.
Schedule Daily · 09:00 Keeps the EFI partition clean after every BIOS update cycle.
🔴
Run in 64-bit PowerShell must be Yes. If set to No, the script runs in a 32-bit WOW64 context where the Storage module doesn't exist. Every cmdlet call will fail silently and you'll see RISK=ERROR in the detection output with no useful message.

05 Real-world output

Here's what the script produced running locally on an HP EliteBook 840 G11 (256 MB EFI partition):

Console output (Write-Host — not captured by Intune)
[17:16:18][INFO] === EFI Remediation START === v4
[17:16:18][INFO] Brand=HP | Model=HP EliteBook 840 14 inch G11 Notebook PC
[17:16:21][INFO] EFI mounted at Q:
[17:16:21][INFO] EFI before : Free=151MB / Total=256MB
[17:16:21][FIX] Deleted : Firmware.BIN (39MB)
[17:16:21][FIX] Deleted : RealtekPD.bin (0.13MB)
[17:16:21][FIX] Deleted : Realtek.bin (3.02MB)
[17:16:21][FIX] Deleted : PDT.bin (0MB)
[17:16:21][FIX] Deleted : ClickPad.bin (0.23MB)
[17:16:21][FIX] Deleted : MeCurrent.BIN (12.04MB)
[17:16:21][INFO] Path not found (skipped) : Q:\EFI\HP\BIOS\Previous
[17:16:21][INFO] Cleanup done : 6 files deleted, 54.4MB freed
[17:16:21][INFO] EFI after : Free=205.4MB / Total=256MB
[17:16:21][OK] BCD integrity : OK (Windows Boot Manager entry present)
[17:16:21][INFO] EFI unmounted
Intune Post-remediation detection output (single Write-Output line)
STATUS=FIXED | FREED=54.4MB | FILES=6 | FREE_NOW=205.4MB/256MB | SB=True | HP HP EliteBook 840 14 inch G11 Notebook PC
Before cleanup
151 MB free / 256 MB
6 staging files present
54.4 MB occupied by DEVFW
After cleanup
205.4 MB free / 256 MB
Secure Boot: True
BCD: Windows Boot Manager OK
ℹ️
This G11 had a 256 MB EFI partition (the new standard) so it was not at immediate boot-failure risk. The real danger zone is older HP models with 100 MB EFI partitions where 54 MB of DEVFW files would leave less than 5 MB free — guaranteeing a BCD write failure on the next Secure Boot CA 2023 update.

06 References

🎓 Ready to go deeper?

Practice real MD-102 exam questions, get AI feedback on your weak areas, and fast-track your Intune certification.

Start Free Practice → Book a Session
Souhaiel Morhag
Souhaiel Morhag
Microsoft Endpoint & Modern Workplace Engineer

Souhaiel Morhag is a Microsoft Intune and endpoint management specialist with hands-on experience deploying and securing enterprise environments across Microsoft 365. He founded MSEndpoint.com to share practical, real-world guides for IT admins navigating Microsoft technologies — and built the MSEndpoint Academy at app.msendpoint.com/academy, a dedicated learning platform for professionals preparing for the MD-102 (Microsoft 365 Endpoint Administrator) certification. Through in-depth articles and AI-powered practice exams, Souhaiel helps IT teams move faster and certify with confidence.

Related Articles

Popular on MSEndpoint