Windows 10 Feature Updates – Using Custom Action Scripts

This is the next installment in my Windows 10 Feature Updates series.

Custom Actions

Beginning in Windows 10 1803, custom actions were introduced and represent what Suma SaganeGowda referred to as a “poor man’s implementation of Task Sequences” at Ignite 2018 in session BRK3027. Windows setup will check for the existence of these special folders and scripts and use them as part of the Feature Update process. They allow you to run scripts at various stages in the Feature Update process as well as specify reflected driver paths just as you can in Setupconfig.ini. Like Setupconfig.ini, Custom Actions has limited documentation, but don’t let the lack of documentation fool you, Custom Actions are pretty cool.

From the Microsoft Docs article:

Custom actions are .cmd scripts that run during the feature update process. These can be run during two phases of an upgrade:
1. Pre-install phase: This phase is when Setup starts, but prior compatibility checks. Actions during this phase are specified in preinstall.cmd.
2. Pre-commit phase: This phase is prior to the upgrade being applied and the system rebooting. Actions during this phase are specified in precommit.cmd.

Folder Structure

Custom Action folder structure includes run and runonce folders followed by a unique GUID folder and, if needed, a reflectdrivers folder. Your folders should look like this:

The GUID is something that you generate is simply a unique identifier folder. I contacted the Feature Update team regarding the GUID folder and this is their explanation for it’s existence:

The purpose of the GUID folder is to enable multiple authors. For example, different software vendors that wish to extend setup, independent of each other. Or, I assume the same could be said for multiple departments in a large corp that wants some level of autonomy.

*Note: My contact is confirming additional information regarding sequencing of the GUID’s/Scripts with engineering and I will update this post when I receive a response.

For the this post, I’m just using a single GUID folder that I created using PowerShell’s New-GUID command.

The key difference to note about the folders is that the run folder and it’s contents are all migrated during the Feature Update process, but the runonce folder is only used for a single Feature Update then removed during the upgrade process.

In the root of the run/runonce folders, you can include the following files to run actions at specific periods during the Feature Update process.

  • preinstall.cmd
  • precommit.cmd
  • failure.cmd

Since preinstall.cmd and precommit.cmd run before any files have moved, it is safe to include any supporting scripts in the run/runonce folders to be used by the cmd files. However, failure.cmd is copied to C:\~Windows.BT\Sources\Scripts before the reboot and the supporting files may not be present in the original folders if the Feature Update fails before the OS migration has been fully completed. To remedy this, I ensure that whatever scripts I reference in failure.cmd are stored in a temporary directory outside of any system folders. I create the hidden folder C:\~FeatureUpdateTemp\Scripts and cache the files there for failure.cmd to reference.

Using these files in conjunction with Setupconfig.ini’s PostOOBE and PostRollback command line options, you can perform most just-in-time actions on your clients without using a task sequence.

Usage Example

If you’ve spent much time on my blog, you’ve probably noticed my posts around 802.1x and how to manage it during Operating System Deployment and InPlace Upgrades. As we began looking at Feature Update servicing, we once again needed to handle 802.1x. We are using my whitelisting solution in production today. In our InPlage Upgrade Task Sequence, we will run the script to whitelist a device just before the upgrade begins. This ensures that the device won’t be challenged by our NAC to re-authenticate during the Feature Update reboot cycles. Now with Feature Update servicing, we use precommit.cmd to run the whitelisting script just before the first reboot into the SafeOS phase.

To achieve this, we simply create the required folder structure C:\Windows\System32\Update\Run\<GUID> and include precommit.cmd and our whitelisting script in the folder. Whenever the Downlevel phase is complete, it will run precommit.cmd and launch the PowerShell whitelisting script. Setup doesn’t check for success or failure, so be sure to include good error handling and logging with any of your scripts.

Sample Scripts

To deploy the files to the correct locations, I have another PowerShell script that we deploy as an Application in SCCM that will copy everything down. There’s nothing special about the Application model, I just like using it. 🙂 You can download the scripts from my repo You will need the following files:

  • Copy-FeatureUpdateFiles.ps1
  • Trigger-DCMEvaluation.ps1 (optional)
  • \Admin\Generate-CMDFiles.ps1
  • \Update\failure.cmd
  • \Update\precommit.cmd
  • \Update\preinstall.cmd
  • \Scripts\Process-Content.ps1
  • Update Copy-FeatureUpdateFiles.ps1 with your new GUID and TempDirPath (C:\~FeatureUpdateTemp is the default).
    • Copy-FeatureUpdateFiles.ps1 is configured to run the setupconfig.ini baseline (see previous post) if you specify the name of the baseline in the $BaseLineName parameter.
  • Update and Run Generate-CMDFiles.ps1 to build any cmd files that you want to include. It’s just an easy way to update your cmd files in the same way each time.
  • Create a new application and add these files & folders in as your source content (you can exclude the Admin folder from the source).
    • Install: Powershell.exe -ExecutionPolicy ByPass -File Copy-FeatureUpdateFiles.ps1
    • Repair: Powershell.exe -ExecutionPolicy ByPass -File Copy-FeatureUpdateFiles.ps1
    • Uninstall: Powershell.exe -ExecutionPolicy ByPass -File Copy-FeatureUpdateFiles.ps1 -RemoveOnly
    • Detection Method
      • Folder Existence rule: C:\Windows\System32\Update\<GUID> where <GUID> is your new custom GUID specified in the scripts.
      • Folder Existence rule: C:\~FeatureUpdateTemp\Scripts
    • Deploy the application to your target devices and verify that the files and folders are correctly created.

Error Handling

I have another post planned for SetupComplete.cmd and Failure.cmd which will fully cover these files. The GitHub repo contains everything you need to build everything out fully, there are just a lot of moving pieces to write up for the full picture here. Ultimately, you can use failure.cmd to run any post-failure script you would like.


If you have a need for pre-install and pre-commit actions that need to be triggered just-in-time as well as a way to handle failures, you should take a look at Custom Actions. They work very well! More to come on these in a future post.


  • Reply
    February 19, 2020 at 10:24 am

    This didnt work for me. All windows 10 1809 machine are still missing the background and lock screen. I can see that the script is running as its logging the information. These powershell are same script I use when machine is build.

    • Reply
      Adam Gross
      February 21, 2020 at 7:44 am

      I’m not sure I understand what you are trying to do here. I would need to see your code.

  • Reply
    Grant M.
    April 8, 2020 at 5:53 am

    If i populated both runonce and run locations – do you know if both set of scripts will be called and if so what is the order? runonce preinstall.cmd > run preinstall.cmd > runonce precommit.cmd > run precommit.cmd … Thanks! BTW new to your site, love the content

    • Reply
      Grant M.
      April 11, 2020 at 8:40 am

      I was actually able to test this myself (finally) … all folders in Run folder are run first then RunOnce , in ‘alphabetical’ order for each folder so Run 0001… then Run 0002.. then RunOnce 0001 > RunOnce 0002 (0001 and 0002 represent the first 4 digits of the GUID… I tested with 4 folders in each with staggered created/mod times – all eight were processed… pretty nifty! thanks for the info Adam.

  • Reply
    April 22, 2020 at 5:45 pm

    Hi Adam,

    I had an issue with previous IPU’s whereby if the upgrade started while on a VPN connection (AnyConnect), the upgrade will continue fine all the way through, however the VPN connection would be lost when it performs the first restart – the upgrade would continue perfectly fine, albeit there would be no comms back to the server until the upgrade is completed and a user logs in thus establishing a VPN connection again, and allowing the client to check back in. (i disabled status messages from this point to speed things up). But I was looking to see if I could reconnect to the VPN using a script and ideally ensure this script executed before the task sequence continues. My assumption would be to call my “VPN-Re connection” script during one of the custom actions you speak of. Do you by any chance have any experience with re-initiating/reconnecting to a VPN (AnyConnect) via script/automation. I found odds and ends but made no progress?


    • Reply
      July 13, 2020 at 11:11 am

      Neil, did you ever figure out any workarounds to your situation? I am in the midst of just starting to gather info and required items to enact Feature Updates for our journey from 1803 to 1909 and we also have large % of our users on Netscaler VPN (similar to AnyConnect, it requires end-user to reestablish VPN after login to Windows). Glad to hear though the upgrade will complete all the way over VPN; did you also do anything like pre-caching stuff, etc. to make that process as successful as possible?

  • Reply
    May 20, 2020 at 7:39 am

    Hey Adam,

    We use SCCM\WSUS to update our machines but they do not push driver updates. I was hoping to force the laptop to go to Windows Update online to install the updates after the upgrade is complete.

    I am trying to use this script, cscript WUA_SearchDownloadInstall.vbs /service:WU /automate, to force the updates. Could using the success.cmd option to kickoff the update?

    Thanks in advance!

    • Reply
      Adam Gross
      May 20, 2020 at 8:18 am

      I haven’t tried but I expect it will work fine.

  • Reply
    October 14, 2020 at 5:16 am

    If I add powershell script into preinstall.cmd and run some checks, if any of the checks fail i would like the update to fail too. How can I do this

  • Comment

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