Improving the User Experience in a ConfigMgr OS Upgrade Task Sequence

Update 24th Nov 2017

  • Fixed the issue where the Upgrade Successful notification does not display for non-admin users. Thanks to a tip from Carl (see comments) I used a somewhat ancient mechanism called ActiveSetup that is still available in Windows 10.
  • The custom background displayed during the online phase of the upgrade now displays on all screens if multiple monitors are being used. Thanks to Ronni Pedersen for the kick ūüôā
  • These changes have added a couple more scripts to the download, but the task sequence remains unchanged, so simply update your notifications package in ConfigMgr.

When upgrading to Windows 10 from a ‘down-level’ OS, or to a new version of Windows 10, using installation media, you get a nice UI that guides you through the installation process.

WindowsSetup2

Upgrading using an OS upgrade task sequence in ConfigMgr however, is a comparatively cold experience with no UI except for the TS Progress UI – assuming you enable that. For an IT admin of course, we don’t necessarily care about having a nice UI, we just care that it works and we have log files to check if it doesn’t. But for an end user that can be a different story. It may be a little disconcerting to some that their system is being upgraded yet the upgrade process is providing little feedback about what is happening. Once you get past the online phase of the upgrade however, the experience is more streamlined.

In an OS upgrade task sequence, Windows Setup will be running silently in SYSTEM context so it will not display anything to the logged-on user. Everything is handled by the task sequence. If the task sequence fails, the user might feel panicked and wonder if they have lost any of their data or applications. There is nothing to reassure them otherwise.

We may not be able to reproduce the nice Windows Installer UX, but we could at least add a few custom notifications at different points in the TS to provide some feedback to the end user and improve the overall experience from their perspective.

I experimented with this a bit using my New-WPFMessageBox PowerShell function and the following is what I came up with.

At the start of the upgrade task sequence, I like to check the currently-installed Windows version because – strange but true – the Windows Setup process will not prevent you from ‘upgrading’ to a version you are already running! How’s that for a time-waster?! Of course, you would try to avoid that with correct collection targeting in ConfigMgr, but just as an insurance I check that the system is not already running that version, and if it is, display the following notification to the user, then exit the TS.

AlreadyUpgraded

Next, during the online phase of the Upgrade Operating System step, I display a custom background. This is just to discourage the user from working or rebooting the computer and provides some extra assurance that something is actually happening. This is actually a WPF window that fills the screen, not a desktop wallpaper.

OSUpgrade

I also run the compatibility scan first and if that fails, I notify the user with the error code and description that they can contact IT support with:

CompatScanFail

The same if the upgrade fails, or if a rollback is performed, although no descriptions here as there are many possible result codes.

OSUpgradeFail

OSUpgradeFailRollback

Finally, when the OS upgrade successfully completes, the first user who logs in will see the following notification giving them some hyperlinks to what is new in the upgraded OS:

UpgradeComplete

Using my New-WPFMessageBox function you can customise these notifications as you please.

To make it simple, I have included here an export of an OS upgrade task sequence that you can import into your environment as a basis or an example of how to add such notifications. Here’s a screenshot:

TaskSequence

I’ve also made available all the PowerShell scripts I used as a download. Simply create a standard package in ConfigMgr containing the all the scripts in the same directory and distribute the content (no program required). Update the imported task sequence to reference this package for each of the Run PowerShell script steps, and also reference your OS Upgrade package in the relevant steps.

Some important things to note:

  • The notifications display in the context and session of the logged-on user. This is accomplished by calling the notification scripts via another script – Invoke-PSScriptAsUser.ps1 – that creates a PowerShell process in the user’s context.
  • Where a notification is displayed, I also first hide the TS Progress UI using the¬†TSDisableProgressUI variable, which is available since ConfigMgr Current Branch 1706. This is because the notification will display behind the TS Progress UI, although if there are no further steps to complete after the notification is displayed it doesn’t matter too much because the TS Progress UI will not display for long anyway. The task sequence will not wait for the user to respond to the notification before it continues processing any remaining steps.
  • Where the compatibility scan or OS upgrade fails, the step is set to continue on error so that we can handle the error ourselves. After displaying the error notification, we manually fail the TS using the _SMSTSOSUpgradeActionReturnCode TS variable value as the error code.
  • Where the compatibility scan or OS upgrade fails, we write out the return code to a file so that the custom notification, which runs in the user context, can read in the value. This is because the task sequence variables are only available to query in the SYSTEM context – the user context cannot read them.
  • In handling a failure I set the¬†SMSTSErrorDialogTimeout TS variable to 1 second so that the TS fails quickly and the user is left with our custom error notification instead of the default TS one.
  • The final notification that the upgrade was successful displays for the first user that logs in after the TS has completed. This is because the OS Upgrade TS simply ends at the Windows lock screen where we cannot display anything. Before the TS ends, we copy the notification script to a temp location and set the RunOnce registry key to call it.
  • Pay attention to the step conditions for the groups in the task sequence, as this controls the logical flow of the sequence.
  • Make sure to “Ignore dependency” when importing the task sequence

Pre-caching Content

Another important activity that should be done before making an OS Upgrade task sequence available is to pre-cache as much content as possible on the target systems. Unless the content is already in the ConfigMgr client cache when the TS runs, it’s gonna need to download that content which, for an OS Upgrade TS, is a sizeable amount of data and could add significant time to the execution of the task sequence making for a poorer experience for the end user.

Since ConfigMgr 1702, we have had the ability to pre-download content for a task sequence, and this was improved a bit in 1706, but in my own experience I have not found it to do quite what it says on the tin. Specifically, this line in the documentation –¬†When the client receives the deployment policy, it will start to pre-cache the content. – appears not to be true (at the time of writing with 1706). Even when you have correctly set the OS Architecture and language on the OS Upgrade package, and set the required conditions on the Upgrade Operating System step, no content is actually cached on the client until the date the deployment becomes available. That is, you can target a system with a deployment that has an available date in the future, and theoretically it should start caching content as soon as a machine policy refresh occurs. But in practice, it does not cache any content until the available date of the deployment is reached, then shortly after it will start to download the content. If the user decides to upgrade as soon as the deployment becomes available, they will need to wait for the content to download first. If anyone has a different experience with this, please let me know!

Until that is fixed, we can still pre-cache most of the content by creating a hidden task sequence that uses the Download Package Content step. Make sure to use the Configuration Manager client cache as the location.

TaskSequence2

Check the option to Suppress task sequence notifications on the TS properties, and deploy the TS to the target systems before you deploy the OS Upgrade TS.

SuppressNotifications

Download

Download the PowerShell Scripts and exported Task Sequence here.

 

 

 

Add Custom Notifications to a ConfigMgr Task Sequence

One feature I would really like to see added to a Configuration Manager task sequence is the ability to natively provide notification messages to the logged-on user. Previously, to accomplish this, I have used simple pop-up notifications like the¬†Wscript Shell Popup method in a PowerShell script, together with the handy ServiceUI utility in MDT to display the notification in the logged-on users’ session. This has worked well enough for simple messages, and has been useful in several scenarios. For example, see my blog post about prompting for input during a task sequence.

Recently I wrote a PowerShell function to display my own custom notifications using WPF, called New-WPFMessageBox. This allows for much greater customisation of the message box, including adding your own WPF content. So I decided to revisit displaying notifications during a task sequence using this new function instead. In this post I will show you how to add a “Restart Required” notification to run at the end of a task sequence. This can be used to advise the user that a restart needs to take place after the installation of some software for example, and give them the option to restart immediately, or restart later.

RestartRequired

Instead of using the ServiceUI utility – which works well, but it still runs in SYSTEM context even though it will allow you to display in the logged-on users’ session – I decided on a different method that allows you to truly run a process in the users’ context. Thanks to a tip from Roger Zander I found some C# sharp code by a guy named Justin Murray that can be used in PowerShell to make this possible.

Invoke-PSScriptAsUser

Create a new PowerShell script containing the following code. In the $Source variable, copy and paste the C# code from https://github.com/murrayju/CreateProcessAsUser/blob/master/ProcessExtensions/ProcessExtensions.cs. I have renamed the namespace (line 4 in the C# code) from namespace murrayju.ProcessExtensions to namespace Runasuser.


Param($File)

$Source = @"

"@

# Load the custom type
Add-Type -ReferencedAssemblies 'System', 'System.Runtime.InteropServices' -TypeDefinition $Source -Language CSharp -ErrorAction Stop

# Run PS as user to display the message box
[Runasuser.ProcessExtensions]::StartProcessAsCurrentUser("$env:windir\System32\WindowsPowerShell\v1.0\Powershell.exe"," -ExecutionPolicy Bypass -WindowStyle Hidden -File $PSScriptRoot\$File")

Save this script as Invoke-PSScriptAsUser.ps1

Display-RestartNotification

Create a new PowerShell script containing the following code. At the top paste in my New-WPFMessageBox function from https://gist.github.com/SMSAgentSoftware/0c0eee98a673b6ac34f5215ea6841beb. You can, of course, customise the notification as you wish.


# Paste here New-WPFMessageBox function from https://gist.github.com/SMSAgentSoftware/0c0eee98a673b6ac34f5215ea6841beb

$Params = @{
    Content = "You must restart your computer before using Software X."
    Title = "Computer Restart Required!"
    TitleFontSize = 20
    TitleFontWeight = "Bold"
    TitleBackground = "OrangeRed"
    ButtonType = "None"
    CustomButtons = "RESTART NOW","RESTART LATER"
    Sound = 'Windows Notify'
}

New-WPFMessageBox @Params
If ($WPFMessageBoxOutput -eq "RESTART NOW")
{
    Restart-Computer
}

The function saves the content of the button you click to the variable $WPFMessageBoxOutput, so you can use this to perform certain actions depending on which button the user clicks, in this case simply restarting the computer. This variable is only available in the script scope however.

Save this script as Display-RestartNotification.ps1.

Create a Package

Now create a standard package in ConfigMgr containing both of these scripts in the same directory, and distribute the content. No program is required for the package.

Configure Task Sequence

In your task sequence, add a Run Powershell Script step. Reference the package you created and enter the script name and parameters:

Script name: Invoke-PSScriptAsUser.ps1

Parameters: -File Display-RestartNotification.ps1

TS

When the task sequence executes, it will run the Invoke-PSScriptAsUser.ps1 in SYSTEM context, which will in turn run PowerShell in the logged-on users’ context and run the Display-RestartNotification.ps1 script, which displays the notification to the user.

The task sequence will not wait for the user to respond to the message; it will simply finish up in the background and the notification will remain on screen until the user responds to it.

If you enabled the option to Show task sequence progress then the notification will display behind the task sequence progress UI. Since this is the last step in the sequence it doesn’t matter, but if you have other steps running after the notification, you should hide the task sequence progress UI at that point. Since ConfigMgr 1706 we have the TSDisableProgressUI task sequence variable that can do that for us, so simply place a step before the notification step disabling the progress UI:

tsui

The ability to run a process in the user context during a task sequence is quite useful, not just for displaying notifications, but for running any code or process that must run in the user context, for example setting HKCU registry keys, or triggering a baseline evaluation that has user-based settings.

ConfigMgr OS Upgrade TS W10 1709 Does Not Care About Windows Edition

Today I ran a ConfigMgr OS Upgrade task sequence configured to use the Enterprise edition of Windows 10 1709 on a workstation that had Windows 10 Pro 1703 installed. Since the VLC media for 1709 contains the various editions in different indexes, you are supposed to choose the relevant one in the Upgrade Operating System step.

OSUpgrade

Of course, I expected the TS to fail because the configured edition is different to the edition that TS was being run on – but to my surprise, it didn’t care and the TS succeeded! Windows 10 Pro 1703 was upgraded to Windows 10 Pro 1709.

Not sure if that’s a bug or a feature, but it’s actually quite convenient!

Setting the Default Wallpaper for Windows 10 during ConfigMgr OSD

So finally I got around to doing some Windows 10 deployments (better late than never :)) and I wanted to set the default wallpaper during Operating System deployment with Configuration Manager. I’ve seen a few posts out there about how to do that, but they all run during the OS phase of the deployment, which means you have to take ownership and set permissions on all the image files. However, ¬†you can bypass those permission changes by setting the wallpaper during the WinPE phase of the deployment instead.

I wrote a post about that before, but it was only tested on Windows 7. In Windows 10, the process has changed a bit. In addition to the \Windows\Web\Wallpaper\Windows directory that contains the “img0.jpg” default wallpaper image, there is now also a¬†\Windows\Web\4K\Wallpaper\Windows¬†directory, which contains images of various resolutions. It seems that if the screen resolution is set to the resolution of one of the images in the 4K directory, it will use that one as the default.

This means that we need to provide image files of equivalent resolutions in this directory as well as the standard wallpaper directory. The following image files and resolutions are required:

img0.jpg
img0_768x1024.jpg
img0_768x1366.jpg
img0_1024x768.jpg
img0_1200x1920.jpg
img0_1366x768.jpg
img0_1600x2560.jpg
img0_2160x3840.jpg
img0_2560x1600.jpg
img0_3840x2160.jpg

The “img0.jpg” image file can be around 1920×1200 to match the default size.

Since I am using MDT-integrated ConfigMgr (and you are too, right?!), I’m simply going to place all these image files in the Scripts directory in my MDT toolkit package, together with the following PowerShell script which will replace the existing image files with the new ones.

If you are using standalone ConfigMgr, then you will need to create a package containing the image files and PowerShell script, scrap the TS variable section at the top of the script, and change the variables used:

$ScriptRoot > $PSScriptRoot

$OSDTargetSystemRoot > $env:windir

After updating the MDT toolkit package to my distribution points (or distributing the package for standalone ConfigMgr), I can add a step to my OSD task sequence to run a PowerShell script. For MDT-integrated, I can use the MDT step Run PowerShell Script:

ts

If standalone ConfigMgr, I can use the ConfigMgr step Run PowerShell Script and reference the package:

ts2

I am adding this step during the Post Install phase, just after laying the OS image on the disk.  At this point, we are still in WinPE, so we are not changing the online OS and therefore no permission changes are required.

And that’s it! ¬†Now all the default wallpaper images will be replaced with my custom ones. ¬†The beauty of doing this during OSD is that the end user can still change the default image if he wants to. If you prefer to lock the default wallpaper, then consider using group policy instead.

New tool: ConfigMgr PXE Boot Log

Today I’m releasing a new tool for ConfigMgr admins and IT support staff! ¬†This tool displays PXE boot events in an easy-to-view format and provides a history of PXE boot attempts on a distribution¬†point during a selected time period. The tool can also display any records that exist in ConfigMgr associated with a device based on its SMBIOS GUID. The ‘log’ entries returned come from the status messages sent by the distribution point and not from the SMSPXE.log.

PXEBoot

The tool can help in troubleshooting PXE boot failures such as devices not being targeted with a deployment, duplicate records in the ConfigMgr database, or mismatched unique identifiers because of hardware changes.

Requirements

  • Minimum PowerShell 5
  • Minimum .Net Framework 4.5
  • Minimum read-only access to the Configuration Manager¬†database (db_datareader role)

Installation

The tool can be downloaded and installed from the Technet Gallery.

Configuration

  • For first time use, click the “More Options” menu (3 dots, top-right) and choose Settings
  • Enter your Configuration Manager SQL Server and Database
  • Optionally select to view dates and times in your local timezone instead of the default UTC.
  • Click Save.

Using the Tool

  • Select a PXE-Enabled distribution point from the drop-down list.
  • Select a time period to view results
  • Click Retrieve Log
  • Double-click any log entry to retrieve the associated record/s for the device in ConfigMgr

Source Code

Source code is available on GitHub

Downloading the Latest Dell Driver Packs with PowerShell

It was a regular Tuesday morning and I hadn’t yet had my ‘PowerShell fix’ for the week, so¬†when I realised I needed to download a new driver pack from Dell for my ConfigMgr OS deployments, I could hear a¬†faint voice calling out to me: ‘Dude, I can make your life easier! Work smarter, not harder!

Of course, that’s only ever partially true, because with PowerShell you must work harder today in order to work smarter tomorrow, but in the interest of long-term benefit I proceeded to fire up the ISE.

Suddenly, a thought arose from my subconscious: ‘Wait just a second. Don’t re-invent the wheel. ¬†Aren’t there already some good solutions out there for this?

Well yes, that’s true,‘ my internal musings continued. ‘Most notably, we have a very cool tool by Maurice Daly – the Driver Automation Tool. With this we can just click buttons and go get coffee while the tool does all the hard work. It’ll even import the driver packs into SCCM. I like that!

Yes that is awesome.‘ I responded to myself, ‘Problem is,¬†I still need a PowerShell fix. So maybe I can find a different way of downloading driver packs. What do you suggest?

Well, we have the Dell Driver Pack Catalog. Dell even provide examples of how to use that with PowerShell to find the URLs you need to download the relevant cab files.

Yes, this is cool too. But I think there is still another way. Doesn’t Dell’s TechCenter wiki contain the download URLs for the most recent driver packs?

Yes, it does. But you want to use PowerShell, right?

Correct.’

So what are you thinking?

Web-scraping.

Ah, you bad boy! Let’s do it!

Dell maintains a wiki page containing links to the latest driver packs which can be found here:

http://en.community.dell.com/techcenter/enterprise-client/w/wiki/2065.dell-command-deploy-driver-packs-for-enterprise-client-os-deployment

You simply find the model and operating system version you want and click the link, which leads you to another wiki page containing a download URL.

Rolling up the sleeves, I whipped up some code that will scrape these web pages to find the download URL for the current driver pack version and download it using a BITS transfer. You can then import or add the driver pack into ConfigMgr for OSD using your favourite method (which is PowerShell, right?!)

The resulting script is quite simple to use and works reliably in my testing, although it takes a few seconds to filter the HTML in order to find the appropriate download URL.

You can download a driver pack for a single model, for example:


Download-LatestDellDriverPack -Model "Latitude E7470" -OperatingSystem 'Windows 10' -DownloadDirectory C:\DriverPacks -Verbose

Just provide the model name, operating system version and a location to save the downloaded file to. Support is provided for verbose output.

You can also pass a list of models to the script and it will download each one in turn, for example:


"M4800","Optiplex 9020","E6420","E5250" | Download-LatestDellDriverPack -OperatingSystem 'Windows 7' -DownloadDirectory C:\DriverPacks -Verbose

drivers
The script in action

The script will work for any driver pack with an operating system Windows 7 or higher (are you really deploying anything older than that?!), and there is no proxy support currently.

Here’s the full script:

New Free App – ConfigMgr Deployment Reporter

Just released a new free application for ConfigMgr admins РConfigMgr Deployment Reporter.  I developed this app for use in the organisation I currently work for, and it turned out quite well, so I decided to release a public version to the community!

capture

I developed this app as an alternative (and IMO easier) way to report on ConfigMgr deployments than using the ConfigMgr console. It uses a little different format than the console node allowing you to select which deployment you wish to view data for based on the “feature type” (ie application, package etc) and report on only that deployment. ¬†It also introduces a separation of results between all applicable systems for a deployment, and only those systems which have currently reported status, which allows for a more accurate view of the success of a deployment as it progresses.

The app allows the creation of charts and HTML-format reports to give a nice graphical snapshot of a deployment.

I also added the capability to report per-device for Software Update and Task Sequence deployments.  For Software Updates, this allows you to see which updates from the deployment are applicable to the machine and the status of each update, and for Task Sequences it allows viewing the execution status of each step in the task sequence for the selected device.

As usual, I code purely in PowerShell using WPF for the UI. ¬†This time I added metro styling using the excellent MahApps.Metro project ūüôā

Download the app from here.