I was searching for a way to further automate new Windows system deployments. One thing I kept running into was Windows updating.
I have refined and added automation to my process over the past several years. For Windows 10 clients, my current deployment process involves an MDT/WDS server that has a stock Windows 10 wim file. To customize the deployments, I have a collection of GPOs and PDQDeploy packages for software installation and some other GPOs for policies.
I recognized that one of the consistent pain points was related to getting Windows updates going. I had previously just had a GPO to specify some of the settings for Windows updates, such as, the WSUS server URL/port number and how often to check for updates. I reorganized my WSUS server from quite a few groups, to a much simpler grouping by OS version. With that, I created GPOs that applied to systems based on OS version, using WMI filtering that would automatically add the computer to the respective WSUS OS group.
That helped... but I still had a considerable wait for Windows to start its automatic check-in with WSUS. I also found that even logging directly into the new system and forcing a check for updates would yield a lot of waiting and false reporting that there were no updates available. I found that if you deleted the C:\Windows\SoftwareDistribution folder, it would almost always force the client to recognize that there were new updates to install, although the check seemed to take a considerable amount of time. After all that, I still had to wait for the installations to finish and reboot and recheck.
All that to say, I started down a rabbit hole of PowerShell commandlets and scripts to try and trigger Windows to check for updates, install the approved updates and reboot the system. I came up with 3 scripts that can be run in sequence or used separately, depending on if it is a completely new deployment or you just want to trigger Windows updates to install on established systems.
First, this will speed up the new system deployment process by deleting the contents of the Software Distribution folder-
Stop-Service -Name wuauserv Get-ChildItem C:\Windows\SoftwareDistribution -Recurse | Remove-Item -Recurse -Force Start-Service -Name wuauserv
Second, install PSWindowsUpdate (Learn more here: https://www.powershellgallery.com/packages/PSWindowsUpdate/18.104.22.168). Essentially, PSWindowsUpdate allows you to run windows update and control certain parameters of how updates are downloaded and applied and if reboots are performed and even rechecks for updates after the first round of updates have been installed.
Install-PackageProvider -Name NuGet -MinimumVersion 22.214.171.124 -Force Install-Module PSWindowsUpdate -Force
Third, run PSWindowsUpdate. This command tells it to accept (download and install) all approved updates from the WSUS server, automatically reboot, and repeat.
Import-Module PSWindowsUpdate Get-WindowsUpdate -Install -AcceptAll -RecurseCycle 2 -AutoReboot
There are several parameters to allow you to customize how you want to handle the updates. For instance, you can have it download only, or don't have it automatically reboot.
I added these scripts as packages in my PDQ Deploy server and into the sequences that are run during the initial setup of my newly-deployed systems. I have also started using them on servers cloned from VM templates in vCenter Server. Next, I will be using the 2nd and 3rd to help with automating my routine server patching processes. No more logging into each server, wasting nights and weekends, to check for updates, download and install and reboot; just a couple clicks via PDQ Deploy, the rest is machine time.
EDIT: I should also add, it looks like you need to have PowerShell 5.1.
EDIT-2: If you have problems with Server 2016 (maybe even 2019) going out to Microsoft's public update server on the internet, instead of your WSUS server, you can see this post (https://mangolassi.it/topic/19993/server-2016-force-default-update-server-to-wsus-server) on how to make sure your GPO will force the system to use your WSUS server as its default.