Skip to content

Commit 5e4702e

Browse files
committed
fix(Update-ToWin11): improve error handling and logging during Windows 11 upgrade process
1 parent 6da5a6c commit 5e4702e

1 file changed

Lines changed: 114 additions & 54 deletions

File tree

src/microsoft/windows/Update-ToWin11.psm1

Lines changed: 114 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -142,63 +142,101 @@ function Test-CanUpgrade {
142142
}
143143
}
144144

145-
function Write-SetupError([Bool]$MaybeSuccess) {
145+
function Write-SetupError {
146146
$RegPath = 'HKLM:\SYSTEM\Setup\setupdiag\results';
147-
$Success = Get-RegistryKey $RegPath 'OperationCompletedSuccessfully';
148-
if ($False -and $MaybeSuccess -and $Success -eq 'True') {
149-
Invoke-Info 'Windows 11 upgrade completed successfully';
147+
$FailureData = Get-RegistryKey $RegPath 'FailureData';
148+
$FailureDetails = Get-RegistryKey $RegPath 'FailureDetails';
149+
150+
Invoke-Error "Windows 11 upgrade failed, see the details below:";
151+
Invoke-Error "Failure Data: $FailureData";
152+
Invoke-Error "Failure Details: $FailureDetails";
153+
154+
$ScanResultFile = "$env:SystemDrive\`$WINDOWS.~BT\Sources\Panther\ScanResult.xml";
155+
if (Test-Path $ScanResultFile) {
156+
Invoke-Verbose 'Checking for blocking drivers...';
157+
158+
$PnpDevices = Get-PnpDevice -PresentOnly;
159+
$PnpDeviceInfs = @{};
160+
$PnpDevices | ForEach-Object {
161+
$InfPath = $_ | Get-PnpDeviceProperty -KeyName 'DEVPKEY_Device_DriverInfPath';
162+
$PnpDeviceInfs[$_.InstanceId] = $InfPath.Data;
163+
}
150164

165+
$DriverPackages = (Select-Xml -Path $ScanResultFile -XPath '/*').Node.DriverPackages.DriverPackage;
166+
$DriverPackages | ForEach-Object {
167+
$Driver = $_;
168+
if ($Driver.BlockMigration -eq 'True') {
169+
$DriverName = $Driver.Inf;
170+
$DriverDetails = Get-WindowsDriver -Online -Driver $DriverName;
171+
$InUseByDevices = $PnpDevices | Where-Object {
172+
$PnpDeviceInfs[$_.InstanceId] -eq $DriverName;
173+
}
151174

152-
$Local:Message = @"
153-
Message from AMT
175+
Invoke-Error @"
176+
Driver: $DriverName is blocking the upgrade to Windows 11, see the details below:
177+
In Use By Devices: $($InUseByDevices | Select-Object -ExpandProperty FriendlyName -Join ', ')
178+
Driver Details: $DriverDetails
179+
"@
180+
}
181+
}
182+
}
183+
}
154184

155-
Your PC needs to restart to install Windows 11.
156-
Please save your work and close all applications before proceeding.
185+
$Script:LogPhaseReader = @{
186+
LastKnownPhase = 'Unknown';
187+
LastKnownPhaseTime = [DateTime]::Now;
188+
189+
LinesRead = 0;
190+
ReadHandle = $null;
191+
};
192+
function Get-CurrentUpgradePhase {
193+
$Path = "C:\`$WINDOWS.~BT\Sources\Panther\setupact.log"
194+
$PrefixRegex = '(^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}), Info\s+MOUPG\s+';
195+
$Regex1 = $PrefixRegex + 'Setup phase change: \[(.+?)\] -> \[(.+?)\]$';
196+
$Regex2 = $PrefixRegex + 'SetupManager::DetermineSetupPhase: CurrentSetupPhase \[(.+?)\]$';
197+
198+
# Approx 400k lines in total on a clean install.
199+
200+
if (-not (Test-Path $Path)) {
201+
return $null; # Not started yet.
202+
}
157203

158-
This will take place in 30 minutes at $([DateTime]::Now.AddMinutes(30).ToString('hh:mm tt')) or reboot immediately if you choose to do so.
159-
"@;
204+
if ($null -eq $Script:LogPhaseReader.ReadHandle) {
205+
$Script:LogPhaseReader.ReadHandle = [System.IO.StreamReader]::new($Path);
206+
}
160207

161-
$Local:Message | . msg * /TIME:3600;
162-
} else {
163-
$FailureData = Get-RegistryKey $RegPath 'FailureData';
164-
$FailureDetails = Get-RegistryKey $RegPath 'FailureDetails';
165-
166-
Invoke-Error "Windows 11 upgrade failed, see the details below:";
167-
Invoke-Error "Failure Data: $FailureData";
168-
Invoke-Error "Failure Details: $FailureDetails";
169-
170-
# $ErrorCode = $FailureDetails | Select-String -Pattern 'ErrorCode = (0x\d+),' | ForEach-Object { $_.Matches.Groups[1].Value };
171-
# $ScanResultFile = "$env:SystemDrive\`$WINDOWS.~BT\Sources\Panther\ScanResult.xml";
172-
$ScanResultFile = "C:\Users\JamesDraycott\AppData\Local\Temp\ScanResult.xml";
173-
if (Test-Path $ScanResultFile) {
174-
Invoke-Verbose 'Checking for blocking drivers...';
175-
176-
$PnpDevices = Get-PnpDevice -PresentOnly;
177-
$PnpDeviceInfs = @{};
178-
$PnpDevices | ForEach-Object {
179-
$InfPath = $_ | Get-PnpDeviceProperty -KeyName 'DEVPKEY_Device_DriverInfPath';
180-
$PnpDeviceInfs[$_.InstanceId] = $InfPath.Data;
181-
}
208+
for ($i = $Script:LogPhaseReader.LinesRead; $i -lt $LogPhaseReader.Handle.BaseStream.Length) {
209+
$Line = $Script:LogPhaseReader.ReadHandle.ReadLine();
210+
$Script:LogPhaseReader.LinesRead++;
182211

183-
$DriverPackages = (Select-Xml -Path $ScanResultFile -XPath '/*').Node.DriverPackages.DriverPackage;
184-
$DriverPackages | ForEach-Object {
185-
$Driver = $_;
186-
if ($Driver.BlockMigration -eq 'True') {
187-
$DriverName = $Driver.Inf;
188-
$DriverDetails = Get-WindowsDriver -Online -Driver $DriverName;
189-
$InUseByDevices = $PnpDevices | Where-Object {
190-
$PnpDeviceInfs[$_.InstanceId] -eq $DriverName;
191-
}
192-
193-
Invoke-Error @"
194-
Driver: $DriverName is blocking the upgrade to Windows 11, see the details below:
195-
In Use By Devices: $($InUseByDevices | Select-Object -ExpandProperty FriendlyName -Join ', ')
196-
Driver Details: $DriverDetails
197-
"@
198-
}
212+
# We know all the lines we care about are less than 140 characters long.
213+
if ($Line.Length -gt 140) {
214+
continue;
215+
}
216+
217+
if ($Line -match $Regex1) {
218+
$Time = [DateTime]::ParseExact($matches[1], "yyyy-MM-dd HH:mm:ss", $null);
219+
$OldPhase = $matches[1];
220+
$NewPhase = $matches[2];
221+
222+
if ($NewPhase -ne $Script:LogPhaseReader.LastKnownPhase) {
223+
Invoke-Info "Setup phase changed from $OldPhase to $NewPhase at $Time";
224+
$Script:LogPhaseReader.LastKnownPhase = $NewPhase;
225+
$Script:LogPhaseReader.LastKnownPhaseTime = $Time;
226+
}
227+
} elseif ($Line -match $Regex2) {
228+
$Time = [DateTime]::ParseExact($matches[1], "yyyy-MM-dd HH:mm:ss", $null);
229+
$NewPhase = $matches[2];
230+
231+
if ($NewPhase -ne $Script:LogPhaseReader.LastKnownPhase) {
232+
Invoke-Info "Setup phase changed to $NewPhase at $Time";
233+
$Script:LogPhaseReader.LastKnownPhase = $NewPhase;
234+
$Script:LogPhaseReader.LastKnownPhaseTime = $Time;
199235
}
200236
}
201237
}
238+
239+
return $Script:LogPhaseReader.LastKnownPhase;
202240
}
203241

204242
function Update-ToWin11 {
@@ -225,20 +263,42 @@ function Update-ToWin11 {
225263
$Status = Get-WindowsOptionalFeature -FeatureName $_ -Online;
226264
if ($Status.State -eq 'Enabled' -or $Status.State -eq 'EnablePending') {
227265
Invoke-Info "Disabling feature: $_";
228-
Disable-WindowsOptionalFeature -FeatureName $_ -Online;
266+
Disable-WindowsOptionalFeature -FeatureName $_ -Online -NoRestart;
229267
}
230268
}
231269

232-
# TODO - Maybe use the processes resource monitor to determine if its downloading, copying or what?
233-
Start-Process -FilePath $Private:OutputFile -ArgumentList "/QuietInstall /SkipEULA /auto upgrade /UninstallUponUpgrade /copylogs $Private:Dir" -Wait;
270+
$UpgradeJob = Start-Job -ScriptBlock {
271+
Start-Process -FilePath $Using:OutputFile -ArgumentList "/QuietInstall","/SkipEULA","/auto","upgrade","/UninstallUponUpgrade","/copylogs $Using:Dir" -Wait;
272+
};
273+
274+
while ($UpgradeJob.Finished -eq $False) {
275+
# TODO - This can probably find errors too.
276+
$CurrentPhase = Get-CurrentUpgradePhase;
277+
if ($CurrentPhase -eq 'SetupPhasePostFinalize') {
278+
Invoke-Info 'Windows 11 upgrade is complete, notifying user & exiting...';
279+
$Local:Message = @"
280+
Message from AMT
281+
282+
Your PC needs to restart to install Windows 11.
283+
Please save your work and close all applications before proceeding.
284+
285+
This will take place in 30 minutes at $([DateTime]::Now.AddMinutes(30).ToString('hh:mm tt')) or reboot immediately if you choose to do so.
286+
"@;
287+
288+
$Local:Message | . msg * /TIME:3600;
289+
290+
return;
291+
}
292+
293+
Start-Sleep -Seconds 5;
294+
}
234295

235-
Write-SetupError $True;
296+
Write-SetupError;
236297
} catch {
237-
Invoke-Error "There was an error upgrading to Windows 11, please check the logs for more information inside $Private:Dir";
238-
Write-SetupError $False;
298+
Write-SetupError;
239299
$PSCmdlet.ThrowTerminatingError($_);
240300
}
241301
}
242302
}
243303

244-
Export-ModuleMember -Function 'Update-ToWin11', 'Test-CanUpgrade', 'Get-SupportedProcessor', 'Write-SetupError';
304+
Export-ModuleMember -Function 'Update-ToWin11', 'Test-CanUpgrade';

0 commit comments

Comments
 (0)