Most readers of this blog should be familiar with MDT User Exit scripts, as many of the posts provided them for many scenarios.  In case you are not, the MDT help file defines them this way:

“A user exit script is effectively a function library that can be called during the processing of the CustomSettings.ini file using the UserExit directive. A user exit script contains one or more functions that can be called during the process of the CustomSettings.ini file.”

User exit scripts are a great way to extend the Gather process. However, how ZTIGather.wsf actually process user exit scripts has implications for which scripts you can use as user exit scripts.

When ZTIGather.wsf finds a UserExit entry in a CustomSettings.ini section, it actually processes the user exit script twice, once at the beginning of the section processing and once at the end.  When it does this it first loads the contents of the user exit script file into a string variable, executes the loaded code in the global namespace of a script using the VBScript ExecuteGlobal statement, and then calls the UserExit function that is included in the script.

The UserExit function I usually include is similar to this and requires the function signature (set of parameters) shown below:

Function UserExit(sType, sWhen, sDetail, bSkip)
    oLogging.CreateEntry "USEREXIT:TestUserExit.vbs started: " & sType & " " & sWhen & " " & sDetail, LogTypeInfo
    UserExit = Success
End Function

The sType input parameter is always called with a value of "SECTION".  The sWhen input parameter is called with "BEFORE" at the beginning of section processing and "AFTER" at the end of section processing.  The sDetail input parameter is the CustomSettings.ini section name.  The bSkip parameter is a return parameter.  If bSkip is set to True in the UserExit function when it is called at the beginning of section processing, the rest of that section in CustomSettings.ini is skipped.

This behavior allows you to run different code at the beginning or end of section processing or skip section processing based on code run at the beginning of section processing like the trivial samples shown below.

Function UserExit(sType, sWhen, sDetail, bSkip)
    oLogging.CreateEntry "USEREXIT:TestUserExit.vbs started: " & sType & " " & sWhen & " " & sDetail, LogTypeInfo
    If sWhen = "BEFORE" Then oEnvironment.Item("WhenProperty") = "Before"
    If sWhen = "AFTER" Then oEnvironment.Item("WhenProperty") = "After"
    UserExit = Success
End Function

Function UserExit(sType, sWhen, sDetail, bSkip)
    oLogging.CreateEntry "USEREXIT:TestUserExit.vbs started: " & sType & " " & sWhen & " " & sDetail, LogTypeInfo
    If TestCondition = False Then bSkip = True
    UserExit = Success
End Function

While this functionality is interesting, I have never had any occasion where I needed to use it.  I only know one person who has put logic in a UserExit function, fellow Deployment Guy Dave Hornbaker.  So why did I bother explaining it to you?  Well, as I said earlier it has implications for which scripts you can use as user exit scripts.

The first is that any script you want to use as a user exit script must have a UserExit function.  Fellow Deployment Guy Dave Hornbaker once needed to use ZTIDiskUtility.vbs functions in one of his user exit scripts.  Now he could have either added a UserExit function to ZTIDiskUtility.vbs so it could be used as a user exit script directly as well or added a script tag to ZTIGather.wsf that referenced ZTIDiskUtility.vbs.  However, we generally recommend against modifying the scripts that ship with MDT unless it is absolutely necessary.  If you do, then you have to remember to redo those changes when applying an update to MDT.

The second is that the when a user exit script contains VBScript Classes, the second ExecuteGlobal of the contents at the end of section processing fails with a “name redefined” error.  This causes ZTIGather.wsf execution to fail.

To overcome both of these limitations, I created a user exit script (MDTExitInclude.vbs) with a function called Include that only loads the contents of a VBScript file into a string variable, executes the loaded code using the VBScript ExecuteGlobal statement, and only does this once.  So while this does not allow the little-used section processing control behavior I described above, this function essentially allows you to use any VBScript as a user exit script.

After adding MDTExitInclude.vbs to Scripts folder in the LTI Deployment Share or ConfigMgr MDT Toolkit package, modify CustomSetting.ini similar to the example below to load MDTExitInclude.vbs as a user exit script and then load another script using the Include function.  This sample loads a script named TestUserExit.vbs and make functions inside it available for use.

[Settings]
Priority=IncludeScript, Default
Properties=IncludeResult

[IncludeScript]
UserExit=IncludeExit.vbs
IncludeResult=#Include("TestUserExit.vbs")#

One other advantage of loading user exit scripts in this fashion is that it simplifies loading multiple scripts.  For example, I recently needed to load five user exit scripts for one deployment.  Normally this would require creating five sections in CustomSettings.ini since you can only have one UserExit directive per section.  Using MDTExitInclude.vbs you can load as many scripts as you want in a single section like this:

[Settings]
Priority=IncludeExitScripts, Default
Properties=ExitScripts(*)

[IncludeExitScripts]
UserExit=MDTExitInclude.vbs
ExitScripts001=#Include("MDTLibHelperClasses.vbs")#
ExitScripts002=#Include("ModelAliasExit.vbs")#
ExitScripts003=#Include("MDTConfigMgrFunctions.vbs")#
ExitScripts004=#Include("MDTExitGetCollectionAdvertsDeploys.vbs")#
ExitScripts005=#Include("MDTExitGetResourceAdvertsDeploys.vbs")#

This sample uses one “throw away” list item, ExitScripts, to execute the Include function for each script.  This avoids “Settings section bloat” by not requiring multiple entries in the Priority or Properties line for just loading multiple scripts.

 

 

Disclaimer: The information on this site is provided "AS IS" with no warranties, confers no rights, and is not supported by the authors or Microsoft Corporation. Use of included script samples are subject to the terms specified in the Terms of Use.

This post was contributed by Michael Murgolo, a Senior Consultant with Microsoft Services - U.S. East Region.