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.


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.


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.


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:


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



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:


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:


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.


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.



Download the PowerShell Scripts and exported Task Sequence here.





36 thoughts on “Improving the User Experience in a ConfigMgr OS Upgrade Task Sequence

  1. Hi Trevor,
    Nice work! do you now if it is possible to pass the button click to a TS variable so we can make the user postpone the update ?

    1. Hi Bend, yes it’s possible but you would need to write the button click output to file and then run another script to read it, because the notifications run in the user context where you can’t read or write TS variables.

      As an example, if you add a button called “POSTPONE” to the notification, you could run this code in your notification script to write the output to file:
      New-WPFMessageBox @Params
      If ($WPFMessageBoxOutput -eq “POSTPONE”)
      # Set variables
      $SaveToRoot = “$env:SystemDrive”
      $SaveToDirectory = “temp”
      $SaveToLocation = “$SaveToRoot\$SaveToDirectory\”
      $FileName = “POSTPONEUPGRADE.txt”

      # Create the temp directory if doesn’t exist
      If (!(Test-Path $SaveToLocation))
      $null = New-Item -Path $SaveToRoot\ -Name $SaveToDirectory -ItemType “directory” -Force

      “TRUE” | Out-File -FilePath “$SaveToLocation\$FileName” -Force
      Then run the following script in another TS step to read the file and write to a TS variable:
      # Read the POSTPONEUPGRADE.txt file
      $ReadFromRoot = “$env:SystemDrive”
      $ReadFromDirectory = “temp”
      $ReadFromLocation = “$ReadFromRoot\$ReadFromDirectory\”
      $FileName = “POSTPONEUPGRADE.txt”
      $PostponeUpgrade = Get-Content “$ReadFromLocation\$FileName” -ErrorAction SilentlyContinue

      If ($PostponeUpgrade -eq “TRUE”)
      # Set the TS variable
      $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
      $tsenv.Value(“PostponeUpgrade”) = “true”

      # Cleanup file
      Remove-Item “$ReadFromLocation\$FileName” -Force
      # Set the TS variable
      $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
      $tsenv.Value(“PostponeUpgrade”) = “false”

      1. Thanks for quick response.. could be awesome if it was possible to display a counter, with a delay so the user eventually was forced to do the upgrade, like with Powershell app deploy toolkit.. do you know if that is possible in user context ? Or I could just use Powershell app deploy to create the file, and then kick off your script..

        Could be nice to keep it all in your script though 🙂

      2. That is possible but would require some custom WPF added to the notification. You would also need a mechanism to wait for a response as the TS will not wait by default.
        But I’m intrigued why you would want to postpone a running TS. Why not just make it available with a deadline?

      3. Because users forget, and if the deadline is passed, it will be forced and interrupt their work.. I work at a law firm, those are not easy users 🙂 they would just keep postponing it with the first solution I guess..

  2. Hello, great work. I’ve tested this out and all works well except for the final notification that the upgrade is complete when the user logs on. For some reason the runonce doesnt trigger for the user when logging in after the upgrade. Any ideas?

      1. Hi Trevor, the file is in c:\temp and runone key set, but just doesnt run at the next logon. Running the command manually works fine.

      2. Hi, looking into the RunOnce keys only work if the user logging on is an administrator (this works). However this wont be the case normally so need to find alternative method.

  3. To work around this, I’ve used an ActiveSetup to run the PowerShell script and as the last step in the script, deleted the script from c:\temp so that this only runs for the first user who logs onto the machine. This appears to work well.

  4. Hi, I have a couple questions about this. Is the custom background supposed to survive a reboot or do we need to run it after every reboot during the upgrade process? Another question: is there a way we can bring up a command prompt without making the custom background go away? Kind of like the ability to press F8 in WinPE. Also, it would be nice if it showed during the lock screen too so the user doesn’t try to login. Thanks for all the work you’ve been doing on this.

    1. Hi Chris, the custom background only displays (and is only needed) during the online (down-level) phase of the upgrade. After the first restart, windows setup enters WinPE where the user experience is streamlined with upgrade methods. The custom background only masks the desktop which is still running, so you can still do ctrl-alt-del for example to bring up task manager and start a process, or Alt-Tab to switch applications.

      1. Right, I understand that, but our task sequence has custom reboots both before and after the upgrade. First we run the readiness and assessment, then update the BIOS and reboot, then OS upgrade happens. After the upgrade there is some hardware changes that might cause a reboot and then there a few steps that happen after that, and an install updates step.

      2. That would be difficult to achieve unless you reboot into WinPE and do your extra activities there, or configure an auto logon then display the custom background again. But even then the ConfigMgr client takes an minute or two before it’s ready and the TS continues. There’s not a lot you can do at the Windows ctrl-alt-delete / lock screen to my knowledge.

  5. Thanks for this! I am having an issue though where if I run the pre-cache task sequence, I can see the files in the cache. Once i run the actual upgrade I can see it still downloading file to the working directory, so I have 2 copies of the content on the machine. Any ideas?

      1. Ah, that will do it. Unfortunately I was trying to avoid that because we want to have a single task sequence for multiple models…and we download drivers specific to each model…I’ll keep looking, thanks!

  6. I have integrated this and when a win10 upgrade fails during the “upgrade the Operating System” step the custom background never goes away, so the user is unable to see the error message unless you right click the custom background.

    Any ideas?

    1. That should not be the case if you set the Upgrade Operating System to continue on error as per the example task sequence. The system should reboot and then the task sequence will handle the error with the custom steps.

      1. The upgrade OS step failed before a reboot was initiated (0x80004005), so the reboot never occurred and the custom background window stayed in the foreground. The TS Notification was hidden and the error message was shown, but only after I right clicked the purple screen could a user see the error message.

      2. Sounds like your TS is not configured as per the example? Question is: if the Upgrade OS step is set to continue on error, why did it not continue? Only if it continues can we handle the error with a custom notification.

  7. Great stuff Trevor! Is there a way to email me when the TS initiated and failed with the subject contains computer name and model ?

    1. Certainly that’s possible, you can simply add a PS script that uses Send-MailMessage or the .Net mail classes. But when sending email from a client you need to check whether your smtp server permits a relay and whether authentication is required and which address you can send from.
      If you have Orchestrator you can also use that to send email, which is something I do for our OSD task sequences.

  8. Thanks Trevor for the quick reply! Does this work from windows 7 to 10? I got this error when the custom background ran

    ” Add-Type : (0) : Metadata file ‘System.Runtime.InteropServices.dll’ could not b RunPowerShellScript 12/20/2017 4:11:42 PM 2980 (0x0BA4)
    e found RunPowerShellScript 12/20/2017 4:11:42 PM 2980 (0x0BA4)
    (1) : using System; RunPowerShellScript 12/20/2017 4:11:42 PM 2980 (0x0BA4)
    At C:\_SMSTaskSequence\Packages\PR10056E\Invoke-PSScriptAsUser.ps1:283 char:9 RunPowerShellScript 12/20/2017 4:11:42 PM 2980 (0x0BA4)
    + Add-Type <<<< -ReferencedAssemblies 'System', 'System.Runtime.InteropService RunPowerShellScript 12/20/2017 4:11:42 PM 2980 (0x0BA4)
    s' -TypeDefinition $Source -Language CSharp -ErrorAction Stop RunPowerShellScript 12/20/2017 4:11:42 PM 2980 (0x0BA4)
    + CategoryInfo : InvalidData: (error CS0006: M…ld not be found: RunPowerShellScript 12/20/2017 4:11:42 PM 2980 (0x0BA4)
    CompilerError) [Add-Type], Exception RunPowerShellScript 12/20/2017 4:11:42 PM 2980 (0x0BA4)
    + FullyQualifiedErrorId : SOURCE_CODE_ERROR,Microsoft.PowerShell.Commands. RunPowerShellScript 12/20/2017 4:11:42 PM 2980 (0x0BA4)
    AddTypeCommand RunPowerShellScript 12/20/2017 4:11:42 PM 2980 (0x0BA4)"

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s