script-windows-updateMicrosoft certainly has been making patching Windows 10 PC’s difficult these days but after some significant effort, I have found a simple combination of commands and one free tool does the trick.

The command in question is an undocumented program inside Windows 10 named C:\Windows\System32\USOClient.exe  This program manages Windows Updates much like the old wuauclt used to:

  1. Launch a CMD prompt or PowerShell AS AN ADMINISTRATOR
  2. Type USOClient.exe ScanInstallWait 
    1. You would expect that command to download and install the updates but it does not appear to, so you have use the next command
  3. Then USOClient.exe StartInstall
    1. The installs will likely take a few minutes to complete so if you are scripting this you are going to want to put a delay timer in (see below)
  4. Then download and unzip the free SHUTDOWNWITHUPDATES directly from the developer, Dennis Babkin, HERE.
  5. Then run SHUTDOWNWITHUPDATES /r /f 
    1. those switches force a reboot and complete the patches installs

Sooooo, below and HERE are the easy script to do this.  Alternately, if you have PDQ Deploy, you can download our PDQ DEPLOY script directly from us HERE.  If do you use this simple PDQ Deploy script you will need to download the SHUTDOWNWITHUPDATES exe and set the path to it in this PDQ Deploy script.

REM Download and Fully Install Windows Updates
REM by Ian Matthews
REM Last Updated Nov 19 2018

REM These two commands do not need a path
usoclient ScanInstallWait
usoclient StartInstall

REM Wait 40 mins to allow all the installs to complete
timeout /T 2400

REM download from
REM this command will need a local path to the file unless it is run from the same folder that contains it
ShutdownWithUpdates.exe /r /f

For more information on USOClient read our USOCLIENT DOCUMENTATION page.


Albert · August 24, 2020 at 6:39 am

natecull , thanks for your script. It returns a few errors when I tried to run it from powershell
Has anyone been able to run it?

[ref] cannot be applied to a variable that does not exist.
At C:\Users\Administrator\Desktop\2.ps1:29 char:1


+ CategoryInfo : InvalidOperation: (x:VariablePath) [], RuntimeException
+ FullyQualifiedErrorId : NonExistingVariableReference
Cannot find an overload for “InitiateShutdown” and the argument count: “2”.
At C:\Users\Administrator\Desktop\2.ps1:37 char:1

$advapi32::InitiateShutdown($null,”Installing updates and restarting” …

+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest

Boris · February 15, 2019 at 5:58 am

Thank you for your explanations.
Tried to reproduce some commands from your script.
It seems that the best option is to provide a full path to “usoclient” like:

%windir%\System32\UsoClient StartDownload

natecull · December 16, 2018 at 7:39 pm

Thank you very much for this! One comment: if, like me, you would prefer to run pure Powershell commands rather than a third-party EXE, you can replace ShutdownWithUpdates.exe with the following pure PowerShell:

This runs the two Win32 API functions: ntdll.dll::RtlAdjustPrivilege for priv 19 (SE_SHUTDOWN_NAME) to enable the Shutdown privilege for this process, and advapi32.dll::InitiateShutdown to restart with code 0x44 (install updates and restart) and reason 0x80020011 (Planned, application, hotfix).

$signature = @”
[DllImport(“ntdll.dll”, SetLastError = true)]
public static extern IntPtr RtlAdjustPrivilege(int Privilege, bool bEnablePrivilege, bool IsThreadPrivilege, out bool PreviousValue);

$ntdll = Add-Type -MemberDefinition $signature -name “NtDll” -Namespace “Win32″ -PassThru

$signature = @”
[DllImport(“advapi32.dll”, SetLastError = true)]
public static extern UInt32 InitiateShutdown(string lpMachineName, string lpMessage, UInt32 dwGraceperiod, UInt32 dwShutdownFlags, UInt32 dwReason);

$advapi32 = Add-Type -MemberDefinition $signature -name “AdvApi32” -Namespace “Win32″ -PassThru


$x = $null
$advapi32::InitiateShutdown($null,”Installing updates and restarting”,10,[Uint32]’0x44′,[Uint32]’0x80020011′)

Questions or Comments?