Recently, I was assigned to project work where I needed to manage configuration settings for users of newly deployed Windows 7/8 computers and an old familiar trick no longer worked. Previously, the use of built-in Windows functions, such as writing information to the RunOnce key of the default user profile, worked well. A colleague clued me in to a new and more enlightened way of running these processes using the Task Scheduler in Windows 7 or Schedule Tasks in Windows 8. There are some special considerations I found while doing this.
Differences in Design
There are two special registry keys both named RunOnce. These keys exists in the machine (HKEY_LOCAL_MACHINE) and user (HKEY_CURRENT_USER) registry hives—and both of these locations behave similarly. These keys can contain as many values as desired and each value contains data that points to an application or command to run at user logon. After the command is run, it is automatically removed from the registry by Windows so that the command would never run again and hence the name RunOnce. This was effective to use in situations where it was necessary to run a configuration process as a user, even when you did not know who the user would be at the time of deployment, which would configure applications or settings as that user for that user. However, this does not work unless the user is a local administrator and best practices discourage this unless necessary.
In order to accomplish the same result, we need to adjust our design through the use of a more modern means; the execution of a scheduled task through an event trigger—in this case a user logon event can accomplish the same result. This plays out well for having our command run for our user at logon, however if we do nothing to curb future executions of the process it will always run when the user logs on. This may or may not be desirable and leads us to our first design decision. There are a couple options; allow the scheduled task to always run at logon every time a user logons or track the execution of the scheduled task’s first run and remove the scheduled task afterwards.
Option 1: Process Runs Every Time Someone Logs In
There may be reasons that this approach is sensible and potentially advantageous for an architect to implement:
- The deployed system will be used by many users over its lifetime
- Logic inside the script or application eliminates the need to control subsequent runs
- This is a system running Citrix XenApp or Microsoft RDP application server
- The process running is continuously updating or changing certain aspects of the user environment
If any of the above are applicable, then everything is set pretty quickly, but remember this is talking about replacing RunOnce which has the added benefit of being removed automatically after running. This first option works well as the user runs the task under their own credentials, it will run without administrative privileges, and is linked to user logon; however, it will not be limited to running only once.
Option 2: Remove Our Scheduled Task after Running Once
Situations where this option will be useful are:
- Only one user will ever need to run this process
- The deployed system will be used exclusively by one user
- The process is resource intensive and impacts system performance
For any of the scenarios above it would be cleaner or more sensible to remove the scheduled task triggered by the user logon. There are several ways to remove the user logon scheduled task but in this blog post we will use another scheduled task accomplish this. In this case another scheduled task that runs at system start-up will be used that removes the user logon event triggered scheduled task and will also remove itself.
There are a couple things to note when running a scheduled task at system start-up:
- Use script or program logic to control when the system start-up task removes the user logon triggered task; ensure the system start-up script can determine if the user’s scheduled task has successfully completed by having the user script leave a file or registry key to mark completion
- Scheduled tasks triggered at system start-up must be run as the NT AUTHORITY\SYSTEM account
Putting the Pieces in Place
To avoid any challenges, be sure the scheduled tasks are in place. To accomplish this; create the tasks manually, generate them by copying previously created tasks, or programmatically generate them. Manually created tasks are straight-forward but require the most administrative effort and will be prone to human error. This may be a good fit for small environments or one-off deployments but overall poorly scales and is best avoided.
The second approach, copying the files, is a better option and can be accomplished in a few ways. The schtasks command line can be used to export the task from a working system and later imported automatically. On deployed systems, using the same command or the scheduled tasks .xml file can be copied from one machine to another from Windows explorer by browsing to the C:\Windows\System32\Tasks folder on the source and destination computers.
Finally, scheduled tasks can also be created using a programmable/scriptable object interface. This approach is pretty complex but is an excellent fit if system deployment is done via the Microsoft Deployment Toolkit (MDT) or Configuration Manager. To help the reader with implementing this way, sample vbscript and jscript functions can be found in files at the end of this blog post.
The RunOnce registry key has been around since the introduction of the Windows registry over twenty years ago but has limitations in modern versions of the operating system. Most notably that their use requires the user be a local administrator. Using scheduled tasks are a great practice but need to be implemented thoughtfully to ensure they are effective and do not impact system performance. This restriction can be tackled several ways; choose the solution that best fits your need and implement the technology. If you have any questions please ask in the comments section.