Deploying a Windows Update with SCCM without rebooting using a Powershell script detection method

Occasionally when deploying software one of the prerequisites might be to install one or more Windows Updates. These are fairly easy to package up and deploy by simply using the command wusa.exe "KB12345678.msu" /quiet and setting the detection method to check the version of one of the files that the update effects. This information is published by Microsoft for each KB article. Once the computer reboots the file will be the newer version and the detection method will properly detect that update is installed.

But what if you don’t want to reboot immediately? It’s quite feasible that if you’re installing an update as a prerequisite to another software installation you want to install the update with the reboot suppressed and then install the software. You’d probably be using the Dependancies feature of SCCM to ensure the update gets installed whenever someone tries to install the software. In this scenario you can’t use the file version detection method as the file version will not change until after a reboot, but as you’re suppressing that reboot until after the software has been installed, the detection will fail. And that will cause the whole software deployment to fail.

The solution to this is to use a Powershell script as your deployment method. This script will do two things:

  1. Check for a particular event in the Windows Setup log that will exist when the update has been installed.
  2. Check if the file version of one of the files the update effects has been updated.

Why do you need to do both the event log check and the file version check? Because you cannot rely on the Setup log to continue to contain the log entry in the future. The Setup log may be cleared or set to roll over when it reaches a certain size and when that happens the detection will fail and incorrectly show as uninstalled in Software Center. The point of this detection method is to make the installation show as successful in the short time between the update installation and the software installation after which you will presumably reboot the machine, or in the worst case wait for the user to turn off their machine at the end of the day. Once the reboot is complete the second detection method will kick in and continue to correctly detect the installed update going forward.

So let’s take an example update and see what the package would look like in SCCM. For this example I am using KB2921916.

KB2921916 Program Properties

This part is nice and simple. Notice the uninstall program is specified as well, I consider it best practise to always include the uninstall program just in case the update causes a problem and the user needs to remove it quickly. These programs are:

wusa.exe "Windows6.1-KB2921916-x64.msu" /quiet /norestart

wusa.exe /kb:2921916 /uninstall /quiet /norestart

Hop over to the Detection Method tab and change it to Use a custom script to detect the presence of this deployment type.

KB2921916 Detection Method

Click Edit and get writing some Powershell!1

KB2921916 Detection Method Properties

That script is:

$KBToDetect = "KB2921916"
$FileToDetect = "$Env:WinDir\System32\setupapi.dll"
$VersionToDetect = "6.1.7601.18361"

Start-Sleep "5"

$GetFileVersionInfo = (Get-Item $FileToDetect).VersionInfo
$FileVersion = $GetFileVersionInfo.FileMajorPart.ToString() + "." + $GetFileVersionInfo.FileMinorPart.ToString() + "." + $GetFileVersionInfo.FileBuildPart.ToString() + "." + $GetFileVersionInfo.FilePrivatePart.ToString()

if ([Version] $FileVersion -ge [Version] $VersionToDetect) {return $true}

if (Get-WinEvent -ErrorAction "SilentlyContinue" -MaxEvents 3 -FilterHashtable @{logname = 'setup'} | select message | Select-String -SimpleMatch "A reboot is necessary before package $KBToDetect can be changed to the Installed state.") {return $true}

There’s quite a lot going on here so let’s briefly run through it. The first three variables define what we are looking for and you will have to update these based on the KB you are installing. Having another look the KB2921916 article and expanding the File information section you can see that one of the files that updates is Setupapi.dll. This file can be found in %SystemDrive%\System32\Setupapi.dll and will bring its version number to 6.1.7601.18361. These details have been added to the $KBToDetect, $FileToDetect and $VersionToDetect variables.

The next part is a simple 5 second sleep. I added this because in my testing I found that sometimes the detection script runs before the Setup log entry is added which causes the detection to fail. By waiting 5 seconds before running the detection we ensure the log entry is in place when the detection runs.

The next part is the needlessly complicated (in my opinion) code needed to find the version of Setupapi.dll. If you just try (Get-Item $FileToDetect).VersionInfo.ProduectVersion you will not get the current version of the file, you will get the version the file was when it shipped! This weirdness is known and has been blogged about. So this code gathers the individual parts of the file’s true version (the FileMajorPart, FileMinorPart, FileBuildPart and FilePrivatePart) and constructs a full version string out of it.

The next two lines are the only ones that really matter for the detection. The first if statement checks the file version and returns $true if the installed version of Setupapi.dll is either equal to or greater than 6.1.7601.18361. This will ensure that post reboot the detection method will still work and ensures that it will also continue to detect correctly if the file version increases again in the future.

The second if statement checks the last 3 events in the Setup log and searches for the string “A reboot is necessary before package $KBToDetect can be changed to the Installed state.” If this string is found then it returns $true.

Note that nothing at all is returned if neither of these if statements is true. That is by design because when you use Powershell as a detection method practically any output from the script is taken to be success so if it fails it must return nothing.

Only one thing left to do now. Click on the User Experience tab and change the enforced behaviour from the default (Determine behaviour based on return codes) to No specific action.

KB2921916 User Experience Properties

This is important because if you don’t do this the return code will be 3010 (soft reboot) and the user will be prompted to reboot regardless of your /noreboot switch. If this happens any further components of the deployment will wait until the reboot has been completed which is probably what you’re trying to avoid!

1In order to use a Powershell script detection method you will need to ensure that you have the Powershell execution policy set to Bypass on your clients. That is unless you’re doing things very properly and have a code signing script that your clients trust that you can sign your Powershell detection scripts with. You can set the execution policy in the Client settings in SCCM or via GPO.

BITS throttling causing slow SCCM client install and policy download

SCCM extensively uses Background Intelligent Transfer Service (BITS) to transfer data between a client and the SCCM server. This also affects downloading client policy! One of the first things that SCCM uses BITS for is to download the client to the machine when you initiate a client push. If BITS is heavily throttled you may find the following entries in your ccmsetup.log file (typically found in C:\Windows\ccmsetup):

Starting BITS download for client deployment files.
Download update: Copy job has been queued.
Download update: Copy job has been queued.
Download update: Copy job has been queued.

There may be some entries showing the progress of the client download, however if BITS is throttled this may be proceeding very slowly.

If you want to check whether BITS is being throttled on a particular machine open a command prompt window and type RSoP to perform a Resultant Set of Policy. Once the RSoP window opens navigate to Computer Configuration —> Administrative Template —> Network —> Background Intelligent Transfer Service (BITS) —> Limit the maximum network bandwidth for BITS background transfers and check if it is enabled and what limitations are in place.

In my case it was limited to 10 KBs at all times! Far too slow to get anything done in a reasonable time.

The next thing you can do before you start talking with your fellow sysadmins and the  network team about removing the throttling is to prove that it is the BITS throttling causing the issue. As long as you are a local administrator on one of the clients this can be done quickly by editing one registry key and restarting the Background Intelligent Transfer Service. Open regedit and navigate to HKLM\SOFTWARE\Policies\Microsoft\Windows\BITS and change EnableBitsMaxBandwidth from 1 to 0. Then open services.msc and restart Background Intelligent Transfer Service. This will remove the BITS throttling on that machine until group policy is reapplied and sets EnableBitsMaxBandwidth back to 1. Before that happens however you should have time to re-deploy the SCCM agent or push a new client policy and observe whether or not it happens in a much more timely fashion than it did before.

Error 80070057 when attempting to update Windows Server 2012 R2

Once when I was updating some servers running the version of Windows Server 2012 R2 I encountered something odd; no patches appeared in Software Center or in the Windows Update panel, even though the server was several years out of date and definitely had applicable updates!

In WindowsUpdate.log I found the following error message repeating:

cidimage001

The fix for this is to manually download and install KB2919355, which is the April 2014 update rollup for Windows Server 2012 R2. After this has been installed and the server has restarted, re-run your updates scan and updates will show up in Windows Update or Software Center.

Increasing the maximum run time for Windows 10 and Windows Server 2016 cumulative updates

One of the things I have noticed since starting to deploy Windows Server 2016 is that the cumulative updates can fail to install when deployed from SCCM. It starts to run but then times out due to the maximum run time having been reached. By default this is set to 10 minutes. However due to the updates being larger and taking longer to install than updates prior to the cumulative updates era 10 minutes doesn’t seem to be long enough. The fix for this is to simply increase the maximum run time for cumulative updates for both Windows Server 2016 and Windows 10 from 10 minutes to 30 minutes.

Screen Shot 2017-06-05 at 23.12.35

Screen Shot 2017-06-05 at 23.12.47

This is a bit tedious as you’ll have to do it every month for both Windows Server 2016 and every version of Windows 10 you have in your environment. Hopefully Microsoft soon catches on to this and changes the default run time to 30 minutes so that this ceases to be an issue. There is already a Configuration Manager UserVoice entry for this idea, so if you’re reading this, pop over and vote to increase its visibility!

ISATAP failing with error 0x490

ISATAP can be very useful if you need to manage out from a machine with an IPv4 address to a machine with an IPv6 address. This is commonly used where DirectAccess has been deployed as all DA clients will be using IPv6. Being able to RDP to them or use the SCCM Remote Control feature from a machine inside your network is very helpful for the IT support staff – it’s as if the user was just working from their desk like usual!

One time I was configuring an ISATAP interface on a server and I got the follow error in the event log:

Unable to update the IP address on ISATAP.yourdomain.com. Update type: 1. Error Code: 0x490

I scratched my head over this for quite a while – setting up an ISATAP interface should be easy! After a while I remembered that there was a history of disabling the IPv6 components on servers via GPO. It turns out that even though the GPO had been removed, the setting to disable IPv6 had been tattooed on the servers it had once been applied to.

To check this I had a look in the following location in the registry:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP6\Parameters\DisabledComponents

The value was set to 0xFFFFFFFF (it may instead be set to 0xFF). This disables enough of the IPv6 components to prevent an ISATAP interface from being able to be created.

Simply delete the DisabledComponents key and restart the server. So long as everything else is set up correctly you will have your ISATAP interface when you next log in.

Automatically enable BitLocker and set a PIN during an SCCM Task Sequence

Getting your operating system deployment one step closer to being zero touch is always a good goal, so with that in mind here is how to automatically enable BitLocker during OSD using a PIN that you define in a variable at the beginning of the Task Sequence.

The first thing to do is add the OSDBitlockerPIN variable to the collection you advertise your OSD Task Sequences too. This is very likely the All Unknown Computers collection. Right click on it and select Properties. Navigate to the Collection Variable tab and click New. The name is OSDBitlockerPIN and you should untick “Do not display this value in the Configuration Manager console”.

Capture

Next up open your Task Sequence and add the Enable BitLocker step. This can be placed anywhere after the Setup Windows and ConfigMgr step.1 Make sure Current operating system drive is selected and then select TPM and PIN. You can then enter anything into this field as it will be overwritten by what you enter into the OSDBitlockerPIN variable when you start the Task Sequence.

Capture

Finally, go ahead boot your client into the WinPE environment. Select your Task Sequence and click next and you will be presented with the Edit Task Sequence Variables step. You may already use the OSDComputerName variable in which case you will already be familiar with this! Double click on OSDBitlockerPIN and enter the PIN you wish to use for this machine.

Capture

Click Next and the Task Sequence will run and complete. BitLocker will be enabled and the PIN will be set. Now you don’t have to configure BitLocker after the operating system has been deployed!

1I would add the Enable BitLocker step at the very end of your Task Sequence, otherwise you will have to enter the PIN each time the machine reboots after applications or updates are installed. You could suspend BitLocker before each reboot, but why go to the extra effort.

Scan failed with error 0x80244021 with WinHTTP proxy configured

While trying to update one particular server using SCCM 2012 R2 I discovered that it was not updating, with the following error was appearing in WindowsUpdate.log and UpdatesHandler.log:

Scan failed with error = 0x80244021

In my case this issue was occurring because a proxy server was configured but no exception was in place for the WSUS / SCCM server. This meant that the server was trying to access the WSUS server through the proxy! To see what my proxy settings were I opened an elevated CMD prompt and entered the following command:

netsh winhttp show proxy

The following information was returned:

Proxy Server(s): ProxyServer.local
Bypass List :

As you can see, the bypass list is empty. In order to create an exception for the WSUS server, enter the following command:

netsh winhttp set proxy ProxyServer.local "https://WSUSServer.local" 1, 2

This sets the proxy and sets the WSUS server as an exception. Re-running netsh winhttp show proxy will now show you the following output:

Proxy Server(s): ProxyServer.local
Bypass List : https://WSUSServer.local

As you can see the WSUS server is now part of the bypass list, meaning that attempts to access this website will not go through the proxy.

Restart the WindowsUpdate service and re-run the Windows Update scan. You should no longer see scan error 0x80244021.

1Whether you use HTTP or HTTPS for your WSUS server depends on your WSUS or SCCM configuration.

2If you need to add multiple exceptions enter the first one, followed by a comma, followed by the next one and so on.