Enabling HSTS and selecting most secure ciphers and protocols for HTTPS for ConfigMgr

Many IT security departments these days frequently scan their servers using a vulnerability scanner such as Nessus or Qualys to look for software that needs patching or settings that need to be changed, in the hopes of finding and fixing them before the bad guys exploit them. On web servers, the findings may include needing to enable HSTS (HTTP Strict Transport Security) and configuring the server to only use the latest versions of TLS and use the most secure ciphers. As many ConfigMgr roles rely on a web server, this may mean your ConfigMgr server(s) appears on the vulnerability report with findings.

So, the big question is: Does enabling HSTS cause problems for ConfigMgr? I have tested this extensively and I believe the answer is no, it does not cause problems. If you are concerned, there is a way to minimise the risk by settings the “max-age” for HTST to just 1 hour to begin with (in the end you should set this to 1 year as that is the industry recommended time).

The second question is: Does ConfigMgr still work if you limit the TLS protocols and ciphers to only the most secure? Again, the answer is yes, ConfigMgr continues to work just fine.

Before you begin, you must understand what HSTS is and how it could break things if it is set incorrectly. HSTS instructs browsers to always expect a secure connection to a site and they will refuse to connect in the future if they do not find a valid SSL certificate. Due to this you must only set HSTS if you are running ConfigMgr with SSL certificates and can guarantee that you will always be doing so in the future.

To enable HSTS you must be running all ConfigMgr roles in HTTPS mode. This includes the Management point and Distribution point, as well as the Enrollment point and Enrollment proxy point if you are still using those.

WSUS must also be configured to use HTTPS.

If you are running a Reporting services point, you must use Report Server Configuration Manager to configure SQL Server Reporting Services (SSRS) to use SSL. There is also a separate process to enable HSTS on SSRS if you are using the 2019 version.

You should be aware that this guide will set HSTS for all websites hosted on your server. If you run any other sites on your ConfigMgr server, you should make sure that you can guarantee HTTPS connections to those sites as well.

Configuring HSTS in IIS 10.0
Microsoft has confusingly continued to use version 10.0 for IIS in Windows Server 2016 and 2019 even though they have added features to IIS in the newer versions of Windows Server. In Windows Server 2016 the full version for IIS is IIS 10.0 version 1607 and in Windows Server 2019 it is IIS 10.0 version 1809. HSTS can be enabled in both versions but have slightly different procedures.

If you are running Windows Server 2016, open Internet Information Services (IIS) Manager and select the site your ConfigMgr roles are running from (by default this will be Default Web Site). Double click on HTTP Response Headers, then click Add from the Actions pane on the left. In the Name field enter “Strict-Transport-Security”, and in the Value field enter “max-age=31536000; includeSubDomains”. Click OK to save this header.

Setting the Strict-Transport-Security header in IIS 10.0 on Windows Server 2016

If you are running Windows Server 2019, open Internet Information Services (IIS) Manager and select the site your ConfigMgr roles are running from (by default this will be Default Web Site). In the Actions pane on the left click HSTS… and tick Enable, put the value 31536000 in the Max-Age field and tick includeSubDomains and Redirect Http to Https. Click OK to save this setting.

Enabling HSTS in IIS 10.0 on Windows Server 2019

Note: The 31536000 value for max-age is equal to 1 year in seconds. This is the industry standard time for this value. If you want to test this before fully committing to a year you can set this value to 3600 (1 hour) or 86400 (1 day). Once you have completed your testing and are satisfied that HSTS is not causing any problems, you should set this to 31536000.

If you are running a previous version of IIS, you may still be able to enable HSTS by configuring the HTTP Response Header. Microsoft has an article that covers enabling HSTS in previous versions of IIS here.

Configuring HSTS in SQL Server Reporting Services
SQL Server Reporting Services (SSRS) has long been decoupled from IIS, so configuring HSTS for your IIS sites will not configure it for the reporting services site if you have a Reporting services point set up in ConfigMgr. In order to configure HSTS for SSRS you must be running SQL Server 2019 Reporting Services or later, as this is the first version where Microsoft has officially supported setting custom response headers.

To configure the Strict-Transport-Security header in SSRS 2019, start by opening SQL Server Management Studio and selecting Reporting Services from the Server type drop down menu and entering the server’s name, followed by the authentication details you use. Once connected, right click on the server’s name in Object Explorer on the left and select Properties. Go to Advanced and look for the CustomHeaders field. By default, this is empty, so you should enter the following value:

<CustomHeaders><Header><Name>Strict-Transport-Security</Name><Pattern>(.+)\/Reports\/(.+)</Pattern><Value>max-age=31536000; includeSubDomains=true</Value></Header></CustomHeaders>
Showing where to set the Strict-Transport-Security header in SQL Server Management Studio

This sets the header name (Strict-Transport-Security) and the value (max-age=31536000; includeSubDomains=true). You also have to use a regular expression to set which URLs will be matched and this header applied to. For a default install of SSRS, your ConfigMgr reports will be accessible at https://<ConfigMgrServer.com/Reports/. If this is the case, the regex “(.+)\/Reports\/(.+)” will work as this matches any URL that has characters before and after “/Reports/”. If you need to modify this regex for your environment, I recommend using a regex testing site such as this one.

Once you have entered the text in the CustomHeaders field click OK to close the Server Properties page. You must then restart the SQL Server Reporting Services service before it will take effect.

Configuring best practise for TLS versions and cipher suites offered by IIS
The easiest way to disable old TLS versions and insecure cipher suites is to download the tool IIS Crypto from Nartac Software. Once downloaded, run it on your ConfigMgr server and click the Best Practices button at the bottom of the window. This will leave only TLS 1.0, 1.1 and 1.2 enabled and disable many less secure ciphers such as MD5 and 3DES. I would also recommend unticking TLS 1.0 to disable that.

You can review exactly what has been disabled by going through the list of protocols and ciphers that have been left enabled in the Schannel tab and the Cipher Suites tab. If you’re happy with the changes that it will make, click Apply and then close IIS Crypto. You must restart the server before the changes will take effect.

Once you have completed this you can ask your security team to rescan your ConfigMgr server and check that it no longer shows vulnerabilities relating to HSTS or insecure HTTPS protocols and cipher suites.

The mystery of the invisible System Center Configuration Manager update

Since mid-December 2019 I have been trying to update my SCCM lab environment from 1906 to 1910. For the first few weeks of release it was in the early update ring, but no matter how many times I ran the PowerShell script to enable early updating, the update would not appear in the Updates and Servicing node in SCCM.

Oh well, no matter, I thought. Perhaps it’s buggy and has been pulled, or maybe there’s just some odd reason my environment isn’t compatible. I’ll just wait for the general release.

General release comes and goes… and still no update. What’s going on?! I am 100% certain that my Service connection point is set to Online, I have restarted the SMS_EXECUTIVE service so many times, and also restarted the whole server just to be as thorough as possible! I know my server has internet connectivity because the Software update point is happily downloading and applying the January 2020 updates to my servers and clients. And finally, I have checked dmpdownloader.log, CMUpdate.log and hman.log and there are no obvious errors in there to tell me why I’m not seeing the latest update.

(Okay – that paragraph was a overblown way to tell you what you should definitely check if you haven’t already, before you continue to read).

So, what was going on?

Delving a little deeper in the hman.log file, I did find the following two errors:

CServerStatusReporter::DeliverToStatusManager(): ERROR: Cannot deliver status message to SMS_STATUS_MANAGER, could not write the message to varfile E:\Program Files\Microsoft Configuration Manager\inboxes\statmgr.box\statmsgs\90sna58w.SVF due to a file error: The file or directory is corrupted and unreadable.

Error: Failed to move file E:\Program Files\Microsoft Configuration Manager\inboxes\hman.box\ForwardingMsg\___CABConfigMgr.Update.Manifest.MCM to E:\Program Files\Microsoft Configuration Manager\inboxes\hman.box\CFD\ConfigMgr.Update.Manifest.CAB, Win32 error = 1392

When I attempted to navigate to “E:\Program Files\Microsoft Configuration Manager\inboxes\statmgr.box\statmsgs” I got an error message telling me the location was corrupted and unreadable!

Oops – my screenshot shows statmsgs.old. That’s because I already fixed it before posting this article.

I got the same error when I tried to navigate to “E:\Program Files\Microsoft Configuration Manager\inboxes\hman.box\CFD”. I’m not entirely certain how these two locations became corrupted, but thankfully Windows Explorer allowed me to rename them to “statmsgs.old” and “CFD.old” respectively, and then re-create the folders.

Once I restarted the SMS_EXECUTIVE service once more, hman.log finally filled up with information about the new update and the download proceeded. Finally, I have my update!

In all seriousness, this may be a very marginal case and may not be the reason you can’t see the latest update for SCCM. However, it seemed worth documenting; just in case someone else has the same symptom!

I would suggest clicking Check for updates in the Updates and Servicing node and then paying close attention to your hman.log file for any errors similar to the ones I found. Bear in mind that it might be different folders that cause errors for you, so keep an eye out for any errors with files being moved between inboxes.

Anyway, I’m off to go play with new SCCM features…!

Updating Office 365 client with SCCM

Each release of SCCM current branch has been improving how it manages Office 365 clients, and the last couple of versions have improved the user experience the most. The only real issue remaining is that, under certain circumstances, the user will be prompted to reboot once the Office 365 update is complete. This reboot is not actually necessary, however. Here is how to go about setting up SCCM so that it delivers Office 365 updates to users without prompting them for an unnecessary reboot.

For starters, make sure your SCCM environment is syncing Office 365 client updates. To do this open the SCCM console and navigate to Administration > Site Configuration > Sites, select your site and select Configure Site Components > Software Update Point. Click on the Products tab and make sure Office 365 Client is ticked. Click OK to close that dialog and then head over to Software Library > Software Updates > All Software Updates and select Synchronize Software Updates in the ribbon.

Another thing to check is that the Office 365 client agent on users’ machines is configured to be managed by SCCM. You can set this in the Client Settings of SCCM. Go to Administration > Client Settings and click properties on the device settings policy your clients receive. Select Software Updates and ensure that Enable management of the Office 365 Client Agent is set to Yes.


Once that’s had time to process, create an Automatic Deployment Rule to push out the updates each month (or on whatever schedule you prefer). Under Software Updates go to Automatic Deployment Rules and click Create Automatic Deployment Rule. Give it a name, such as Office 365 Monthly Updates, and select the target collection that contains your clients that need updating. Click Next, and Next again and now select the criteria for updates in the ADR.

Select the following options:
Date Released or Revised: Last 1 month
Product: Office 365 Client
Superseded: No
Title: -semi-annual OR -Monthly Channel (Targeted)


Those two items in the Title field (-semi-annual and -Monthly Channel (Targeted)) means exclude any updates with ‘semi-annual’ or ‘Monthly Channel (Targeted)’ in the name. This is because I only want the Monthly Channel updates in my ADR. If you prefer to get the semi-annual updates in yours, do not add that and add ‘-Monthly Channel Version’ to your title instead.

Click Preview to ensure you’re getting what you expect. If not, the updates may not have synchronized yet.


Click Next and select the schedule you want this ADR to evaluate on. Office 365 client updates are released every month (for monthly channel) but not on any particular day, so I have this rule run toward the end of each month to hopefully catch that month’s client update.


Click Next and select when you want these updates to be pushed to users. This will depend on your organisations patching schedules and agreements, however I set both the available time and deadline to As soon as possible. This is because I want these updates to be delivered to users as soon as their SCCM client receives the notification of the update.

On the next screen, this is where you need to select specific options to get what I consider to be the best user experience for the Office 365 client updates. In User notifications select Hide in Software Center and all notifications. Under Deadline behaviour tick Software Update Installation, and under Device restart behaviour tick to suppress updates on both Servers and Workstations.


Okay, time for a tangent to explain how this is beneficial to the Office 365 update user experience. If you select Display in Software Center for the user experience and omit the system restart suppression the user will be notified that new updates are available. If they go to Software Center, find the update and click Install they will be presented with the following dialog:


As it suggests, when they click Install any Office applications they have open will close and the user will think they need to wait for the update to complete before they can continue to work. On top of that, when the update does complete, it will prompt the user to reboot their PC.


The problem is that the user waiting, and rebooting are a waste of their time. Office is able to install updates in the background while the user is working and only actually needs the user to close the apps right at the end of the install to complete the update. Office prompts the user to complete this step by means of a banner that appears in any of the Office apps that the user opens after the update has finished doing what it can. They can then click Update now at their leisure, and because most of the update process has taken place behind the scenes, the users’ downtime is only a few minutes. Office will even re-open the apps the user had open when it is complete!


So, to mitigate this sub-par user experience, the deployment settings for the ADR should be set to Hide in Software Center and all notifications, with the update allowed to install once the deadline is reached and all restarts supressed. From the users’ point of view, they will not see any indication that Office is being updated until they see the banner in their app prompting them to Update now. They can ignore this until a time that suits them, or it can just happen at the end of the day when they shut down their machine and pack up for the day. And better still, no unnecessary restart prompt.

Let’s get back to that ADR, there isn’t much left to do. Click Next and Next again to get to the Deployment Package page. Create a new deployment package (or add to an existing one, if you wish) and click Next. Continue through the ADR wizard, selecting the download location and which languages you wish to download and finally complete the wizard. Select your new ADR and click Run Now in the ribbon to get the Deployment Package and Software Update Group created. Be aware that this will also create the deployment to the client collection you selected at the start of the process, so make sure that’s acceptable! If it’s not, you may wish to change the deployment collection to a collection containing test clients or just your own machine at first to test that the user experience is what you expect it to be.

And that’s it! Office 365 client updates deployed to your users in the best possible way.


Buy Me A Coffee

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.


Buy Me A Coffee

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.


Buy Me A Coffee

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:


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.


Buy Me A Coffee

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!


Buy Me A Coffee

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”.


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.


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.


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.


Buy Me A Coffee