Add an update-status tool and copy numbered scripts to Desktop\Tools
80-Get-UpdateStatus-Admin.ps1 prints whether the Windows 11 guest is upgrading or downloading updates (policy, pending markers, services, processes, BITS jobs, update events, network use); the .bat launcher elevates and keeps the window open. The seed ISO and Configure-Windows.ps1 now take every NN-*.bat and NN-*.ps1, and the first-logon copy goes to Desktop\Tools instead of Desktop\Installers. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fccc7986d9
commit
72af276309
5 files changed
+127
-5
No files matched your search
@@ -0,0 +1,14 @@
|
||||
@echo off
|
||||
setlocal
|
||||
title Windows update status
|
||||
|
||||
fltmc >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo Requesting administrator rights...
|
||||
powershell.exe -NoProfile -Command "Start-Process -FilePath '%~f0' -Verb RunAs"
|
||||
exit /b
|
||||
)
|
||||
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp080-Get-UpdateStatus-Admin.ps1"
|
||||
echo.
|
||||
pause
|
||||
@@ -0,0 +1,104 @@
|
||||
# Prints whether the guest is upgrading or downloading updates.
|
||||
# Read-only. Run as Administrator for complete results (BITS jobs of all users,
|
||||
# command lines of system processes); a standard user gets partial output.
|
||||
param(
|
||||
[int]$EventCount = 8
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
|
||||
function Write-Section([string]$Title) {
|
||||
Write-Host ''
|
||||
Write-Host "== $Title =="
|
||||
}
|
||||
|
||||
function Out-Text($Value) {
|
||||
$text = ($Value | Out-String).TrimEnd()
|
||||
if ($text) { Write-Host $text } else { Write-Host '(none)' }
|
||||
}
|
||||
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$isAdmin = [Security.Principal.WindowsPrincipal]::new($identity).IsInRole(
|
||||
[Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
|
||||
Write-Section 'Host'
|
||||
Write-Host ('Time: {0}' -f (Get-Date))
|
||||
Write-Host ('Admin: {0}' -f $isAdmin)
|
||||
if (-not $isAdmin) {
|
||||
Write-Host 'Warning: not elevated, BITS jobs and process command lines may be incomplete.'
|
||||
}
|
||||
|
||||
Write-Section 'Operating system'
|
||||
Out-Text (Get-CimInstance Win32_OperatingSystem |
|
||||
Select-Object Caption, Version, BuildNumber, LastBootUpTime | Format-List)
|
||||
|
||||
Write-Section 'Update policy'
|
||||
$wuPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate'
|
||||
$props = 'NoAutoUpdate', 'NoAutoRebootWithLoggedOnUsers', 'ExcludeWUDriversInQualityUpdate',
|
||||
'TargetReleaseVersion', 'TargetReleaseVersionInfo'
|
||||
foreach ($item in @(@{ Path = "$wuPath\AU" }, @{ Path = $wuPath })) {
|
||||
$values = Get-ItemProperty -Path $item.Path -ErrorAction SilentlyContinue
|
||||
foreach ($name in $props) {
|
||||
if ($values -and $values.PSObject.Properties[$name]) {
|
||||
Write-Host ('{0,-34} {1}' -f $name, $values.$name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Section 'Upgrade / pending-reboot markers'
|
||||
$markers = [ordered]@{
|
||||
'C:\$WINDOWS.~BT (upgrade staging)' = Test-Path -LiteralPath 'C:\$WINDOWS.~BT'
|
||||
'C:\Windows\WinSxS\pending.xml' = Test-Path -LiteralPath 'C:\Windows\WinSxS\pending.xml'
|
||||
'WindowsUpdate RebootRequired' = Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired'
|
||||
'Component Based Servicing RebootPending' = Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending'
|
||||
}
|
||||
foreach ($key in $markers.Keys) {
|
||||
Write-Host ('{0,-42} {1}' -f $key, $markers[$key])
|
||||
}
|
||||
|
||||
Write-Section 'Update services'
|
||||
Out-Text (Get-Service wuauserv, UsoSvc, TrustedInstaller, bits, WaaSMedicSvc, DoSvc `
|
||||
-ErrorAction SilentlyContinue |
|
||||
Select-Object Name, Status, StartType | Format-Table -AutoSize)
|
||||
|
||||
Write-Section 'Update scheduled tasks'
|
||||
Out-Text (Get-ScheduledTask `
|
||||
-TaskPath '\Microsoft\Windows\UpdateOrchestrator\*', '\Microsoft\Windows\WindowsUpdate\*' `
|
||||
-ErrorAction SilentlyContinue |
|
||||
Select-Object TaskName, State | Format-Table -AutoSize)
|
||||
|
||||
Write-Section 'Update-related processes'
|
||||
$pattern = 'setup|TiWorker|TrustedInstaller|UsoClient|MoSetup|SetupHost|SetupPrep|MusNotification|wusa|dism|EdgeUpdate|Installer'
|
||||
Out-Text (Get-CimInstance Win32_Process |
|
||||
Where-Object { $_.Name -match $pattern -or $_.CommandLine -match 'wuauserv|UsoSvc|DoSvc' } |
|
||||
Select-Object ProcessId, Name, CreationDate, CommandLine | Format-List)
|
||||
|
||||
Write-Section 'BITS download jobs'
|
||||
Out-Text (Get-BitsTransfer -AllUsers -ErrorAction SilentlyContinue |
|
||||
Select-Object DisplayName, JobState, BytesTransferred, BytesTotal | Format-Table -AutoSize)
|
||||
|
||||
Write-Section "Windows Update events (last $EventCount)"
|
||||
Out-Text (Get-WinEvent -LogName 'Microsoft-Windows-WindowsUpdateClient/Operational' `
|
||||
-MaxEvents $EventCount -ErrorAction SilentlyContinue |
|
||||
Select-Object TimeCreated, Id, Message | Format-List)
|
||||
|
||||
Write-Section 'Established HTTP/HTTPS connections by process'
|
||||
Out-Text (Get-NetTCPConnection -State Established -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.RemotePort -in 80, 443 } |
|
||||
Group-Object OwningProcess |
|
||||
ForEach-Object {
|
||||
$process = Get-Process -Id $_.Name -ErrorAction SilentlyContinue
|
||||
[pscustomobject]@{ Pid = $_.Name; Process = $process.Name; Connections = $_.Count }
|
||||
} | Format-Table -AutoSize)
|
||||
|
||||
Write-Section 'Network receive rate (2 samples, 2 s apart)'
|
||||
$counter = Get-Counter '\Network Interface(*)\Bytes Received/sec' `
|
||||
-SampleInterval 2 -MaxSamples 2 -ErrorAction SilentlyContinue
|
||||
if ($counter) {
|
||||
Out-Text ($counter[-1].CounterSamples |
|
||||
Select-Object InstanceName, @{ n = 'KBps'; e = { [int]($_.CookedValue / 1024) } } |
|
||||
Format-Table -AutoSize)
|
||||
} else {
|
||||
Write-Host '(performance counters unavailable)'
|
||||
}
|
||||
@@ -144,12 +144,14 @@ function Disable-AutomaticUpdate {
|
||||
}
|
||||
|
||||
function Copy-InstallerScripts {
|
||||
$scripts = @(Get-ChildItem -Path $PSScriptRoot -Filter '*-Install-*.bat' -File)
|
||||
# Numbered scripts (NN-*.bat / NN-*.ps1: installers and tools) go to Desktop\Tools.
|
||||
$scripts = @(Get-ChildItem -Path $PSScriptRoot -File |
|
||||
Where-Object { $_.Name -match '^\d\d-.*\.(bat|ps1)$' })
|
||||
if ($scripts.Count -eq 0) {
|
||||
throw "No installer batch files were found next to $PSCommandPath."
|
||||
}
|
||||
|
||||
$target = Join-Path ([Environment]::GetFolderPath('Desktop')) 'Installers'
|
||||
$target = Join-Path ([Environment]::GetFolderPath('Desktop')) 'Tools'
|
||||
New-Item -ItemType Directory -Path $target -Force | Out-Null
|
||||
Copy-Item -Path $scripts.FullName -Destination $target -Force
|
||||
}
|
||||
|
||||
@@ -43,9 +43,10 @@ if [[ -n "$ssh_key" ]]; then
|
||||
cp -- "$ssh_key" "$work_dir/authorized_keys"
|
||||
seed_files+=("$work_dir/authorized_keys")
|
||||
fi
|
||||
# Numbered installer scripts (10-Install-Git-Admin.bat, 20-..., in install order).
|
||||
# Numbered scripts: installers (10-Install-Git-Admin.bat, 20-..., in install order)
|
||||
# and tools (80-Get-UpdateStatus-Admin.bat/.ps1); all land in Desktop\Tools.
|
||||
shopt -s nullglob
|
||||
seed_files+=("$script_dir"/[0-9][0-9]-*.bat)
|
||||
seed_files+=("$script_dir"/[0-9][0-9]-*.bat "$script_dir"/[0-9][0-9]-*.ps1)
|
||||
shopt -u nullglob
|
||||
|
||||
mkdir -p "$(dirname -- "$output")"
|
||||
|
||||
Reference in new issue
Block a user