Video Screencast Help

JobMon –A Deployment Server 6.9 Client Progress Monitor

Created: 25 Oct 2013 • Updated: 28 Oct 2013 | 11 comments
ianatkin's picture
+10 10 Votes
Login to vote

Providing desk-side IT staff with up-to-date information on PC build process is critical for establishing deployment confidence. To address the requirement for timely access to build progress, deployment server administrators often grant console access to all desk-side technicians engaged in the deployment process. This method of keeping your technicians informed however is not ideal; it increases server load and is suboptimal as it imposes a significant number of steps between the technician and the data they need.

To address this concern, we built a few years ago a client-side HTML Application (HTA) we call JobMon.

1. Quick Preview
2. Credit
3. Background
4. Installing JobMon for DS6.9
5. The Job Monitor Sample
6. What Next?
7. Any Gotchas?
8. Under the Hood –How JobMon Works
   8.1 The HTA
   8.2 Delivering the HTA
   8.3 Delivering updated JobMon.txt Files

1. Quick Preview

This Job Monitor displays on the Windows Logon screen each computer’s progress through the provisioning process,


Here we can see at a glance the following,

  1. The computer name
  2. The current time (can't assume we all wear a watch!)
  3. Which job is currently executing and when that started
  4. Any jobs which have already executed
  5. Any jobs still scheduled for execution
  6. AClient/DAgent connectivity status

So, all a tech now has to do is take a casual glance at a computer being provisioned to be assured first that the deployment is indeed in progress and secondly that it will finish in a timely manner.

The value of JobMon in the end went far beyond fulfilling the timely information requirement of our technical staff. It had the additional benefit of increasing confidence in the deployment framework and the business process they underpin. We therefore consider JobMon to be a critical cornerstone of our deployment process.

2. Credit

Like much of what we do here, our creations are a team effort.  Whilst I am the proud father of the SQL wizardry worked into JobMon, the client-side HTA is the brain child of our lead packager and automation guru, Darren Collins.

3. Background

Community provided deployment splash screens are not new; I’ve seen many incarnations over the years, all built by Altiris Administrators who saw need to know at a glance whether a computer was still in the active provisioning process.

On of the more elegant of these was published to Symantec CONNECT by Rhys Paterson back in 2011 in this most excellent article Deployment Splash. This provided Altiris administrators a sound basis for building deployment-time progress display.  In his HTA, deployment progress was divined using ADODB (ActiveX Data Object for Databases) connections to the Deployment Server database.

A good question is why did we create a Deployment Splash? Two reasons,

  1. Security
    We are part of a highly distributed IT environment and PC provisioning must be viable across even our less trusted networks. Opening up our SQL ports to our entire network was frankly a worry.
  2. Server load
    Our techs ideally wanted the information on the splash to resemble what they’d see if they logged into the Deployment Console directly. This means detailed T-SQL queries. Add to this the need to provision up to 50 machines daily, then another concern appears; Deployment Server Console performance could well be compromised by clients splash screens hitting the database every three seconds.

The solution we hit upon was to use DS tasks to serve updates directly to the HTA. Although this would result in deployments hosting additional DS tasks it would have the benefit of,

  1. Preventing clients pressing the database with excessive ADODB connections
  2. Not leaving a potential security hole by opening up the Deployment Server SQL Service to the entire PC estate
  3. Providing a measure of control over what status messages would be sent to the client through the use of separate jobs to update the HTA (our change control process often results in some interesting job names)

To simplify progress tracking, we decided to store client progress using a new database table. This subsequently necessitated the creation of a stored procedure to update the table from a DS task and then a simple function to read from it.

4. Installing JobMon for DS6.9

Attached to this download you’ll find a zip that contains,

  1. A JobMon folder
    This contains the client HTA folder
  2. StoreEvents2.sql
    This is the SQL Stored procedure which we call to build a table of events which we are going to expose through JobMonitor
  3. GetEvents2.sql
    This is the function which retrieves the list of events scheduled on a PC with their progress
  4. JobMon_Example.bin
    This is a import file which will help you  get started with JobMon

The steps to get going with JobMon are,

  1. Copy the JobMon Files to your Deployment Share
    Create the folder path on your eXpress Share ./apps/JobMon and copy the JobMon files into it. Add to the folder Mark Russinovich’s  psexec v1.98.
    When you’ve done this, your JobMon folder should look like this,


    Note: If you have multiple deployment servers and have a common file server path for apps and utilities then use this. Just be sure to amend the file source in the ‘Build’ job  I show later accordingly!


  2. Add the SQL JobMon Stored Procedure and Function to the Deployment Server Database
    Open up  SQL Server Management Studio on the SQL Server hosting your eXpress database (this will typically be your Deployment Server). Copy the T-SQL in the .sql text files one at a time into a query window and execute on your Deployment Server database (by default named ‘eXpress’).


    You should receive a message “Command(s) completed Successfully” in both cases.


  3. Authorise The JobMon Stored Procedure in the Deployment Console
    As a security precaution, Symantec limit the stored procedures which can be executed by the DS engine.  In order for JobMon to work, we’ll need to authorise this stored procedure.

    1. In the DS Console, click “Tools” on the menu bar and select ‘Options’
    2. In the “Program Options” window that appears, select the ‘Custom Data Sources’ tab
    3. Click the button ‘Allowed Stored Procedures


    4. Select the Custom_JobMon_GetEvents2 stored procedure in the ‘Available Stored Procedures’ pane and add it to the ‘Allowed Stored Procedures’ pane and click ‘Apply’ and ‘OK’.



  4. Import the JobMon sample binary
    Select the job pane in the Deployment Console and click ‘Import Jobs’ from the file menu. Navigate to the jobmon.bin file from your downloaded zip and import it. You should have a sample folder which looks like this,


And that’s it. JobMon is now installed and you’re ready to go.

5. The Job Monitor Sample

In the sample job list that you imported, you’ll find three classes of jobs,

  1.  The ‘Real ‘ Jobs
    These are the jobs like ‘Install Plugins (latest packaged)’ and ‘Install Anti-virus’. For the purposes of getting a demo up and running the jobs imported here just execute a 30 second pause.
  2. The JobMon ‘tag’ Jobs
    These Jobs which contain a ‘(JM)’ tag in the job name are JobMon markers. They consist of two tasks, one tasks dumps where the computer currently is in the JobMon schedule to the express share, and the second task copies this to the client for the HTA to read.

    We call these jobs JobMon markers as they name the stages in your deployment process that you want exposed through JobMon. In this example we’ve created a JobMon marker job to pair up with each real deployment job. This is not essential however, you could have a set of 5 jobs for software and then one JobMon marker to precede them all named ‘Installing Software’. It’s your choice what you text you expose to the HTA and when.

    To create markers, just copy/paste the ones in this sample and rename.


  3. The JobMon ‘Build’ Job
    This job builds the data for the JobMon interface and fires up the HTA on the client’s WinLogon screen.

    It consists of a 5 tasks, the most important being the first task which builds the list of Job ids for the HTA to monitor.


Now let’s test JobMon. Drop the Job Monitor sample folder onto a client machine (one that isn’t logged into) and see what happens. The jobs in this sample are client-safe; they don’t actually perform any action on the client apart from a ping.

After a few seconds you should see the following on the target’s logon screen,


And that’s it. JobMon is now working for you.

6. What Next?

If JobMon looks useful to you, you'll now need to modify a test deployment so it present the Job Monitor and update the source txt file at regular intervals in your deployment. Generally speaking you’ll need to,

  1. Put in a JobMon Build job as the first ‘production’ job after the PC has been imaged (so the HTA comes up as soon as possible once Windows has booted)
  2. Put in a JobMon Build job after every reboot if you want the HTA to re-appear. Conversely, once your deployment has completed, use either a reboot or a taskkill command will remove the JobMon display.
  3. Put in the JobMon Marker jobs in places that make sense to you. You can choose here to mark each and every software deployment by name and version, or perhaps have one marker represent a job group. For example, you might want to represent a bundle of jobs that action registry tweaks, firewall changes and filesystem permission updates as something as simple as “Configuring Computer” or you might even want to specify each one independently.

7. Any Gotchas?

Yes there is. This incarnation of JobMon only indexes the ‘(JM)’ tagged jobs which are scheduled at the time the build job is executed. It therefore works fine when you deploy job folders that contain your deployment jobs and tasks from start-to-finish, but will entirely neglect to note any jobs you decide to drop on later while deployment is in progress (even if they contain ‘(JM)’ tags).

JobMon is therefore not well suited to environments where machines are deployed by techs scheduling jobs from different areas of the console piecemeal to provision machines. Conversely, Jobmon works best in environments that provision machines using a single drag’n’drop of a job/folder hierarchy, such as environments that use ImageInvoker.

As an item of future work, we aim to remove the intermediary table and push the SQL work into a more complex status retrieval function. This would remove this little gotcha.

8. Under the Hood –How JobMon Works

For those out there who’ve tried out JobMon and perhaps what to amend it a little more to suite their environment here is a rough guide to how JobMon works.

8.1 The HTA

JobMon is in essence an HTA which polls for input from the text file JobMon.txt  which is located in %WINDIR%\Temp.  When the HTA loads, the following code is executed,

    Sub window_OnLoad
        strAppName = oHTA.APPLICATIONNAME & " " & oHTA.VERSION
    End Sub

As this stands it’s hard to see how this execution of these 5 subroutines can result in this highly animated HTA. The trick to keeping the HTA ‘live’ is for some of these subroutines to create timers which ensure that after a specified timeout they will be automatically re-run using.

To create these timers, we use the HTA method SetTimeOut. The syntax for this is,

setTimeout (code,millisec,lang)

Where code is the function you want executed after the timeout,  millisec is the number of milliseconds to wait before executing the code, and lang is an optional argument which allows you to define the scripting language.

The last lines of the following functions configure the timers as followings,

  • Updatemonitor()
    The last line is,
    iTimerID = window.SetTimeOut("Updatemonitor()", intUpdateFreq * 1000)

    This ensures the Updatemonitor() subroutine which reads JobMon.txt and populates the HTA with job data is polled every 5 seconds (intUpdateFreq is set to 5 in the HTA variable declarations area).

  • StartFlashCurrentSub()
    The last line is,
    iTimerID = window.SetTimeOut("StartFlashCurrentSub()", 500)

    This ensures the  StartFlashCurrentSub() subroutine which flashes the currently active job is polled every half second.

  • AnimateArrow()
    The last line is,
    iTimerID = window.SetTimeOut("AnimateArrow()", 10)

    This is the sub which moves the arrow, pixel by pixel every 10ms, from the package graphic to the computer.  This is what gives JobMon that ‘live’ feel.

  • AClientConCheck()
    The last line is,
    iTimerID = window.SetTimeOut("AClientConCheck()", 5000)

    This sub executes every 5seconds and checks whether we have an outgoing TCP connection established on port 402. Depending on this state, we set the AClient symbol on the top-right of the HTA to either an active or inactive graphic.  

For more details of how each sub performs its allocated task, just open up the HTA code in your favourite text editor and delve away!

8.2 Delivering the HTA

The Job that takes note of any ‘JM’tagged scheduled on the client and activates the client HTA in our example is “0. Build Job Monitor”.  It consists of the following 5 tasks,

  1. Server-side ‘Run Script’ task to Create Job ID Row in Custom Table
    This server- side creates the table Custom_Event_Table (if not already present ) and updates the table with row containing the computer ID and a comma delimited list of any ‘(JM)’ tagged job IDs scheduled on that machine. To keep the table trim, any pre-existing data for that client is removed prior to the table update. The server-side code that does this is,

    REM Inserts Scheduled Events into Database
    REM %#*"{ call Custom_JobMon_StoreEvents2( %ID% ) }"%

    Here’s we’ve used the fabulous custom token replacement feature of DS to execute raw T-SQL stored procedure. All you have to do is wrap your T-SQL stored procedure call like this,


    If we’ve made sure we’ve authorised the stored procedure (as detailed in the installation steps above) at script delivery time the DS engine will execute the stored procedure and update the database accordingly.


  2. Server-side ‘Run-Script’ task to extract job ID row from custom table and add Status 
    This ‘Run Script’ task reads our custom table to retrieve the list of Job IDs that should be presented in the HTA for the target machine. So we can tell the HTA where we are in the queue here, the Job IDs are translated to names and a status execution flag is added.

    The contents of the script is simply the following two lines,

    REM Output event data to express share
    echo=%#*"select dbo.Custom_JobMon_GetEvents2(%ID%)"%  > .\temp\%ID%.evt

    Where here we use the custom token replacement feature of DS6 to execute a T-SQL SELECT. All you have to do is wrap your T-SQL  like this,


    At script delivery time the DS engine replaces such token with the SQL result (no need to authorise functions as they don’t change info in the database).  In the case of the T-SQL function we’ve used here, Custom_JobMon_GetEvents2, what we have echoed into the .evt file is a long string. This string details the status of JobMon tagged job along with its name.


  3. A Copy-File Task to push  JobMon.txt to client
    This task simply copied the %ID%.evt file to the client as JobMon.txt


  4. A Copy File Task to Push the JobMon HTA to client
    Here we use the DS file copy task to copy the JobMon HTA folder to the client under C:\temp\JobMon


  5. A Client-side Run-Script task to execute the JobMon HTA
    Here we a client-side script to start the Microsoft HTA application using psexec,

    REM DS Build Job Monitor
    SET JMFLDR=C:\temp\JobMon

    Start "" /MIN "%JMFLDR%\PSexec.exe" -accepteula -s -x mshta.exe "%JMFLDR%\BuildJobMon.hta”

    This should start the HTA on the WinLogon screen and it should have a JobMon.txt file ready and waiting for it to begin providing instantly a useful display.

8.3 Delivering updated JobMon.txt Files

As we’ve seen, the only data feed the JobMon HTA accepts comes from polling the JobMon.txt file every 5 seconds. In order to refresh the HTA with updated data we must therefore update this text file.

This is the reason for the need of the separate HTA update jobs within the job execution chain. The anatomy of the HTA updates job is as follows,

  1. A ‘(JM)’ tag in the name
    This tag identifies to the SQL code behind JobMon that this job is a HTA update job.


  2. A Server-side Script task
    This script task creates within the express share’s temp folder a .evt file  which is named using the client’s unique ID.

    The contents of the script is simply the following two lines,

    REM Output event data to express share
    echo=%#*"select dbo.Custom_JobMon_GetEvents2(%ID%)"%  > .\temp\%ID%.evt

    Where here we use the custom token replacement feature of DS6 to return the result of the embedded T-SQL SELECT.


  3. A Copy-File Task
    This task simply copied the %ID%.evt file to the client as JobMon.txt

So to use JobMon all you need to do is clone this Job, rename to the text you’d like displayed on the HTA, and insert it accordingly into the job execution chain.

Comments 11 CommentsJump to latest comment

XB's picture

Nice job !!!!

If I read it correctly it will only work if all your jobs are in jobfolders.

In our environment we normally have master jobs that will have many tasks that reference other jobs via shortcuts. Don't think it will work there?

If a Connect post helped you out, be sure to click "Mark As Solution" or the "Thumbs Up" button to let other users know about it.

Login to vote
ianatkin's picture

If I read it correctly it will only work if all your jobs are in jobfolders.

I'll try to answer this without causing confusion, which is going to be hard.... ;-)

JobMon only displays the progress of the marker jobs you have scheduled on a computer at the time the build job is exeucted (i.e. when the HTA becomes active). It keeps track of all those marker tasks from that point.

Now... as you have correctly figured out shortcuts could present a problem as any marker jobs referenced by shortcuts jobs won't present themselves in the chain. This can be helped by our "DS Shortcut Accelerator" but that application is limited to scenarios where the shortcuts are a simple pointer only (and don't contain conditional logic)

The way we get round this issue of conditions is to simply ammend the marker job name to reflect the condition. So we have for example we have marker jobs named things like "(JM) Cisco VPN Client -LAPTOPS ONLY".  This makes it clear to the techs that it's not going to install on the desktops.

And in general, we also have one job per functional task. So every individual software item it a job, and the task level is used to define the stages of the single software product installation.

we normally have master jobs that will have many tasks that reference other jobs via shortcuts. Don't think it will work there?

This scenario really describes the usage in this article. Put in one marker that references what your complex job does. So you might have a single job that installs lots of web browser plugins. Well, in that case put a marker in front to just say "Installing Plug-ins". The shorcuts will inject, perform the installations specified and will then move onto the next marker.

EDIT: I should add that if you have one job that does your whole deployment (by branching out to shortcuts from it's tasks) then yes, JobMon will have limited use!

Ian Atkin, IT Services, Oxford University, UK

Connect Etiquette: "Mark as Solution" those posts which assist you most in resolving your problem, and give a thumbs up to useful articles and downloads

Login to vote
XB's picture

Thanks for your answers. I like the idea and give it a try.

One remaining question pops up. Normally, on Windows7 or Windows2008, you cannot produce output visable on the client computer from within a job unless you specify "run script in console user session".

Is that where the psexec comes in? Does that also work in the our phase where the autologon is still running?

Thanks Ian for all the work and sharing with us. A bunch of your ideas has found it to our production already.

If a Connect post helped you out, be sure to click "Mark As Solution" or the "Thumbs Up" button to let other users know about it.

Login to vote
ianatkin's picture

Normally, on Windows7 or Windows2008, you cannot produce output visable on the client computer from within a job unless you specify "run script in console user session". Is that where the psexec comes in?

Yes. We use psexec to put jobmon on the WinLogon screen. If you have autologon in place and want the HTA visible in a user session just have a job to fire up the HTA directly without using our psexec call.

For the scenario of autologin, what you do depends on whether what your job schedule is like. For example, you might go straight into autologon at first boot at which point you won't want a WinLogon session HTA at all. In that case, just call the HTA directly and don't use psexec.

If you however you have plenty of tasks running before the autologon, you might want to use a psexec based HTA to present progress, then when you autologon kill the current HTA and start a new one (without the psexec call).

Thanks Ian for all the work and sharing with us. A bunch of your ideas has found it to our production already.

Brilliant! That's why we publish this stuff after all; it's all in the hope that sharing these tricks help others get business benefit from what we learn. And often we do learn the hard way... ;-)

Ian Atkin, IT Services, Oxford University, UK

Connect Etiquette: "Mark as Solution" those posts which assist you most in resolving your problem, and give a thumbs up to useful articles and downloads

Login to vote
JSpur's picture

Well done!
This is one option that many of my customers have asked for and you have taken it to the next step.
This should prove to be a valuable tool when deploying multi-step processes

Login to vote
nicwic's picture

Hello Ian

This is some great work. I just wanted to ask you something.

Right now we are running a program called Deepfreeze on all our computers. The software deletes all changes made on the computer on restart. And it restarts the computer when logging off.

So my question is. I want to run this hta file when the user is logged in. But without explorer.exe. Is there a way to run the hta when explorer.exe process is closed?

i'd like for the hta file to stay "always on top"

Login to vote
Darren Collins's picture

Hi nicwic

For your last question, when I wrote the HTA I bulit in a timer that sets the window to take foreground focus every 5 seconds.  This means that if something else takes foreground focus, the HTA will take it back within  a few seconds.  Three lines ensure that the screen saver does not kick in and that the HTA grabs back focus if it loses it:

wshShell.SendKeys "{NUMLOCK}"  '# these 2 numlock presses stop the screen saver kicking in.

wshShell.SendKeys "{NUMLOCK}"


The first two lines effectively press the numlock key twice in quick succession, too fast to see the light flash on the keyboard, but a safe key that won't interfere with anything else.  The third line brings the window to the foreground.

As for your deepfreeze related question, I haven't tried executing the HTA as a logged on user when the shell is not explorer.exe.  My feeling is it should work, but you might have to specify the full path to mshta.exe and pass the HTA file as a parameter.  Also you would not use PSExec.exe and you would need to call it in the user context.  Or PSExec may have other options to display interactively running as System (for example) but displayable to a logged on user.


Darren Collins
Applications Packaging and Deployment for IT Services,
Oxford University, UK.

Login to vote
dcuoq's picture

Hello, Thanks for your DS Tools. It's working for me with the last DS 6.9 SP6.

Thans for your share.wink

Cordialement | Best Regards
Coordinateur Technique Micro-Informatique

Login to vote
DerekMusselman's picture

This is exactly what we've been looking for, however I'm having a hard time getting the .hta to display on the winlogon screen.  I've tried just about every combination of every option to get this working, but I've had no luck.

What I've tried:

- Ran job unmodified from original on Win7 PC on the domain, and another Win7 machine off of the domain

- Ran job unmodified on vanilla Win7 install not joined to the domain

- Modified the psexec task with about every psexec switch I could find

- Ran the psexec task using local admin credentials, local system account, domain admin, etc.

- Updated psexec to the latest version and tried all of the above

- Made some registry changes involving passing security tokens (can't remember exactly what this was)

- Disabled UAC and ran all of the above

- Ran the psexec command on the PC itself, without using Altiris

Almost all of these scenarios will display the .hta in the user console session, but not at the logon screen.  I've verified that the mshta is actually running as SYSTEM.  If I try forcing pxexec to session 0 it just doesn't display anything at all at the logon screen.

Is there some sort of Windows 7 security change that has happened since this article was written, or am I missing something (I'm guessing that I'm overlooking something)?

Any help would be appreciated.  Thanks!

Login to vote
Darren Collins's picture

Hi, Below is our exact uncensored command line we use to display the jon monitor over the top of our fully patched Windows 7 x64 SP1 computer's logon screen.  It works both before and after the computer has joined the domain (all one command line):

Start "" /MIN "C:\JobSource\InformationScripts\DS_Build_Job_Monitor\PSexec.exe" -accepteula -s -x mshta.exe "C:\JobSource\InformationScripts\DS_Build_Job_Monitor\BuildJobMon.hta"

We use the 'C:\JobSource\InformationScripts\DS_Build_Job_Monitor\' on the local machine to copy into the psexec.exe, the hta and the image files used by the hta.

If the JobMon.bin file is imported into you DS you'll see where this command line is represented in the first job in the example job set called '0. Build Job monitor' and it is in the last task in that job 'Run Script   DS Build Job Monitor' as seen in the image below: 


Please note that the command line is different for Windows XP.  This is what worked for us in XP (unedited):

Start "" /MIN "C:\JobSource\InformationScripts\DS_Build_Job_Monitor\PSexec.exe" -accepteula -s -x -i 0 mshta.exe "C:\JobSource\InformationScripts\DS_Build_Job_Monitor\BuildJobMon.hta"

Hope this helps,


Darren Collins
Applications Packaging and Deployment for IT Services,
Oxford University, UK.

Login to vote
DerekMusselman's picture

Thank you for the reply.  I copied your command string and took out the variable that came with the original downloaded from here.  It was then I remembered that I updated psexec at some point in the troubleshooting process, so I replaced it with the original and it now works.

Thanks again.

Login to vote