Private/Resolve-WUSystemPartition.ps1

function Resolve-WUSystemPartition {
    <#
    .SYNOPSIS
        Resolves System Reserved Partition disk space issues for Windows Update.
 
    .DESCRIPTION
        Mounts the System Reserved partition and performs comprehensive cleanup operations to free disk space
        for Windows Update operations. Includes:
        - Font file cleanup (all font files in EFI\Microsoft\Boot\Fonts)
        - Language folder cleanup (removes non-English language folders)
        - Vendor firmware cleanup (HP, Dell, Lenovo, ASUS BIOS/firmware files)
        - Windows Update cache cleanup
         
        Based on Microsoft support recommendations and Garytown cleanup procedures.
 
    .PARAMETER LogPath
        Path to the log file for detailed logging.
 
    .EXAMPLE
        $result = Resolve-WUSystemPartition -LogPath "C:\Logs\wu.log"
 
    .NOTES
        This function requires Administrator privileges.
        Returns an object with Success, SpaceFreed, and RebootRequired properties.
        Vendor firmware files are permanently deleted to maximize space recovery.
         
    .REFERENCE
        https://support.microsoft.com/en-us/topic/-we-couldn-t-update-system-reserved-partition-error-installing-windows-10-46865f3f-37bb-4c51-c69f-07271b6672ac
        https://garytown.com/low-space-on-efi-system-partition-clean-up
    #>


    [CmdletBinding()]
    param(
        [string]$LogPath
    )

    # Initialize result object
    $result = [PSCustomObject]@{
        Success = $false
        SpaceFreed = 0
        RebootRequired = $false
        ActionsPerformed = @()
        ErrorMessage = $null
    }

    Write-WULog -Message "Starting System Reserved Partition remediation" -LogPath $LogPath

    try {
        # Mount the system reserved partition to Y:
        Write-WULog -Message "Mounting System Reserved partition to Y:..." -LogPath $LogPath
        
        $mountResult = & cmd.exe /c "mountvol Y: /s" 2>&1
        
        if (!(Test-Path 'Y:\')) { 
            throw "Failed to mount System Reserved partition to Y:. Output: $mountResult"
        }
        
        Write-WULog -Message "System Reserved partition mounted successfully" -LogPath $LogPath

        # Get initial free space
        try {
            $initialSpace = (Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object { $_.DeviceID -eq "Y:" }).FreeSpace
            Write-WULog -Message "Initial free space on System Reserved: $([math]::Round($initialSpace / 1MB, 2)) MB" -LogPath $LogPath
        }
        catch {
            Write-WULog -Message "Could not determine initial free space" -Level Warning -LogPath $LogPath
            $initialSpace = 0
        }

        # Delete all font files in the Fonts directory (enhanced cleanup)
        $fontsPath = 'Y:\EFI\Microsoft\Boot\Fonts'
        if (Test-Path $fontsPath) {
            try {
                $fontFiles = Get-ChildItem -Path $fontsPath -File -ErrorAction SilentlyContinue
                if ($fontFiles) {
                    $totalSize = ($fontFiles | Measure-Object -Property Length -Sum).Sum
                    Remove-Item -Path "$fontsPath\*.*" -Force -ErrorAction Stop
                    $result.ActionsPerformed += "Removed $($fontFiles.Count) font files ($([math]::Round($totalSize / 1MB, 2)) MB)"
                    $result.SpaceFreed += $totalSize
                }
            }
            catch {
                Write-WULog -Message "Error cleaning font files: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
            }
        }

        # Clean up language folders (keeping only essential languages)
        try {
            $bootPath = 'Y:\EFI\Microsoft\Boot'
            $preserveLanguages = @('en-US', 'en-GB', 'fr-CA')  
            $deletedLanguages = @()
            $totalLangSize = 0
            
            if (Test-Path $bootPath) {
                $bootLangFolders = Get-ChildItem -Path $bootPath -Directory -ErrorAction SilentlyContinue
                foreach ($folder in $bootLangFolders) {
                    if ($folder.Name -match '^[a-z]{2}-[A-Z]{2}$') {
                        if ($preserveLanguages -notcontains $folder.Name) {
                            try {
                                $langFiles = Get-ChildItem -Path $folder.FullName -File -Recurse -ErrorAction SilentlyContinue
                                if ($langFiles) {
                                    $langSize = ($langFiles | Measure-Object -Property Length -Sum).Sum
                                    Remove-Item -Path $folder.FullName -Recurse -Force -ErrorAction Stop
                                    $deletedLanguages += $folder.Name
                                    $totalLangSize += $langSize
                                }
                            }
                            catch {
                                Write-WULog -Message "Failed to delete language folder $($folder.Name): $($_.Exception.Message)" -Level Warning -LogPath $LogPath
                            }
                        }
                    }
                }
                
                if ($deletedLanguages.Count -gt 0) {
                    $result.ActionsPerformed += "Removed $($deletedLanguages.Count) language folders ($([math]::Round($totalLangSize / 1MB, 2)) MB)"
                    $result.SpaceFreed += $totalLangSize
                }
            }
        }
        catch {
            Write-WULog -Message "Error cleaning language folders: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
        }

        # Handle manufacturer-specific cleanup (enhanced with additional vendor paths)
        try {
            $manufacturer = (Get-CimInstance -ClassName Win32_ComputerSystem).Manufacturer
            Write-WULog -Message "Detected Manufacturer: $manufacturer" -LogPath $LogPath

            # Generic vendor firmware cleanup (from Garytown recommendations)
            $vendorPaths = @(
                'Y:\EFI\HP\BIOS\Previous',
                'Y:\EFI\HP\BIOS\Current', 
                'Y:\EFI\HP\DEVFW',
                'Y:\EFI\Lenovo\fw',
                'Y:\EFI\Dell\BIOS',
                'Y:\EFI\ASUS\fw'
            )

            $vendorFilesDeleted = 0
            $totalVendorSize = 0

            foreach ($vendorPath in $vendorPaths) {
                if (Test-Path $vendorPath) {
                    try {
                        $vendorFiles = Get-ChildItem -Path $vendorPath -File -Recurse -ErrorAction SilentlyContinue
                        if ($vendorFiles) {
                            $vendorSize = ($vendorFiles | Measure-Object -Property Length -Sum).Sum
                            $vendorName = ($vendorPath -split '\\')[2]  # Extract vendor name from path
                            
                            # Delete vendor firmware files directly
                            Remove-Item -Path $vendorPath -Recurse -Force -ErrorAction Stop
                            $vendorFilesDeleted += $vendorFiles.Count
                            $totalVendorSize += $vendorSize
                        }
                    }
                    catch {
                        Write-WULog -Message "Failed to delete vendor path $vendorPath : $($_.Exception.Message)" -Level Warning -LogPath $LogPath
                    }
                }
            }

            if ($vendorFilesDeleted -gt 0) {
                $result.ActionsPerformed += "Deleted $vendorFilesDeleted vendor firmware files ($([math]::Round($totalVendorSize / 1MB, 2)) MB)"
                $result.SpaceFreed += $totalVendorSize
            }

            # Manufacturer-specific additional cleanup
            switch -Wildcard ($manufacturer) {
                "*Dell*" {
                    # Check for additional Dell paths not covered above
                    $additionalDellPaths = @('Y:\Dell')
                    foreach ($dellPath in $additionalDellPaths) {
                        if (Test-Path $dellPath) {
                            $dellFiles = Get-ChildItem -Path $dellPath -File -Recurse -ErrorAction SilentlyContinue
                            if ($dellFiles) {
                                $totalSize = ($dellFiles | Measure-Object -Property Length -Sum).Sum
                                if ($totalSize -gt 10MB) {  # Only clean if significant space
                                    try {
                                        Remove-Item -Path $dellPath -Recurse -Force -ErrorAction Stop
                                        $result.ActionsPerformed += "Deleted additional Dell files ($([math]::Round($totalSize / 1MB, 2)) MB)"
                                        $result.SpaceFreed += $totalSize
                                    }
                                    catch {
                                        Write-WULog -Message "Failed to delete additional Dell files: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        catch {
            Write-WULog -Message "Error during manufacturer-specific cleanup: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
        }

        # Additional cleanup - old Windows Update cache files on System Reserved
        try {
            $updateCachePaths = @(
                'Y:\$Windows.~BT',
                'Y:\$Windows.~WS', 
                'Y:\Windows.old'
            )
            
            $cacheFilesDeleted = 0
            $totalCacheSize = 0
            
            foreach ($cachePath in $updateCachePaths) {
                if (Test-Path $cachePath) {
                    $cacheFiles = Get-ChildItem -Path $cachePath -Recurse -File -ErrorAction SilentlyContinue
                    if ($cacheFiles) {
                        $cacheSize = ($cacheFiles | Measure-Object -Property Length -Sum).Sum
                        if ($cacheSize -gt 0) {
                            # Only remove if it's actually taking significant space and is old
                            $cacheAge = (Get-Date) - (Get-Item $cachePath).LastWriteTime
                            if ($cacheAge.TotalDays -gt 7 -and $cacheSize -gt 50MB) {
                                Remove-Item -Path $cachePath -Recurse -Force -ErrorAction Stop
                                $cacheFilesDeleted += $cacheFiles.Count
                                $totalCacheSize += $cacheSize
                            }
                        }
                    }
                }
            }
            
            if ($cacheFilesDeleted -gt 0) {
                $result.ActionsPerformed += "Removed old Windows Update cache ($([math]::Round($totalCacheSize / 1MB, 2)) MB)"
                $result.SpaceFreed += $totalCacheSize
            }
        }
        catch {
            Write-WULog -Message "Error cleaning Windows Update cache: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
        }

        # Check final free space
        try {
            $finalSpace = (Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object { $_.DeviceID -eq "Y:" }).FreeSpace
            $freedSpace = $finalSpace - $initialSpace
            Write-WULog -Message "Final free space on System Reserved: $([math]::Round($finalSpace / 1MB, 2)) MB" -LogPath $LogPath
            Write-WULog -Message "Space freed: $([math]::Round($freedSpace / 1MB, 2)) MB" -LogPath $LogPath
            
            # Update the actual space freed (may be different from file sizes due to cluster sizes)
            if ($freedSpace -gt 0) {
                $result.SpaceFreed = $freedSpace
            }
        }
        catch {
            Write-WULog -Message "Could not determine final free space" -Level Warning -LogPath $LogPath
        }

        $result.Success = $true

    }
    catch {
        $result.ErrorMessage = $_.Exception.Message
        Write-WULog -Message "Error during System Reserved partition remediation: $($_.Exception.Message)" -Level Error -LogPath $LogPath
    }
    finally {
        # Always attempt to unmount
        Write-WULog -Message "Unmounting System Reserved partition..." -LogPath $LogPath
        
        try {
            $unmountResult = & cmd.exe /c "mountvol Y: /d" 2>&1
            
            # Give it a moment to unmount
            Start-Sleep -Seconds 2
            
            if (Test-Path 'Y:\') {
                Write-WULog -Message "Y: drive still mounted after unmount attempt" -Level Warning -LogPath $LogPath
                
                # Try force unmount
                try {
                    & cmd.exe /c "mountvol Y: /d" 2>&1 | Out-Null
                    Start-Sleep -Seconds 1
                }
                catch {
                    Write-WULog -Message "Force unmount also failed" -Level Warning -LogPath $LogPath
                }
            }
        }
        catch {
            Write-WULog -Message "Error during unmount: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
        }
    }

    # Summary
    if ($result.Success) {
        Write-WULog -Message "System Reserved Partition remediation completed successfully" -LogPath $LogPath
        if ($result.SpaceFreed -gt 0) {
            Write-WULog -Message "Total space freed: $([math]::Round($result.SpaceFreed / 1MB, 2)) MB" -LogPath $LogPath
        }
    } else {
        Write-WULog -Message "System Reserved Partition remediation failed: $($result.ErrorMessage)" -Level Error -LogPath $LogPath
    }

    return $result
}