Windows 10 Feature Updates – Testing the /MigNEO Disable Parameter

Over the past few weeks I’ve been testing re-writing my Windows 10 Feature Update repo to make it easier to implement – if you haven’t tried it, go check it out. Just follow the readme in the repo https://github.com/AdamGrossTX/Windows10FeatureUpdates.

During the re-write I was reminded that there were a few command line parameters that I hadn’t experimented with. One of them is /MigNEO which only has a Disable option. According to the product group, NEO stands for non-event objective which, doesn’t help it make more sense to me. Anyway, MigNEO is designed to give IT Admins more control over how Feature Updates are applied. Windows Setup Command-Line Options | Microsoft Docs

The default behavior of Feature Updates since 1803 is to perform MOST of the upgrade operations while the device is still in use. You may see this referred to as Downlevel/Online Time/Online Phase, etc. It means that the Feature Update can be processing the upgrade while the user is still working with a shorter Offline time where the device is applying the update and unusable. /MigNEO Disable removes this optimization and performs more actions during the Offline phases with a much shorter Downlevel/Online time. See the troubleshooting docs for info on each phase Troubleshoot Windows 10 upgrade errors – Windows IT Pro – Windows Deployment | Microsoft Docs.

Upgrade process

Comparison Tests

Note: All tests were done on clean VMs with no apps or data to migrate. Real-world results may vary.

I decided that I needed to test /MigNEO Disable and see what the difference was really like and built 6 Hyper-V VMs (2 each with 1909, 1809, 1709) and upgraded them to 2004 using the 2004 ISO media. Each upgrade used the initial release version of 2004 so that the devices would also download Dynamic Updates during the process. I ran the tests several different times and the results were pretty consistent. I attempted to run all 6 VMs at the same time, and while it worked, the upgrades took about 1.5hrs to complete so I switched to just running 2 at a time and they completed in under 45 minutes.

Here are the command lines that I used. You could also use SetupConfig.ini for this, I just used the command line for quick testing:

I recorded the upgrades and uploaded them for your viewing pleasure. It is literally 50 minutes of feature updates in realtime. I added milestone text in the videos so you should be able to scroll through and see the text change.

The results were interesting. All of the machines with /MigNEO Disable rebooted around the 8 minute mark. while the default machines rebooted between 29-34 minutes. That means that the users would have been able to work for ~25 minutes longer before the first reboot than with /MigNEO Disable.

The key takeaway for me here was that using the default setting, the devices were offline for under 7 minutes, compared to up to 30 minutes when disabled!

So which is better?

As I was testing, I kept wondering why anyone would want to disable the optimizations and cause users to be offline for so long. I got some feedback on Twitter as well as from the product team on why this option is useful. https://twitter.com/AdamGrossTX/status/1303079790921080833?s=20

According to the product team:
By default NEO is enabled. So we spend more time down level with lower priority and if you have other install tasks while pending reboot, the staged feature update gets invalidated and you end up spending more time offline also. So MigNEO was added to give more control to you.

Gary Blok brought up an interesting point as well. Applying the upgrade with a task sequence from ConfigMgr, if something happens during the Downlevel phase and task sequence fails, it is difficult to get the ConfigMgr client back online and/or restart the task sequence, so having a short offline time is more desirable in this case as well.

On the Task Sequence note, you should be able to add /MigNEO Disable to your ConfigMgr Task Sequence if you are using one. See OSDSetupAdditionalUpgradeOptions in the docs Task sequence variable reference – Configuration Manager | Microsoft Docs for more info. Johan Arwidmark has a great blog on using it as well Specify additional command-line options to Setup during a Windows 10 upgrade when using ConfigMgr – Deployment Research (deploymentresearch.com). My guess would be that your TS would reboot into the Safe OS phase within 8 mins of starting the Upgrade Operating System step, instead of 30+ mins of sitting in Windows watching the progress dialog.

Digging Deeper

If you’ve ever looked through setupact.log, you’ll know that it has a TON of info and can be daunting to dig through. I attempted to find references to MigNEO in the logs and pull out relevant bits. Here are a few entries that give us clues about whether NEO is in use or not. (Copy to notepad for easier viewing then search for NEO to see the entries.)

Summary

For our environment, we’ve found that using the default (NEO enabled) is optimal. It reduces user downtime and is almost a non-event for most users. The upgrades take almost the same time with either setting, so choose the option that fits your needs best.

1 Comment

  • Reply
    adampskib
    September 15, 2020 at 1:51 am

    Great digging and nice pres of the detail thanks Adam

  • Comment

    This site uses Akismet to reduce spam. Learn how your comment data is processed.

    1,858

    Fatal error: Uncaught GuzzleHttp\Exception\ClientException: Client error: `POST https://dc.services.visualstudio.com/v2/track` resulted in a `400 Invalid instrumentation key` response: {"itemsReceived":1,"itemsAccepted":0,"errors":[{"index":0,"statusCode":400,"message":"Invalid instrumentation key"}]} in /opt/bitnami/apps/wordpress/htdocs/wp-content/plugins/application-insights/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:113 Stack trace: #0 /opt/bitnami/apps/wordpress/htdocs/wp-content/plugins/application-insights/vendor/guzzlehttp/guzzle/src/Middleware.php(66): GuzzleHttp\Exception\RequestException::create(Object(GuzzleHttp\Psr7\Request), Object(GuzzleHttp\Psr7\Response)) #1 /opt/bitnami/apps/wordpress/htdocs/wp-content/plugins/application-insights/vendor/guzzlehttp/promises/src/Promise.php(203): GuzzleHttp\Middleware::GuzzleHttp\{closure}(Object(GuzzleHttp\Psr7\Response)) #2 /opt/bitnami/apps/wordpress/htdocs/wp-content/plugins/application-insights/vendor/guzzlehttp/promises/src/Promise.php(156): Guzzle in /opt/bitnami/apps/wordpress/htdocs/wp-content/plugins/application-insights/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php on line 113