Upgrading enterprise
devices to Windows 11 at scale requires careful orchestration. Devices must
meet hardware requirements, have enough disk space, maintain user productivity,
and ensure minimal disruption. This Smart Windows 11 Upgrade solution
delivers a fully automated, resilient, and user-aware upgrade workflow using PsExec,
batch scripting, and PowerShell automation.
This implementation is
designed for enterprise environments, ensuring reliability, fallback
mechanisms, logging, and compliance checks before initiating the in-place
upgrade (IPU).
- 🚀 Solution Overview
This solution consists
of three major components:
- PsExec Launcher (Remote Execution Layer)
- Batch Script (Content Distribution &
Trigger Layer)
- PowerShell Scripts (Logic, Validation
& Upgrade Execution)
Together, they create
a robust and self-healing upgrade workflow.
- 🔹 1. Remote Execution Using PsExec
The process begins
with:
PowerShell
psexec @file.txt -s -h
-d -c -f DC_SmartUpgrade.bat
``
Show more lines
- Key Features:
- @file.txt → Targets multiple systems
- -s → Runs as SYSTEM (highest privilege)
- -h → Elevated execution
- -d → Non-blocking execution
- -c -f → Copies and overwrites the batch
script
- Purpose:
This enables mass
deployment of the upgrade trigger across endpoints without requiring manual
intervention.
- 🔹 2. Batch Script – Smart Content Staging
The batch script acts
as a resilient launcher and content distributor.
- Key Capabilities:
- ✅ Multi-Server Fallback
BAT
for %%S in
("%SERVER_SHARE1%" "%SERVER_SHARE2%"
"%SERVER_SHARE3%")
Show more lines
- Attempts multiple file shares
- Ensures availability even if one server is
down
- ✅ File Download Logic
Copies required files:
- PowerShell upgrade script
- ServiceUI (for user interaction)
- Reboot prompt script
- ✅ Local Execution
Runs PowerShell
locally:
BAT
start powershell.exe
-ExecutionPolicy Bypass -File script.ps1
Show more lines
- Benefit:
Ensures high
availability and fault tolerance in distributed environments.
- 🔹 3. PowerShell Core Script – Smart Upgrade Engine
This is the heart
of the solution, responsible for:
- Pre-check validation
- Disk cleanup
- Upgrade execution
- Logging and fallback handling
- ✅ Pre-Upgrade Eligibility Checks
The script validates
whether a system is ready for Windows 11:
- ✔ OS Version Check
Ensures device is
below Windows 11 threshold.
- ✔ Hardware Validation
- TPM 2.0 availability
- Secure Boot enabled
- UEFI mode verified
- ✔ Virtual Machine Handling
- Adjusted validation logic for VMs
- ✅ Reboot Coordination
Before starting
upgrade:
- Detects pending reboot states
- Checks active user sessions
- Behavior:
- No user → Immediate reboot
- User logged in → Show reboot prompt (10
min window)
- ✅ User Notification via ServiceUI
If a user is active:
- Uses ServiceUI.exe to display
prompts in user context
- Shows a friendly reboot warning
- Allows users to save work
- ✅ Intelligent Cleanup Mechanism
- Mandatory Cleanup
Always removes:
- Windows upgrade leftovers
- Temp folders
- Previous setup files
- Timestamp-Based Cleanup
Runs only if not
executed within 7 days:
- DISM restore health
- WMI recompilation
- Windows Update reset
- SCCM log cleanup
- ✅ Disk Space Validation
The script ensures:
PowerShell
Threshold = 30GB
Show more lines
- Upgrade proceeds only if sufficient disk
space exists
- Prevents upgrade failures due to low
storage
- ✅ Upgrade Execution Logic
- Primary Source: CCMCache
- Checks if setup.exe already exists
- Validates file completeness
- Fallback: Central Shares
- Copies upgrade files from multiple network
shares
- Ensures redundancy
- Upgrade Command:
PowerShell
/auto upgrade /quiet
/noreboot /dynamicupdate enable /compat ignorewarning
Show more lines
- Benefit:
Provides a silent,
fully automated in-place upgrade.
- ✅ Error Handling & Fallback Strategy
- Multiple share fallback for setup files
- Retry logic when errors occur
- Logs every step for troubleshooting
- ✅ Centralized Logging
Logs are stored in:
C:\ProgramData\GlobalClient\LogFiles
- Features:
- Detailed execution logs
- Upgrade progress tracking
- Error visibility
- Log Upload:
- Automatically copies logs to network
shares
- Includes SetupDiag output for
troubleshooting
- ✅ Resume Capability After Reboot
The script uses:
PowerShell
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
Show more lines
- Ensures the upgrade script resumes after
reboot
- Maintains state continuity
- 🔹 4. Reboot Prompt Script
A separate PowerShell
module handles user interaction.
- Features:
- Displays professional reboot popup
- Waits up to 10 minutes
- Captures user response
- Schedules reboot accordingly
- Behavior:
- User clicks OK → reboot scheduled
- No response → timeout handled gracefully
- 🔄 End-to-End Workflow
- PsExec triggers batch file remotely
- Batch script:
- Pulls files from available server
- Launches PowerShell script
- PowerShell script:
- Runs eligibility checks
- Cleans system
- Validates disk space
- Executes upgrade
- User is notified (if logged in)
- Upgrade runs silently
- Logs uploaded centrally
- System reboots and resumes if needed
- ✅ Key Advantages
- ✔ Enterprise-Ready
Designed for
large-scale deployment across thousands of devices
- ✔ Self-Healing
Multiple fallback
mechanisms for shares, files, and execution
- ✔ User-Aware
Ensures minimal
disruption with proper notifications
- ✔ Secure Execution
Runs under SYSTEM with
controlled privilege
- ✔ Optimized Performance
Cleanup and validation
reduce upgrade failures
- ✔ Fully Automated
No manual intervention
required once triggered
- 🏁 Conclusion
This Smart Windows 11
Upgrade solution demonstrates how PowerShell automation, PsExec
orchestration, and intelligent logic can transform a complex OS upgrade
into a controlled, scalable, and resilient process.
By combining
validation, cleanup, fallback strategies, and user interaction, it ensures:
- Higher success rates
- Better user experience
- Reduced operational overhead
This approach is ideal
for modern enterprises looking to accelerate Windows 11 adoption while
maintaining control and stability.
- ✅ Final Combined Search Description
Main Version:
Automate Windows 11 in-place upgrades using PowerShell, PsExec, and SCCM with
smart pre-check validation, cleanup, disk space checks, user reboot prompts,
centralized logging, and enterprise-ready fallback mechanisms.
BAT:
@echo off
setlocal
:: -------------------------------
:: Configuration
:: -------------------------------
set SERVER_SHARE1=\\server\MDT_LOGS$\AA_W11_IPU\IPURemoteTriggers
set SERVER_SHARE2=\\server\MDT_LOGS$\AA_W11_IPU\IPURemoteTriggers
set SERVER_SHARE3=\\server\MDT_LOGS$\AA_W11_IPU\IPURemoteTriggers
set LOCAL_PATH=%SystemRoot%\Temp\AA_W11_IPU
set PS1_FILE=DC_SmartUpgrade.ps1
set SERVICE_UI=ServiceUI.exe
set RebootPrompt=Show-RebootPrompt.ps1
set LOG_FILE=%LOCAL_PATH%\SmartUpgrade_Log.log
:: -------------------------------
:: Initialize log
:: -------------------------------
if not exist "%LOCAL_PATH%" mkdir "%LOCAL_PATH%"
echo %date% %time% - Starting SmartUpgrade >> "%LOG_FILE%"
:: -------------------------------
:: Copy required files from first accessible server
:: -------------------------------
set FILES=%PS1_FILE% %SERVICE_UI% %RebootPrompt%
set SERVER_FOUND=
for %%S in ("%SERVER_SHARE1%" "%SERVER_SHARE2%" "%SERVER_SHARE3%") do (
if exist "%%~S\%PS1_FILE%" (
echo %date% %time% - Found server share: %%~S >> "%LOG_FILE%"
for %%F in (%FILES%) do (
copy /Y "%%~S\%%F" "%LOCAL_PATH%\%%F" >> "%LOG_FILE%" 2>&1
if %ERRORLEVEL% EQU 0 (
echo %date% %time% - Copied %%F to %LOCAL_PATH% >> "%LOG_FILE%"
) else (
echo %date% %time% - ERROR copying %%F from %%~S >> "%LOG_FILE%"
)
)
set SERVER_FOUND=1
goto :CopyDone
) else (
echo %date% %time% - Server share %%~S not accessible or file missing >> "%LOG_FILE%"
)
)
:CopyDone
if not defined SERVER_FOUND (
echo %date% %time% - ERROR: Required files not found on any server share! >> "%LOG_FILE%"
exit /b 1
)
:: -------------------------------
:: Run the PS1 script locally using ServiceUI
:: -------------------------------
set PS_EXE=%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe
set PS_CMD=-WindowStyle Hidden -ExecutionPolicy Bypass -File "%LOCAL_PATH%\%PS1_FILE%"
echo %date% %time% - Launching SmartUpgrade via "%PS_EXE%" %PS_CMD%... >> "%LOG_FILE%"
start %PS_EXE% %PS_CMD%
if %ERRORLEVEL% EQU 0 (
echo %date% %time% - ServiceUI launched successfully >> "%LOG_FILE%"
) else (
echo %date% %time% - ERROR: Failed to launch ServiceUI >> "%LOG_FILE%"
)
echo %date% %time% - SmartUpgrade batch completed >> "%LOG_FILE%"
endlocal
exit /b %ERRORLEVEL%
<#
.SYNOPSIS
Shows a professional reboot prompt to the
logged-on user.
If no user session, system reboots
immediately.
#>
param(
[int]$TimeoutMinutes = 10
)
# ---------------- Logging
Setup ----------------
$LogDir = "C:\ProgramData\LogFiles"
$LogFile = "$LogDir\DC_Smart_Win11Upgrade.log"
if (!(Test-Path $LogDir)) {
New-Item -Path $LogDir -ItemType Directory -Force | Out-Null
}
function Write-Log {
param ([string]$Message)
$timestamp = Get-Date -Format "yyyy-MM-dd
HH:mm:ss"
$line = "$timestamp - $Message"
$line | Out-File -FilePath $LogFile -Append -Encoding UTF8
Write-Output $line
}
# ---------------- UI Setup
----------------
Add-Type -AssemblyName PresentationFramework
function Show-RebootPrompt {
param([int]$TimeoutMinutes)
$title = "System
Reboot Required"
$msg = "An
operating system upgrade has been installed and requires a restart.
Please save your work. The
system will reboot automatically in $TimeoutMinutes minutes."
#
Start the MessageBox in a background job (STA thread)
$job = Start-Job -ScriptBlock {
param($msg,$title)
Add-Type -AssemblyName PresentationFramework
#
Return the actual dialog result
return [System.Windows.MessageBox]::Show(
$msg,
$title,
'OK',
'Warning'
)
} -ArgumentList $msg,$title
# Wait
for user input or timeout
if (Wait-Job -Job $job -Timeout ($TimeoutMinutes*60)) {
$result = Receive-Job $job # Capture what the user clicked
Remove-Job $job
return $result
}
else {
#
Timeout → kill prompt and return null
Stop-Job $job
Remove-Job $job
return $null
}
}
# ---------------- Main
Execution ----------------
Write-Log "Checking
for active user session..."
$users = (quser 2>$null) | Where-Object {$_ -notmatch "USERNAME"}
if (-not $users) {
Write-Log "No
user session detected. Rebooting immediately..."
shutdown.exe /r /t 0 /c "System
rebooting to complete OS Upgrade."
exit 0
}
Write-Log "Active
user session detected. Displaying reboot prompt (max wait = $TimeoutMinutes minutes)..."
$result = Show-RebootPrompt -TimeoutMinutes $TimeoutMinutes
if ($result.Value -eq 'OK') {
Write-Log "User
responded to reboot prompt (clicked OK)."
Write-Log "Scheduling
reboot in $TimeoutMinutes minutes..."
shutdown.exe /r /t ($TimeoutMinutes*60) /c "System
reboot required to complete OS Upgrade"
Write-Log "Reboot
scheduled successfully."
}
else {
Write-Log "User
did not respond within $TimeoutMinutes minutes (dialog timeout). No forced reboot scheduled."
}
<#
.SYNOPSIS
- Reboot system only if OS Upgrade reboot
is pending.
- If no user logged on → immediate reboot
- If user logged on → 10 min warning then
reboot
#>
$LogDir = "C:\ProgramData\LogFiles"
$LogFile = "$LogDir\DC_Smart_Win11Upgrade.log"
$ServiceUI = Join-Path $PSScriptRoot "ServiceUI.exe"
$SetupCmd = "/auto upgrade /quiet /noreboot /dynamicupdate enable /compat
ignorewarning /eula accept /showoobe none"
$regPath1 = "HKLM:\SOFTWARE\OSDeploy"
$RegPath2 = "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager"
$ValueName = "PendingFileRenameOperations"
$regName = "Win11_PreUpgradeCleanup"
$ScriptPath= "$PSScriptRoot\DC_SmartUpgrade.ps1"
$Threshold = 30720 #
Threshold in MB to get free space of C: drive in MB
$fldr = "AA_W11_IPU"
$Logfldr = "AA_W11_IPU\logs"
$hostname = $env:COMPUTERNAME
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$destPath1 = "$env:SystemRoot\CCMCache"
$ShowRebootPrompt = Join-Path $PSScriptRoot "Show-RebootPrompt.ps1"
$CheckpointPath = "HKLM:\SOFTWARE\OSDeploy"
$ResumeCommand = "powershell.exe -ExecutionPolicy Bypass -File `"$ScriptPath`""
$RunOncePath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"
$setupsource = 'AA_W11_IPU\Win11_Upgrade23H2'
$slShares = @(
"\\server\MDT_LOGS$",
"\\
server \MDT_LOGS$",
"\\
server \MDT_LOGS$"
)
$setupshares = @(
"\\
server \MDT_LOGS$\AA_W11_IPU\Win11_Upgrade23H2",
"\\
server \MDT_LOGS$\AA_W11_IPU\Win11_Upgrade23H2",
"\\
server \MDT_LOGS$\AA_W11_IPU\Win11_Upgrade23H2"
)
$Arguments = @(
"-process:explorer.exe"
"$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
"-ExecutionPolicy
Bypass"
"-WindowStyle
Hidden"
"-File
`"$ShowRebootPrompt`""
"-TimeoutMinutes
10"
">>
`"$LogFile`" 2>&1"
) -join " "
# ---------------- Logging
Setup ----------------
if (!(Test-Path $LogDir)) {
New-Item -Path $LogDir -ItemType Directory -Force | Out-Null
}
function Write-Log {
param ([string]$Message)
$timestamp = Get-Date -Format "yyyy-MM-dd
HH:mm:ss"
$line = "$timestamp - $Message"
$line | Out-File -FilePath $LogFile -Append -Encoding UTF8
Write-Output $line
}
function Get-OSUpgradeRebootPending {
$RebootPending = $false
try {
$UpgradeStatus = Get-ItemProperty -Path $regPath1 -ErrorAction SilentlyContinue
$Pending = Get-ItemProperty -Path $RegPath2 -Name $ValueName -ErrorAction SilentlyContinue
if (($UpgradeStatus.UpgradeInProgress
-eq 1) -or ($Pending -and $Pending.$ValueName)) {
$RebootPending = $true
}
} catch {}
return $RebootPending}
function Get-UefiState {
try {
$firmware = (Get-ItemProperty -Path "HKLM:\HARDWARE\DESCRIPTION\System").SystemBiosVersion
#
crude but effective: if Confirm-SecureBootUEFI works, we're UEFI; else BIOS
$null = Confirm-SecureBootUEFI -ErrorAction Stop
return $true
} catch {
return $false
}
}
function Get-SecureBootState {
try {
return [bool](Confirm-SecureBootUEFI -ErrorAction Stop)
} catch {
return $false
}
}
Write-Log "===
Starting Windows 11 Upgrade Pre-Check Script ==="
#1 OS Check
$OSVersion = [Version](Get-WmiObject -Class Win32_OperatingSystem).Version
$OSArchitecture = (Get-WmiObject -Class Win32_OperatingSystem).OSArchitecture
$OSVersionReady = $false
Write-Log "OS
Version: $OSVersion, Architecture: $OSArchitecture"
if ($OSVersion -lt [Version]"10.0.22000") {
$OSVersionReady = $true
Write-Log "OS
Version: $OSVersion, Architecture: $OSArchitecture (Eligible for upgrade)"
}
#2 Detect Physical or
Virtual Machine
$computerSystem = Get-WmiObject -Class Win32_ComputerSystem
$isVirtual = $computerSystem.Model -match "Virtual|VMware|Hyper-V|HVD"
Write-Log "System
Type: $($computerSystem.Model) →
Virtual: $isVirtual"
#3 TPM Check
$tpm = Get-WmiObject -Namespace "Root\CIMv2\Security\MicrosoftTpm" -Class Win32_Tpm -ErrorAction SilentlyContinue
$TPMReady = $false
if ($tpm -and $tpm.SpecVersion
-like "*2.0*") {
$TPMReady = $tpm.IsEnabled_InitialValue
-and $tpm.IsActivated_InitialValue
-and $tpm.IsOwned_InitialValue
Write-Log "TPM
Version: $($tpm.SpecVersion), Enabled: $($tpm.IsEnabled_InitialValue), Owned: $($tpm.IsOwned_InitialValue)"
}
#4 --- Secure Boot / UEFI
helpers ---
$UEFIReady
= Get-UefiState
$SecureBootReady = Get-SecureBootState
if ($isVirtual) {
Write-Log "Secure
Boot Check Result (VM): $SecureBootReady"
} else {
Write-Log "Secure
Boot Enabled: $SecureBootReady"
}
Write-Log "Eligibility
inputs → TPMReady: $TPMReady; OSVersionReady: $OSVersionReady; UEFI: $UEFIReady; SecureBoot: $SecureBootReady"
#5 VM policy: skip UEFI/SB
if VM
if ($isVirtual) {
$AllChecksPassed = $TPMReady -and $OSVersionReady
} else {
$AllChecksPassed = $TPMReady -and $OSVersionReady -and $UEFIReady -and $SecureBootReady
}
Write-Log "AllChecksPassed:
$AllChecksPassed"
if ($AllChecksPassed) {
Write-Log "System
is eligible for Windows 11 upgrade. Proceeding..."
#6 ---------------- Check
if Reboot is required ----------------
if (Get-OSUpgradeRebootPending) {
$Users = (quser 2>$null) | Where-Object {$_ -notmatch "USERNAME"}
if (-not $Users) {
Write-Log "No
user session found. Rebooting immediately..."
shutdown.exe /r /t 0 /c "System
rebooting to complete OS Upgrade."
}
else {
Write-Log "Active
user session found. Warning user and rebooting in 10 mins..."
if (Test-Path $ServiceUI) {
Write-Log "Launching
ServiceUI with arguments: $Arguments"
Set-ItemProperty -Path $RunOncePath -Name "ResumeUpgradeScript" -Value $ResumeCommand -Force
Write-Log "Added
RunOnce entry to resume script after reboot."
#
Start the child script via ServiceUI
$childProcess = Start-Process -FilePath $ServiceUI -ArgumentList $Arguments -PassThru
Write-Log "Waiting
for child script to finish..."
# Loop until the process
exits
while (-not $childProcess.HasExited)
{
Start-Sleep -Seconds 2
}
Write-Log "Child
script completed. Exit code: $($childProcess.ExitCode)"
}
else {
Write-Log "ERROR:
ServiceUI.exe not found. Skipping user prompt."
}
}
}
else {
#7------------------------System
Cleanup Started------------------------
Write-Log "No
OS Upgrade reboot is pending."
Write-Log "=====
System Cleanup Started ====="
Remove-ItemProperty -Path $RunOncePath -Name "ResumeUpgradeScript" -ErrorAction SilentlyContinue
Write-Log "Cleaned
up RunOnce entry after as no OS Upgrade reboot is required."
#7.1 --------------------
[MANDATORY] Cleanup Block --------------------
Write-Log "[MANDATORY]
Cleanup block started — this section always runs regardless of last
run(7days)."
# Cleanup Windows Setup
folders
$cleanupFolders = @(
"$env:SystemDrive\`$WINDOWS.~BT",
"$env:SystemDrive\`$WINDOWS.~WS",
"$env:SystemDrive\Panther",
"$env:SystemDrive\ESD",
"$env:SystemDrive\Recovery",
"$env:SystemDrive\`$INPLACE.~TR",
"$env:SystemDrive\Intel",
"$env:SystemDrive\OEM",
"$env:SystemDrive\MSOCache",
"$env:SystemDrive\ProgramData\Microsoft\Windows\WER",
"$env:SystemDrive\Windows10Upgrade",
"$env:SystemDrive\`$GetCurrent",
"$env:SystemDrive\Temp"
)
foreach ($folder in $cleanupFolders) {
if (Test-Path $folder) {
try {
Remove-Item -Path $folder -Recurse -Force -ErrorAction Stop
Write-Log "[MANDATORY]
Removed folder: $folder"
} catch {
Write-Log "[MANDATORY]
Failed to remove $folder : $_" "WARN"
}
} else {
Write-Log "[MANDATORY]
Folder not found: $folder"
}
}
Write-Log "[MANDATORY]
Cleanup block completed."
#7.2 --------------------
[TIMESTAMP] Execution Control --------------------
Write-Log "[TIMESTAMP]
Checking last execution timestamp in registry..."
$continueTimestampSection = $true
if (Get-ItemProperty -Path $regPath1 -Name $regName -ErrorAction SilentlyContinue) {
try {
$lastRun = Get-ItemPropertyValue -Path $regPath1 -Name $regName
$lastRunDate = [datetime]::Parse($lastRun)
if ((Get-Date) -lt $lastRunDate.AddDays(7)) {
Write-Log "[TIMESTAMP]
Skipping timestamp-based cleanup — last run was on $lastRunDate." "WARN"
$continueTimestampSection = $false
} else {
Write-Log "[TIMESTAMP]
Proceeding — last run was on $lastRunDate."
}
} catch {
Write-Log "[TIMESTAMP]
Failed to parse registry timestamp: $_" "ERROR"
}
} else {
Write-Log "[TIMESTAMP]
No existing timestamp found — proceeding with timestamp-based cleanup."
#7.2.1 Step 1: SFC scan
#Write-Log "Running
SFC scan..."
#sfc /scannow |
ForEach-Object { Write-Log $_ } # Sfc Scan is already handled in W11 upgrade
TS, so skiping here.
#7.2.2 Step 2: DISM Online
RestoreHealth
Write-Log "Running
DISM restore..."
DISM /Online /Cleanup-Image /RestoreHealth | ForEach-Object { Write-Log $_ }
Write-Log "Recompiling
BitLocker WMI namespace..."
mofcomp "$env:SystemRoot\System32\wbem\win32_encryptablevolume.mof" | ForEach-Object { Write-Log $_ }
#7.2.3 Step 3: Delete SCCM
Logs Folder
$sccmLogPath = "C:\Windows\CCM\Logs"
if (Test-Path $sccmLogPath) {
try {
Remove-Item -Path $sccmLogPath -Recurse -Force
Write-Log "Deleted
SCCM log path: $sccmLogPath"
} catch {
Write-Log "Failed
to delete SCCM logs: $_" "ERROR"
}
}
else {
Write-Log "SCCM
log path not found: $sccmLogPath"
}
#7.2.4 Step 4: Ensure
Windows Update Services Are Running
$wuServices = @("wuauserv", "bits", "cryptsvc")
foreach ($svc in $wuServices) {
try {
$s = Get-Service -Name $svc -ErrorAction Stop
if ($s.Status
-ne 'Running') {
Start-Service -Name $svc
Write-Log "Started
service: $svc"
} else {
Write-Log "Service
already running: $svc"
}
} catch {
Write-Log "Service
$svc
missing or failed to start." "WARN"
}
}
#7.2.5 Step 5: Reset
Windows Update Components
Write-Log "Resetting
Windows Update Components..."
Stop-Service wuauserv -Force -ErrorAction SilentlyContinue
Stop-Service bits -Force -ErrorAction SilentlyContinue
Stop-Service cryptsvc -Force -ErrorAction SilentlyContinue
$swDist = "$env:SystemRoot\SoftwareDistribution"
$catRoot = "$env:SystemRoot\System32\catroot2"
Rename-Item -Path $swDist -NewName "${swDist}_old_20250722163805" -ErrorAction SilentlyContinue
Rename-Item -Path $catRoot -NewName "${catRoot}_old_20250722163805" -ErrorAction SilentlyContinue
Start-Service wuauserv
Start-Service bits
Start-Service cryptsvc
Write-Log "Reset
completed for SoftwareDistribution and Catroot2"
#7.2.6 Step 6: Check WSUS
Policy
try {
$wuPolicy = Get-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
if ($wuPolicy.WUServer)
{
Write-Log "WSUS
server detected: $($wuPolicy.WUServer)"
} else {
Write-Log "No
WSUS server override detected."
}
} catch {
Write-Log "Unable
to read WSUS policy from registry." "WARN"
}
#7.2.7 Step 7: Update
registry with current date/time
try {
Set-ItemProperty -Path $regPath1 -Name $regName -Value (Get-Date).ToString("o")
Write-Log "Updated
registry key $regName with current timestamp."
} catch {
Write-Log "Failed
to write registry key: $_" "ERROR"
}
Write-Log "=====
[TIMESTAMP] Execution Completed ====="
}
Write-Log "=====
System Cleanup Completed ====="
#8
-----------------------System Upgrade Started(Main
Script)-----------------------
Write-Log "=====
System Upgrade Started(Main Script) ====="
#8.1
---------------------Cleanup CCMCache-----------------------
$KeepPkg = @("CAS028C2","CAS026D1")
$UIResourceMgr = New-Object -ComObject UIResource.UIResourceMgr
$Cache = $UIResourceMgr.GetCacheInfo()
$CacheElements = $Cache.GetCacheElements()
$SetupExe = $null
foreach ($Element in $CacheElements) {
if ($KeepPkg -notcontains $Element.ContentId)
{
try {
$Cache.DeleteCacheElement($Element.CacheElementID)
Write-Log "Removed $($Element.ContentId) via
ConfigMgr API"
Write-Log "Deleting location: $($Element.Location)"
} catch {
Write-Log "Failed ConfigMgr delete for $($Element.ContentId) : $_"
}
} else {
Write-Log "Keeping
$($Element.ContentId)"
Write-Log "Location:
$($Element.Location)"
$joinsetup = Join-Path $Element.Location
"setup.exe"
$Ccmcachesetup = "$Element.Location"
if (Test-Path $joinsetup) { $SetupExe = $joinsetup }
#
NOTE: do NOT break; we still want to process the rest of the cache
}
}
#8.2
---------------------Checking if 30GB space is available-----------------------
$FreeSpaceMB = (Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='C:'").FreeSpace / 1MB
if ($FreeSpaceMB -gt $Threshold) {
Write-Log ("C:
drive free space is sufficient. Available: {0:N2} MB (threshold {1} MB)" -f $FreeSpaceMB, $Threshold)
# 8.2.1 --- Check if
setup.exe exists in CCMCache ---
if ($SetupExe -and (Test-Path $SetupExe)) {
$ccmCacheFileCount = (Get-ChildItem -Path $Ccmcachesetup -Recurse -File | Measure-Object).Count
if ($ccmCacheFileCount -ge 940) {
Write-Log "setup.exe
already present in CCMCache and file count matches. Running setup.exe..."
try {
Start-Process -FilePath $SetupExe -ArgumentList $SetupCmd -Wait -PassThru
Write-Log "setup.exe
launched successfully from CCMCache."
} catch {
Write-Log "Failed
to launch setup.exe from CCMCache: $_"
}
} else {
Write-Log "setup.exe
present in CCMCache but file count mismatch. Will try central shares."
$SetupExe = $null # Force fallback
}
}
# 8.2.2 --- Fallback to
Central Shares if CCMCache not valid ---
if (-not $SetupExe) {
$setupDestination = Join-Path $destPath1 $fldr
if (!(Test-Path $setupDestination)) {
New-Item -Path $setupDestination -ItemType Directory -Force | Out-Null
}
$setupexe = Join-Path $setupDestination "setup.exe"
$setupSuccess = $false
foreach ($remoteSource in $setupshares) {
if (Test-Path $remoteSource) {
try {
Write-Log "Copying setup files from $remoteSource to $setupDestination"
Copy-Item -Path (Join-Path $remoteSource "*") -Destination $setupDestination -Recurse -Force
Write-Log "Setup files copied successfully to $setupDestination"
if (Test-Path $setupexe) {
Start-Process -FilePath $setupexe -ArgumentList $SetupCmd -Wait -PassThru
Write-Log "setup.exe launched successfully from central share copy."
$setupSuccess = $true
break
}
} catch {
Write-Log "Failed to copy/run setup.exe from $remoteSource : $_"
continue
}
} else {
Write-Log "Setup
share not accessible: $remoteSource"
}
}
if (-not $setupSuccess) {
Write-Log "All
setup sources failed. setup.exe could not be launched."
}
}
} else {
Write-Log ("C:
drive free space is less than 30 GB. Available: {0:N2} MB" -f $FreeSpaceMB)
}
} # If reboot is not required block is end here
} #
Allcheckpassed end here.If any check is failed from AllChecksPassed
else {
Write-Log "Upgrade
blocked due to failed eligibility checks:"
if (-not $TPMReady)
{ Write-Log "TPM
2.0 not ready" }
if (-not $OSVersionReady)
{ Write-Log "System
already running Windows version: $OSVersion. Upgrade not required." }
if (-not $isVirtual -and -not $UEFIReady)
{ Write-Log " - Not booted in UEFI mode" }
if (-not $isVirtual -and -not $SecureBootReady) { Write-Log " - Secure Boot not enabled" }
}
#9 --- Upload log: try each
share; only break on first success ---
foreach ($SLShare in $slShares) {
if (Test-Path $SLShare) {
try {
$destPath = Join-Path $SLShare "$Logfldr\$hostname"
if (!(Test-Path $destPath)) { New-Item -Path $destPath -ItemType Directory -Force | Out-Null }
$destFile = Join-Path $destPath "$hostname`_$timestamp.log"
Copy-Item -Path $LogFile -Destination $destFile -Force
$setupdiag = "C:\Windows\Logs\SetupDiag\setupdiagresults.xml"
if (Test-Path $setupdiag) { Copy-Item -Path $setupdiag -Destination $destPath -Force }
Write-Log "Log
uploaded to: $destFile"
break
} catch {
Write-Log "Failed
to copy log to $SLShare → $Logfldr\$hostname : $_"
continue
}
} else {
Write-Log "Log
Share not accessible: $SLShare"
}
}
#10--------------Write log
on local Drive as well---------------------------
Write-Log "===
Script Completed ==="
No comments:
Post a Comment
Leave your valuable words here for improve better.