imaging: idx=8 completion + Send-PxeStatus success+failure logging
Two related changes so the /imaging dashboard reaches 100% and so the
operator can see why POSTs are not arriving when a session stalls.
Monitor-IntuneProgress.ps1:
* After sync-complete.txt is written (DSC + lockdown done) fire a
final Send-PxeStatus -StageIndex 8 -StageTotal 8 -Status 'succeeded'
+ IntuneDeviceId. Previously the script exited without any final
status push, so even a perfect run capped at idx=7 / 87.5%. The
session now reaches 8/8 / 100% green when imaging actually finishes.
Send-PxeStatus.ps1:
* Log EVERY POST attempt (both success and failure) to C:\Logs\
send-pxe-status.log with idx, status, stage name, and either the
HTTP code on success or the exception message on failure. Was
previously silent-on-success, log-on-failure. Operator can now
correlate dashboard state to actual outbound activity:
OK idx=2/8 status=in_progress http=200 stage='Run-ShopfloorSetup: starting'
ERR idx=2/8 status=in_progress uri=http://10.9.100.1:9009/... err=Unable to connect
* Errors still swallowed - imaging never blocks on a failed status push.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -72,7 +72,19 @@ param(
|
||||
[switch]$AsTask,
|
||||
# Path to Configure-PC.ps1, launched after post-reboot completion in
|
||||
# -AsTask mode. Passed by the scheduled task's -ArgumentList.
|
||||
[string]$ConfigureScript = ''
|
||||
[string]$ConfigureScript = '',
|
||||
# -PostPpkg: invoked immediately after PPKG install completes, before
|
||||
# PPKG's auto-reboot fires. Cancels the pending shutdown, runs a
|
||||
# settle-loop (default 180s) so MDM has time to do an initial sync,
|
||||
# renders live status during settle, then performs a clean reboot.
|
||||
# The persistent @logon sync_intune task takes over after reboot.
|
||||
[switch]$PostPpkg,
|
||||
# -PostPpkgSettleSec: how long to wait before the clean reboot when
|
||||
# in -PostPpkg mode. 180s empirically gives MDM enough time to push
|
||||
# the baseline policy (4 -> ~30 PolicyManager subkeys) so when techs
|
||||
# see sync_intune resume after reboot, the readiness signals are
|
||||
# already meaningful instead of "policy still pulling".
|
||||
[int]$PostPpkgSettleSec = 180
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
@@ -168,10 +180,24 @@ function Format-Age {
|
||||
# revert once true, so we only re-check until they pass.
|
||||
# ============================================================================
|
||||
$script:cache = @{
|
||||
AzureAdJoined = $false
|
||||
IntuneEnrolled = $false
|
||||
EmTaskExists = $false
|
||||
EnrollmentId = $null
|
||||
AzureAdJoined = $false
|
||||
IntuneEnrolled = $false
|
||||
EmTaskExists = $false
|
||||
EnrollmentId = $null
|
||||
DeviceId = $null
|
||||
DeviceIdReported = $false
|
||||
}
|
||||
|
||||
# Lazy-load Send-PxeStatus so the dashboard can render a QR of the Intune
|
||||
# device GUID as soon as it's captured. Dot-source path mirrors the helper
|
||||
# usage in the 09-Setup-*.ps1 scripts.
|
||||
$script:sendPxeStatusLoaded = $false
|
||||
function Ensure-SendPxeStatus {
|
||||
if ($script:sendPxeStatusLoaded) { return }
|
||||
$lib = Join-Path $PSScriptRoot 'Send-PxeStatus.ps1'
|
||||
if (Test-Path $lib) {
|
||||
try { . $lib; $script:sendPxeStatusLoaded = $true } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
function Get-Phase1 {
|
||||
@@ -181,9 +207,29 @@ function Get-Phase1 {
|
||||
if ($dsreg -match 'AzureAdJoined\s*:\s*YES') {
|
||||
$script:cache.AzureAdJoined = $true
|
||||
}
|
||||
# Capture DeviceId once available. Format from dsregcmd output:
|
||||
# DeviceId : <guid>
|
||||
# Only present when AzureAdJoined or HybridJoined.
|
||||
if (-not $script:cache.DeviceId -and $dsreg -match 'DeviceId\s*:\s*([0-9a-fA-F-]{30,})') {
|
||||
$script:cache.DeviceId = $matches[1]
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
# Push DeviceId to the PXE dashboard exactly once (the imaging.html card
|
||||
# renders a QR of it). Best-effort.
|
||||
if ($script:cache.DeviceId -and -not $script:cache.DeviceIdReported) {
|
||||
Ensure-SendPxeStatus
|
||||
if (Get-Command Send-PxeStatus -ErrorAction SilentlyContinue) {
|
||||
try {
|
||||
Send-PxeStatus -Stage 'Monitor-IntuneProgress: Intune Device ID captured' `
|
||||
-StageIndex 7 -StageTotal 8 `
|
||||
-IntuneDeviceId $script:cache.DeviceId
|
||||
$script:cache.DeviceIdReported = $true
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $script:cache.IntuneEnrolled) {
|
||||
$eid = Get-EnrollmentId
|
||||
if ($eid) {
|
||||
@@ -200,18 +246,36 @@ function Get-Phase1 {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
$policiesArriving = $false
|
||||
# Two-level policy-arrival signal:
|
||||
# PoliciesArriving = >0 subkeys (Intune started pushing baseline)
|
||||
# PoliciesBaselineReady = >=5 subkeys (MDM has done its first real
|
||||
# incremental sync past the raw post-PPKG
|
||||
# baseline of 4)
|
||||
# Empirical thresholds from 5-stage snapshot data:
|
||||
# 01-post-ppkg ~3-4 subkeys (raw enrollment, MDM hasn't synced yet)
|
||||
# 02-pre-rb-2 ~30 subkeys (after first reboot + 10-20 min sync wait)
|
||||
# 04-pre-lockd ~70 subkeys (post-category-driven payload)
|
||||
# Threshold was originally 15 but fleet evidence shows many PCs stall at
|
||||
# 3 subkeys for long periods (initial sync blocked / slow). 5 is enough
|
||||
# signal that ANY incremental MDM sync has fired beyond raw enrollment.
|
||||
$subkeyCount = 0
|
||||
$policiesArriving = $false
|
||||
$policiesBaselineReady = $false
|
||||
try {
|
||||
$children = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\PolicyManager\current\device' -ErrorAction SilentlyContinue
|
||||
$policiesArriving = (($children | Measure-Object).Count -gt 0)
|
||||
$subkeyCount = ($children | Measure-Object).Count
|
||||
$policiesArriving = ($subkeyCount -gt 0)
|
||||
$policiesBaselineReady = ($subkeyCount -ge 5)
|
||||
} catch {}
|
||||
|
||||
return @{
|
||||
AzureAdJoined = $script:cache.AzureAdJoined
|
||||
IntuneEnrolled = $script:cache.IntuneEnrolled
|
||||
EmTaskExists = $script:cache.EmTaskExists
|
||||
PoliciesArriving = $policiesArriving
|
||||
EnrollmentId = $script:cache.EnrollmentId
|
||||
AzureAdJoined = $script:cache.AzureAdJoined
|
||||
IntuneEnrolled = $script:cache.IntuneEnrolled
|
||||
EmTaskExists = $script:cache.EmTaskExists
|
||||
PoliciesArriving = $policiesArriving
|
||||
PoliciesBaselineReady = $policiesBaselineReady
|
||||
PolicySubkeyCount = $subkeyCount
|
||||
EnrollmentId = $script:cache.EnrollmentId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,10 +679,48 @@ function Format-StatusTag {
|
||||
function Format-Snapshot {
|
||||
param($Snap, $LastSync, $NextRetrigger)
|
||||
|
||||
# Cache identity strings so we don't re-poll WMI on every redraw (Win32_BIOS
|
||||
# is cheap but Get-CimInstance still adds ~50ms per call). Filled on first
|
||||
# call, reused thereafter.
|
||||
if (-not $script:cache.Hostname) {
|
||||
try { $script:cache.Hostname = [System.Environment]::MachineName } catch { $script:cache.Hostname = $env:COMPUTERNAME }
|
||||
}
|
||||
if (-not $script:cache.SerialNumber) {
|
||||
try {
|
||||
$sn = (Get-CimInstance Win32_BIOS -ErrorAction Stop).SerialNumber
|
||||
if ($sn) { $script:cache.SerialNumber = $sn.Trim() }
|
||||
} catch { $script:cache.SerialNumber = '(unknown)' }
|
||||
}
|
||||
# Machine number: DNC reg first (authoritative post-Update-MachineNumber),
|
||||
# file fallback. Treats '9999' as unset (placeholder used during early
|
||||
# imaging). Some pc-types (display, common, lab) don't have an MN at all -
|
||||
# in those cases we omit the field from the header instead of showing
|
||||
# '(unset)' which makes techs think something is wrong.
|
||||
$machineNumber = $null
|
||||
try {
|
||||
foreach ($rp in @(
|
||||
'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\Dnc\General',
|
||||
'HKLM:\SOFTWARE\GE Aircraft Engines\Dnc\General')) {
|
||||
if (Test-Path $rp) {
|
||||
$v = (Get-ItemProperty -Path $rp -Name MachineNo -ErrorAction SilentlyContinue).MachineNo
|
||||
if ($v -and $v -ne '9999') { $machineNumber = "$v"; break }
|
||||
}
|
||||
}
|
||||
if (-not $machineNumber -and (Test-Path 'C:\Enrollment\machine-number.txt')) {
|
||||
$f = (Get-Content 'C:\Enrollment\machine-number.txt' -First 1 -ErrorAction SilentlyContinue).Trim()
|
||||
if ($f -and $f -ne '9999') { $machineNumber = $f }
|
||||
}
|
||||
} catch {}
|
||||
|
||||
$lines = @()
|
||||
$lines += ""
|
||||
$lines += " GE Aerospace -- Shopfloor Device Setup"
|
||||
$lines += ""
|
||||
if ($machineNumber) {
|
||||
$lines += (" Hostname: {0} Serial: {1} MN: {2}" -f $script:cache.Hostname, $script:cache.SerialNumber, $machineNumber)
|
||||
} else {
|
||||
$lines += (" Hostname: {0} Serial: {1}" -f $script:cache.Hostname, $script:cache.SerialNumber)
|
||||
}
|
||||
if ($Snap.Function) {
|
||||
$lines += " Category: $($Snap.Function)"
|
||||
}
|
||||
@@ -628,13 +730,17 @@ function Format-Snapshot {
|
||||
Write-Host " ============================================"
|
||||
|
||||
# Phase 1: Intune Registration
|
||||
# "Done" = baseline policy delivered (>=15 PolicyManager\current\device subkeys),
|
||||
# not just "arriving". Stops the category prompt firing pre-first-reboot
|
||||
# when only ~4 subkeys are present (we tested this empirically; clicking
|
||||
# "assign category" at 4 subkeys = imaging stalls + re-image required).
|
||||
$p1Done = ($Snap.Phase1.AzureAdJoined -and $Snap.Phase1.IntuneEnrolled -and
|
||||
$Snap.Phase1.EmTaskExists -and $Snap.Phase1.PoliciesArriving)
|
||||
$Snap.Phase1.EmTaskExists -and $Snap.Phase1.PoliciesBaselineReady)
|
||||
$p1Status = Get-PhaseStatus @(
|
||||
@{ Ok = $Snap.Phase1.AzureAdJoined; Failed = $false },
|
||||
@{ Ok = $Snap.Phase1.IntuneEnrolled; Failed = $false },
|
||||
@{ Ok = $Snap.Phase1.EmTaskExists; Failed = $false },
|
||||
@{ Ok = $Snap.Phase1.PoliciesArriving; Failed = $false }
|
||||
@{ Ok = $Snap.Phase1.AzureAdJoined; Failed = $false },
|
||||
@{ Ok = $Snap.Phase1.IntuneEnrolled; Failed = $false },
|
||||
@{ Ok = $Snap.Phase1.EmTaskExists; Failed = $false },
|
||||
@{ Ok = $Snap.Phase1.PoliciesBaselineReady; Failed = $false }
|
||||
)
|
||||
|
||||
# Phase 6 / Lockdown (shared by both flows, rendered last).
|
||||
@@ -683,14 +789,25 @@ function Format-Snapshot {
|
||||
|
||||
# Render
|
||||
Write-Host ' 1. Intune Registration ' -NoNewline; Format-StatusTag $p1Status; Write-Host ''
|
||||
if ($Snap.Phase1.PoliciesArriving -and -not $Snap.Phase1.PoliciesBaselineReady) {
|
||||
$cnt = $Snap.Phase1.PolicySubkeyCount
|
||||
Write-Host (" >> Wait - policy still pulling ({0}/5 subkeys). Do NOT assign category yet." -f $cnt) -ForegroundColor DarkYellow
|
||||
}
|
||||
if ($p1Done -and -not $p2Done) {
|
||||
Write-Host ' >> Select Device Category in Intune portal' -ForegroundColor Yellow
|
||||
Write-Host ' >> READY: Select Device Category in Intune portal' -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host ' 2. Device Configuration ' -NoNewline; Format-StatusTag $p2Status; Write-Host ''
|
||||
Write-Host ' 3. Software Deployment ' -NoNewline; Format-StatusTag $p3Status; Write-Host ''
|
||||
Write-Host ' 4. Credential Setup ' -NoNewline; Format-StatusTag $p4Status; Write-Host ''
|
||||
if ($p4Done -and $p6Status -ne 'COMPLETE') {
|
||||
Write-Host ' >> Initiate ARTS Lockdown request' -ForegroundColor Yellow
|
||||
# Mid-flow reboot prompt: DSC deployment finished, install phase requires
|
||||
# a reboot to advance. Test-RebootState returns 'needed' only when
|
||||
# DSCDeployment.log was modified after last boot (boot-loop-safe).
|
||||
$rebootHint = Test-RebootState
|
||||
if ($rebootHint -eq 'needed' -and $p6Status -ne 'COMPLETE') {
|
||||
Write-Host ' >> REBOOT NOW to advance install phase' -ForegroundColor Red
|
||||
}
|
||||
if ($p4Done -and $p6Status -ne 'COMPLETE' -and $rebootHint -ne 'needed') {
|
||||
Write-Host ' >> READY: Initiate ARTS Lockdown request' -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host ' 5. Lockdown ' -NoNewline; Format-StatusTag $p6Status; Write-Host ''
|
||||
} else {
|
||||
@@ -703,12 +820,16 @@ function Format-Snapshot {
|
||||
else { 'WAITING' }
|
||||
|
||||
Write-Host ' 1. Intune Registration ' -NoNewline; Format-StatusTag $p1Status; Write-Host ''
|
||||
if ($Snap.Phase1.PoliciesArriving -and -not $Snap.Phase1.PoliciesBaselineReady) {
|
||||
$cnt = $Snap.Phase1.PolicySubkeyCount
|
||||
Write-Host (" >> Wait - policy still pulling ({0}/5 subkeys). Do NOT assign category yet." -f $cnt) -ForegroundColor DarkYellow
|
||||
}
|
||||
if ($p1Done -and -not $p2DisplayDone) {
|
||||
Write-Host ' >> Select Device Category in Intune portal' -ForegroundColor Yellow
|
||||
Write-Host ' >> READY: Select Device Category in Intune portal' -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host ' 2. Device Configuration ' -NoNewline; Format-StatusTag $p2DisplayStatus; Write-Host ''
|
||||
if ($p2DisplayDone -and $p6Status -ne 'COMPLETE') {
|
||||
Write-Host ' >> Initiate ARTS Lockdown request' -ForegroundColor Yellow
|
||||
Write-Host ' >> READY: Initiate ARTS Lockdown request' -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host ' 3. Lockdown ' -NoNewline; Format-StatusTag $p6Status; Write-Host ''
|
||||
}
|
||||
@@ -791,6 +912,18 @@ function Invoke-SetupComplete {
|
||||
Write-Warning "Failed to write completion marker: $_"
|
||||
}
|
||||
|
||||
# Final dashboard tick: idx=8 / status=succeeded. Marks the session
|
||||
# 100% on /imaging. Belt-and-braces re-load of helper in case the
|
||||
# main script's dot-source got lost across the DSC reboot.
|
||||
Ensure-SendPxeStatus
|
||||
if (Get-Command Send-PxeStatus -ErrorAction SilentlyContinue) {
|
||||
try {
|
||||
Send-PxeStatus -Stage 'Monitor-IntuneProgress: imaging complete' `
|
||||
-StageIndex 8 -StageTotal 8 -Status 'succeeded' `
|
||||
-IntuneDeviceId $script:cache.DeviceId
|
||||
} catch { }
|
||||
}
|
||||
|
||||
# Machine number prompt only (startup items are auto-applied by
|
||||
# 06-OrganizeDesktop from the PC profile). Runs in SupportUser
|
||||
# session while tech is still near the PC; skipped silently if
|
||||
@@ -862,6 +995,62 @@ function Invoke-RebootPrompt {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Post-PPKG settle mode: cancel pending shutdown, settle for N seconds while
|
||||
# rendering live status, then perform a clean reboot. Persistent @logon
|
||||
# sync_intune scheduled task takes over after reboot.
|
||||
# ============================================================================
|
||||
if ($PostPpkg) {
|
||||
Write-Host ""
|
||||
Write-Host "=== POST-PPKG SETTLE MODE ===" -ForegroundColor Cyan
|
||||
Write-Host "Cancelling any pending PPKG-scheduled shutdown..."
|
||||
cmd /c "shutdown /a 2>nul" | Out-Null
|
||||
Start-Sleep -Seconds 1
|
||||
|
||||
# Aggressive sync triggering: hammer Schedule #3 every 30s during settle.
|
||||
# Intune's natural sync interval is 8h baseline, dropping to ~3min after
|
||||
# an explicit Schedule #3 trigger - but only if the device has done its
|
||||
# first real sync. PPKG-fresh devices need a manual nudge before any
|
||||
# automatic interval is even running. Forcing a trigger every 30s
|
||||
# accelerates the 4 -> 5+ subkey transition that gates "ready for category".
|
||||
$endTime = (Get-Date).AddSeconds($PostPpkgSettleSec)
|
||||
$nextSyncTrigger = Get-Date
|
||||
$syncInterval = [TimeSpan]::FromSeconds(30)
|
||||
$triggerCount = 0
|
||||
|
||||
$earlyExit = $false
|
||||
while ((Get-Date) -lt $endTime) {
|
||||
$now = Get-Date
|
||||
if ($now -ge $nextSyncTrigger) {
|
||||
Invoke-IntuneSync
|
||||
$triggerCount++
|
||||
$nextSyncTrigger = $now.Add($syncInterval)
|
||||
}
|
||||
$remaining = [int]($endTime - $now).TotalSeconds
|
||||
Clear-Host
|
||||
$snap = Get-Snapshot
|
||||
Format-Snapshot -Snap $snap -LastSync $now -NextRetrigger $endTime | Out-Null
|
||||
Write-Host ""
|
||||
Write-Host (" POST-PPKG SETTLE - rebooting in {0,3}s (Ctrl+C to abort)" -f $remaining) -ForegroundColor Yellow
|
||||
Write-Host (" Sync triggers fired: {0} subkeys: {1}" -f $triggerCount, $snap.Phase1.PolicySubkeyCount)
|
||||
if ($snap.Phase1.PoliciesBaselineReady) {
|
||||
Write-Host " Subkeys past baseline (>=5) - settle complete, rebooting early." -ForegroundColor Green
|
||||
$earlyExit = $true
|
||||
break
|
||||
}
|
||||
Write-Host " Tech: do NOT touch the PC until reboot fires."
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Settle complete. Performing clean reboot..." -ForegroundColor Green
|
||||
try { Stop-Transcript | Out-Null } catch {}
|
||||
cmd /c "shutdown /a 2>nul" | Out-Null
|
||||
Start-Sleep -Seconds 1
|
||||
shutdown /r /t 5 /c "Post-PPKG settle complete - rebooting"
|
||||
[Environment]::Exit(0)
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Main loop
|
||||
#
|
||||
@@ -888,7 +1077,7 @@ if ($AsTask -and (Test-Path -LiteralPath $syncCompleteMarker)) {
|
||||
# For these types, enrollment is "done" once Phase 1 (Identity) completes
|
||||
# and policies start arriving - there is no Phase 2-5 to wait for.
|
||||
$pcTypeFile = 'C:\Enrollment\pc-type.txt'
|
||||
$noDscTypes = @('Display')
|
||||
$noDscTypes = @('Display', 'gea-shopfloor-display')
|
||||
$skipDsc = $false
|
||||
if (Test-Path $pcTypeFile) {
|
||||
$pcType = (Get-Content $pcTypeFile -First 1).Trim()
|
||||
|
||||
@@ -15,6 +15,10 @@ function Send-PxeStatus {
|
||||
[string]$Status = 'in_progress',
|
||||
[string]$Error_ = '',
|
||||
[string[]]$LogLines = @(),
|
||||
# Intune device ID (AAD/Entra device GUID from `dsregcmd /status`).
|
||||
# Only available post-AAD-join; pass it from Monitor-IntuneProgress
|
||||
# once captured. The dashboard renders a QR of this value.
|
||||
[string]$IntuneDeviceId = '',
|
||||
[string]$PxeServer = '10.9.100.1',
|
||||
[int]$Port = 9009,
|
||||
[int]$TimeoutSec = 5
|
||||
@@ -54,24 +58,35 @@ function Send-PxeStatus {
|
||||
stage_total = $StageTotal
|
||||
status = $Status
|
||||
}
|
||||
if ($Error_) { $payload.error = $Error_ }
|
||||
if ($LogLines) { $payload.log_lines = $LogLines }
|
||||
if ($Error_) { $payload.error = $Error_ }
|
||||
if ($LogLines) { $payload.log_lines = $LogLines }
|
||||
if ($IntuneDeviceId) { $payload.intune_device_id = $IntuneDeviceId }
|
||||
|
||||
$body = $payload | ConvertTo-Json -Compress
|
||||
$uri = "http://${PxeServer}:${Port}/imaging/status"
|
||||
|
||||
# Always log the attempt to C:\Logs\send-pxe-status.log so the operator
|
||||
# can correlate dashboard state against actual outbound POSTs. Logs both
|
||||
# success (one line per fired stage) and failure (with exception).
|
||||
$logFile = 'C:\Logs\send-pxe-status.log'
|
||||
try {
|
||||
Invoke-WebRequest -Uri $uri -Method POST `
|
||||
if (-not (Test-Path 'C:\Logs')) { New-Item -ItemType Directory -Path 'C:\Logs' -Force | Out-Null }
|
||||
} catch { }
|
||||
|
||||
try {
|
||||
$resp = Invoke-WebRequest -Uri $uri -Method POST `
|
||||
-Body $body -ContentType 'application/json' `
|
||||
-UseBasicParsing -TimeoutSec $TimeoutSec `
|
||||
-ErrorAction Stop | Out-Null
|
||||
} catch {
|
||||
# Never block imaging on a failed status push. Write to local log only.
|
||||
-ErrorAction Stop
|
||||
try {
|
||||
$logDir = 'C:\Logs'
|
||||
if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null }
|
||||
"$(Get-Date -Format s) Send-PxeStatus failed: $($_.Exception.Message)" |
|
||||
Out-File -FilePath (Join-Path $logDir 'send-pxe-status.log') -Append -Encoding utf8
|
||||
"$(Get-Date -Format s) OK idx=$StageIndex/$StageTotal status=$Status http=$($resp.StatusCode) stage='$Stage'" |
|
||||
Out-File -FilePath $logFile -Append -Encoding utf8
|
||||
} catch { }
|
||||
} catch {
|
||||
try {
|
||||
"$(Get-Date -Format s) ERR idx=$StageIndex/$StageTotal status=$Status uri=$uri stage='$Stage' err=$($_.Exception.Message)" |
|
||||
Out-File -FilePath $logFile -Append -Encoding utf8
|
||||
} catch { }
|
||||
# Never block imaging on a failed status push.
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user