Switch from Legacy BIOS to UEFI on existing installs of Windows Server 2016 (and 2012 / 2012 R2)

With most modern servers now shipping with UEFI enabled by default and many virtual hypervisor platforms supporting it for guest VMs as well, you may wish to switch your existing installations of Windows Server 2012, 2012 R2 or 2016 from booting with legacy BIOS to booting with UEFI. Thanks to a small tool introduced by Microsoft in Windows 10 1703, MBR2GPT.EXE, this can be done very easily.

You will need to download a copy of Windows 10 1703 or later in order to follow this guide as you need a copy of MBR2GPT.EXE and you need to boot your server into the version of WinPE that comes with these releases of Windows 10. You cannot run the tool in versions of Windows prior to 1703 (which, technically, Window Server 2016 is as it uses the same codebase Windows 10 1607).

Make certain your platform supports UEFI booting before continuing with this guide. If it does not, you will no longer be able to boot into Windows after you have changed the partition table from MBR to GPT.

If this process fails it has the potential to make your server unbootable, corrupt your data and cause lengthy downtime. Make certain you have an adequate backup of your server and a restoration process in place before proceeding.

Okay, with the big scary warnings out of the way, let’s get going!

First start by loading the Window 10 1703 (or later) ISO onto a USB drive, DVD, or if you’re following this process on a VM, mount it directly on the VM. Restart your server and boot the server from the Windows installation media. Click Next on the language selection screen and on the next screen click Repair your computer.

When the options screen comes up select Troubleshoot, and in Advanced Options select Command Prompt. You’ll find yourself at the command prompt in X:\Sources. Change directory to X:\Windows\system32 and start diskpart. Type list disk to list the disks attached to the system and note down the one that contains the OS. This will likely to be disk 0 but may not be depending on your servers’ disk layout.

diskpart List Disk

Exit diskpart and type MBR2GPT.exe /validate /disk:0. This will run the MBR2GPT tool on your OS disk to validate that it is ready for the conversion. If it is successful, run MBR2GPT.exe /convert /disk:0 and wait for the conversion to complete – it should not take long.

MBR2GPT Convert

That’s it for the conversion. Now restart your server, enter setup and enable UEFI booting. Save and exit and Windows should boot right up without any issues.

Now there is some optional clean up you can do. How optional it is depends on if you’ll ever want to extend the size of the drive. On a physical server it is unlikely your partition doesn’t already use the full size of the drive, however on a VM where it is possible to increase the size of the disk, you may have need to extend it in the future. It also depends on if you use BitLocker or not – more on that in a moment.

Here’s the deal. In Windows, open a command prompt and start diskpart. Type select disk 0 and then list partition. You will see three partitions – a 500 MB recovery partition, your OS partition and a 100 MB EFI system partition. That 500 MB partition is no longer in use since you no longer have an MBR partition. The new system partition is that 100 MB one at the end, and because it is at the end it will block you extending your OS partition. Fortunately, you can reuse that 500 MB of space and put the EFI system partition there instead.

If you use BitLocker to encrypt your drive the 500 MB partition also contains the unencrypted system files needed to start your computer. In this case, you cannot delete the partition. If you really need to be able to extend your partition in the future you can either unencrypt your drive, move the partition and then re-encrypt it, or use a third-party partitioning tool to move the EFI system partition to the front of the disk.

If you want to complete the partition clean up, here’s what to do. First, reboot the server and use the same steps as earlier with your Windows 10 installation media to end up back at the command prompt. From here, enter diskpart and type select disk 0 and then list partition. Note down which partitions are the 500 MB recovery partition and 100 MB system partition (typically these will be partition 1 and partition 3). Proceed to delete these partitions by first selecting them and then deleting them, using the commands select partition 1 followed by delete partition override and then select partition 3 followed by delete partition override.

diskpart Delete Partitions

Now create a new EFI system partition at the front of the disk using the command create partition efi size=100, format it using format quick fs=fat32 and assign it a drive letter using assign letter=s. Next, use the list volume command to get a list of volumes and make a note of the drive letter your OS drive is on (very likely C).

diskpartListVolume

Exit diskpart and type bcdboot C:\Windows /s S: to copy the necessary system files to the new EFI system partition.

Finally, enter diskpart one last time, type select disk 0 and then select partition 2 and then type extend to reclaim that 100 MB of space at the end of the drive (may as well, right?) This will leave you with 2 partitions – the 100 MB EFI system partition at the beginning of the drive and your OS partition. Exit diskpart and restart your server.

diskpartExtendPartition

And that’s it, the server now boots with UEFI and the partitions are all tidied up1.

1The astute among you will have realised that actually, the partitions aren’t quite as clean as you might like. Due to the old system partition being 500 MB and the new one being 100 MB, there is now 400 MB of unused space between the system partition and the OS partition. You have a few options here. The first, and simplest, is to actually create the new EFI partition with 500 MB of space rather than 100 MB. It doesn’t need that much space, but it will look better in Disk Management not having that random unused space between the partitions. Your second option is to simply leave it there, which could be beneficial in the future. If you ever turn on BitLocker it will need to create a 350 MB partition to store the unencrypted system files needed to boot your computer, and it can do so inside that 400 MB of space. Your third option is to use a third-party partitioning tool which is capable of extending your OS partition into the space in front of it, which is something that diskpart cannot do.

Installing Remote Server Administration Tools (RSAT) for Windows 10 1809 (including SCCM deployment)

Starting in Windows 10 1809 and continuing in future versions of Windows 10, the Remote Server Administration Tools are an optional feature that can be installed from within the OS, rather than you having to download them from Microsoft separately. This new approach fixes the issue that the tools would be uninstalled every time you install a Windows 10 feature update.

This does have a downside though, which is that Windows must go out to an update source to get the source files for RSAT (they are not included in the /Sources/sxs directory like the .NET 3 Framework source files are). This means you may have to allow clients to go out to Windows Update if they are installing RSAT!

The quick and dirty way to install all RSAT components is to open an administrative PowerShell window and use the command:

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'RSAT')} | %{Add-WindowsCapability -Name $_.Name -Online

This will look for all features that have RSAT in the name and install them.

If you get an error 0800f0954 it means the source location could not be reached. This may be because the machine you’re attempting to install RSAT on doesn’t have internet access or is configured to go to a WSUS or SCCM server for updates. If it’s the latter, you can enable a GPO which allows Windows to go to Windows Update for optional component installations only (while still getting regular updates from WSUS or SCCM). This GPO is in:

Computer Configuration > Administrative Templates > System > Specify settings for optional component installation and component repair

Tick the Download repair content and optional features directly from Windows Update instead of Windows Server Update Services (WSUS) and click OK to set the GPO. You can test this with local group policy to make sure it fixes the issue before deploying it to everyone!

Each of the RSAT components can also be installed individually if you don’t wish to install all of them. The list of RSAT components can be found on this Microsoft Docs page. Use the following PowerShell command to install the component you want, changing the name to match the component you want to install (these are listed as capability names on the linked Microsoft Docs page):

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.BitLocker.Recovery.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

You can install multiple components by simply duplicating the command and changing the name to match another component.

Uninstalling is a little more complicated as you cannot simply use the same catch-all PowerShell command to uninstall as you can to install. This is because some of the RSAT components have dependencies and if you try to uninstall them all the dependancies  will remain installed. To get around this, you can uninstall them in a specific order to ensure that all the dependant components are removed first.

I use the following order:

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.BitLocker.Recovery.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.CertificateServices.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.DHCP.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.Dns.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.FailoverCluster.Management.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.FileServices.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.IPAM.Client.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.LLDP.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.NetworkController.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.NetworkLoadBalancing.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.RemoteAccess.Management.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.RemoteDesktop.Services.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.Shielded.VM.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.StorageMigrationService.Management.Tools')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.StorageReplica.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.SystemInsights.Management.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.VolumeActivation.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.WSUS.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.GroupPolicy.Management.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'Rsat.ServerManager.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}

Making sure that the last three are Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0, Rsat.GroupPolicy.Management.Tools~~~~0.0.1.0 and Rsat.ServerManager.Tools~~~~0.0.1.0 seems to fix issues with dependant components.

Creating an SCCM application to deploy RSAT

To create an application in SCCM you will need three things: An install command, an uninstall command and a detection method. To cover the install and uninstall command, let’s create a PowerShell script with an install and uninstall function that can be called from the command line. The following PowerShell script is used to install and uninstall all RSAT components; if you want to pick and chose the ones you install or uninstall, modify it accordingly.

## Install all RSAT components
Function InstallRSAT {
    Get-WindowsCapability -Online | Where-Object {($_.State -notmatch 'Installed') -and ($_.Name -match 'RSAT')} | %{Add-WindowsCapability -Name $_.Name -Online}
}

## Uninstall each RSAT component so that no dependancies are left behind
Function UninstallRSAT {
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.BitLocker.Recovery.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.CertificateServices.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.DHCP.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.Dns.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.FailoverCluster.Management.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.FileServices.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.IPAM.Client.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.LLDP.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.NetworkController.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.NetworkLoadBalancing.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.RemoteAccess.Management.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.RemoteDesktop.Services.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.Shielded.VM.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.StorageMigrationService.Management.Tools')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.StorageReplica.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.SystemInsights.Management.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.VolumeActivation.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.WSUS.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.GroupPolicy.Management.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
    Get-WindowsCapability -Online | Where-Object {($_.State -match 'Installed') -and ($_.Name -match 'Rsat.ServerManager.Tools~~~~0.0.1.0')} | %{Remove-WindowsCapability -Name $_.Name -Online}
}

## Get the parameter passed to the script
$DeploymentType=$args[0]

## Run the install or uninstall function
if ($DeploymentType -eq "Uninstall") {
    UninstallRSAT
}

else {
    InstallRSAT
}

Copy that script and save it as Install-RSAT.ps1. Create your application in SCCM and go through the wizard, giving it a name, publisher and version. The source will be the location where you saved Install-RSAT.ps1. When you get to the install and uninstall commands in the wizard, you can use the following commands:

To install:
powershell.exe -ExecutionPolicy Bypass -File .\Install-RSAT.ps1 -DeploymentType Install

To uninstall:
powershell.exe -ExecutionPolicy Bypass -File .\Install-RSAT.ps1 -DeploymentType Uninstall

Next up is the detection method. For this, you will need to use a PowerShell detection method. The PowerShell will simply check that the RSAT optional components have their install state set to Installed:

$installed = Get-WindowsCapability -Online | where name -like RSAT* | where state -like Installed | select name
if ($installed) {
    return $true
}

That should be all you need! Deploy that to a Windows 10 1809 device and the user should be able to install RSAT from Software Center… as long as their device can go online to reach the source files.

Download Window 10 Enterprise 1809 with the Media Creation Tool (including en-GB and other language versions)

The October 2018 release of Windows 10 has just dropped and is now availabe to download using the Media Creation Tool. Using the GUI you can download the consumer ISO which contains the Home, Professional and Education SKUs of Windows 10.

If you want to download the Enterprise version of Windows 10, but don’t have access to Microsoft VLSC or Action Pack subscriptions, it is possible to download it using the Media Creation Tool if you know the right command line switches.

To download Windows 10 Enterprise 1809 using the Media Creation Tool, log in with a local administrator account (for some reason it isn’t good enough to  run the tool using Run as administrator, you actually do have to be logged in as an administrator) and download the tool. Open a CMD prompt and change directory to the directory you saved the Media Creation Tool in, and enter the following command:

MediaCreationTool1809.exe /Eula Accept /Retail /MediaArch x64 /MediaEdition Enterprise

When you’re prompted for a product key, you can use the Windows 10 Enterprise KMS client key from this site on Microsoft Docs.

This will download an ISO that contains the various Enterprise SKUs (Enterprise, Enterprise N,  Education, Education N, Professional and Professional N) with en-US installed and set to default. If you’d prefer to get en-GB, use the following command:

MediaCreationTool1809.exe /Eula Accept /Retail /MediaLangCode en-GB /MediaArch x64 /MediaEdition Enterprise

This will download an ISO containing the same SKUs as above, but with en-GB installed and set to default.

As far as I can tell, this works for any of the language pack region tags listed on this site. So for example, to download Windows 10 Enterprise 1809 with French installed and set to the default language, you can use this command:

MediaCreationTool1809.exe /Eula Accept /Retail /MediaLangCode fr-FR /MediaArch x64 /MediaEdition Enterprise

If you want to download the 32-bit version of Windows 10 Enterprise instead, you should change /MediaArch to x86.

When you have downloaded the ISO you may unpack it to find that the it does not contain an install.wim, but instead contains install.esd in the sources directory. Depending on what you are doing, you may need the .wim file (for example, if you’re planning to use it with SCCM). Thankfully obtaining a .wim file from the .esd is quite straightforward using DISM.

Open a CMD prompt and use the following command (changing the path for /WimFile to match where your install.esd file is):

dism.exe /Get-WimInfo /WimFile:C:\Temp\Windows10_1809\sources\install.esd

This will list each of the SKUs in the install.esd file. Make a note of the index of the SKU you want (in my case, I want the Enterprise SKU which is index 3).

DISM Get-WimInfo

Now use the following command to create a install.wim file which contains the SKU you want:

dism.exe /Export-Image /SourceImageFile:C:\Temp\Windows10_1809\sources\install.esd /SourceIndex:3 /DestinationImageFile:C:\Temp\Windows10_1809\sources\install.wim /Compress:max /CheckIntegrity

Make sure the path for /SourceImageFile and /DestinationImageFile are correct for you and change the /SourceIndex to match the index you noted earlier.

DISM Convert ESD

Once that is done you can delete the install.esd file if you want, to save space.

This process also works with earlier versions of Windows 10.

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.

ClientSettingsOffice365ClientAgent

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)

ADRCriteria

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.

ADRUpdatesPreview

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.

ADRSchedule

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.

ADRDeploymentSettings

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:

SoftwareCenterConfirmClientUpdate

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.

SoftwareCenterRebootPrompt

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!

OfficeUpdateBanner

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.

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.

Outlook 2016 slow loading issue

If there is one splash screen I have become sick of staring at over the past few weeks it is the one for Outlook 2016, with those irritating little dots sliding across the screen while Outlook is supposedly “Loading Profile” or “Processing”.

Urghhhhhhhhh

If you, like me, have spent quite a lot of time researching the cause of this problem you will probably have found that quite a lot of things can cause it. You’ve probably scanned and repaired your Outlook Data Files with SCANSPST.exe, created a new profile, repaired your Office 2016 install, opened Outlook in safe mode (or otherwise disabled all of the add-ins), removed any additional mailboxes or shared calendars and any number of other solutions you will find online.

These things probably do help to fix the issue if it is affecting only certain individuals, but what if the problem is affecting everyone in the office who upgrades to Outlook 2016? I had users who had previously been running Outlook 2010 or 2013 and were not seeing any problem with Outlook loading times, with it typically only taking between 10 to 15 seconds to load. But as soon as they were upgraded to Outlook 2016 it would take anywhere from 5 minutes to 15 minutes to load and some people reported times even longer than that.

A small curiosity about this problem was that it only affected the first launch of Outlook after turning on or restarting the machine. A user could close and reopen Outlook as many times as they liked once it had started the first time and it would load within a few seconds. Even more curious was that if a user did not launch Outlook immediately after logging in, but instead waited between 5 to 10 minutes before doing so, it would launch fine. I also found that if the machine was not connected to any network when Outlook was launched it would launch in just a few seconds. Obviously this meant Outlook was in offline mode and wasn’t very useful, but it did suggest that something was going on over the network that was causing Outlook to hang while loading.

To figure out the cause of this issue I started with a vanilla build of Windows 7 installed on a laptop with Office 2016 and connected it to the corporate network. I went through the steps of launching Outlook for the first time and setting up my profile and then proceeded to restart the laptop for the first proper test. The result? Outlook launched in 10 seconds. So it appeared the network on its own was not the cause.

Next I joined the laptop to the domain and left it in the Computers container. I also moved my user account into an OU with blocked inheritance. This meant that I could test with a minimal set of GPOs applied (the ones at the domain level only). I expected that I would have to apply one GPO at a time and test until I identified the GPO that was causing the problem (assuming that it even was a GPO causing the problem). However I did not have to go through that laborious process at all as the problem returned the first time I opened Outlook after adding the machine to the domain. This told me that one of the GPOs applied at the domain level was causing the problem.

That just left identifying which specific setting was causing the problem. Let’s see… a logon legal notice, some Internet Explorer site zoning and… hmm… the DNS suffix search list. Perhaps Outlook is taking ages looking for autodiscover on each of these domains? (Purely hypothesising; it wasn’t that).

As I was fairly sure the cause of the issue wouldn’t be the legal notice or the IE zoning I gave my attention on the DNS suffixes. There where quite a few of them so I thought it would be a good idea to first remove them all to see if the problem persists, and if it didn’t, add them back one at a time until the problem came back. To do this I navigated to the registry key HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows NT\DNSClient, made a note of what was in the SearchList value and then deleted it, leaving the SearchList value empty. I then amended the permissions on the DNSClient key to prevent Windows simply adding the values back in the next time GPOs where processed. To do this I right clicked on the DNSClient key and selected Permissions, clicked on Advanced and unticked Include inheritable permissions from this object’s parent and then in the security notice that comes up I clicked Add. This allowed me to freely change the permission I needed to change. I clicked OK to dismiss the advanced security settings box and in the simple permissions box, clicked on SYSTEM and removed the Full Control permission (leaving it with just Read). Following that I restarted the laptop and opened Outlook to see how long it would take; it took 10 seconds. Bingo.

Next I proceeded to add back one domain at a time to the SearchList registry value, each time restarting the laptop and opening Outlook until I struck on the one domain that was causing Outlook to take an age to load. I then confirmed that was the only domain causing the problem by adding them all back except that one, and once that was proven I added back the troublesome domain to prove that by adding it back, the problem returned. With about 50% of the root cause determined it was time to get Wireshark out and find out exactly why having that domain in the DNS search list was causing Outlook to load slowly.

After restarting the laptop I immediately opened Wireshark to began a trace and then opened Outlook. Once Outlook had finished opening I stopped the trace and started looking through. I noticed at the start of the trace that there where several DNS lookups being made for WPAD on each domain in the DNS search list.

Windows WPAD DNS Lookups

WPAD is the Web Proxy Auto-Discovery Protocol and is used by the WinHTTP Web Proxy Auto-Discovery Service and Internet Explorer to automatically find a proxy server on your network to route Internet traffic through. Windows checks for WPAD entries in each domain on your DNS search list even if you don’t use WPAD to assign a proxy. You may even believe you had WPAD turned off by having Automatically detect settings unticked in your Internet options, however this only turns it off for Internet Explorer. The WinHTTP Web Proxy Auto-Discovery Service still goes looking for them.

Most of the domains did not have a record for WPAD, however legacydomain.com did have a record and therefore wpad.legacydomain.com returned an IP address. This IP address was not assigned to any device (the device having been decommissioned at some point in the past) and this seems to be the key issue here. If no IP address is returned from DNS Windows moves straight on to the next domain, but if an IP address is returned Windows will attempt to route traffic through that IP address for several minutes before it gives up, and it is during this time that your Outlook client is hanging on the splash screen.

In this image you can see that my client attempted to send packets to 192.168.0.10 (the IP returned by wpad.legacydomain.com) several times with approximately 20 seconds between each attempt. The first packet was sent at 09:03:51 and the last packet (which is cropped out of the image below) was sent at 09:08:34 – 4 minutes and 45 seconds later!

Outlook Opening Wireshark Trace

As the network device that was originally assigned to 192.168.0.10 was no longer around I removed the DNS record for WPAD in the legacydomain.com DNS zone and the slow Outlook loading problem went away.

This explains some of the observations made at the beginning of this post regarding Outlook only being slow to load the first time you launch it and Outlook loading quickly if you wait a few minutes after logging on. The issue is not being caused by Outlook, Outlook is merely a victim of Windows’ HTTP proxy service latching on to the IP addresses returned by DNS and spending time trying to route traffic through them. If you wait a few minutes before launching Outlook or close and reopen it during the day the WPAD process has already happened so Outlook hasn’t got to wait for anything.

So if you are experiencing issues with Outlook starting slowly and have already tried everything you can find on the Internet, I would suggest making sure you haven’t got any stale WPAD entries in DNS! It could be a very quick fix for a very frustrating issue.

 

Windows 95 Special Edition

On August 24th 1995 Microsoft hosted a Windows 95 launch event at their campus in Redmond, Washington. At this event journalists and other attendees were gifted a copy of Windows 95 in a special commemorative box: Windows 95 Special Edition.

IMG_8300

Only 3,000 copies where handed out making this a fairly rare item to have (especially now, 22 years later).

The inside cover opens up and inside the following text can be found:

Screen Shot 2017-11-28 at 18.38.34

In the box you get a CD copy of Windows 95 – upgrade! Apparently Microsoft couldn’t bring themselves to give away free copies with a full license. You also get the owners manual, a leaflet introducing you to The Microsoft Network and a little Launch95: Introducing the world of Windows 95 insert.

And that’s it! The copy of Windows 95 is no different to the version you could buy in the shops, so the only thing about that this is special is the box.