OK… I’ll admit it, I like to write Command Shell (CMD) scripts when I can. For me it’s like putting on an old broken-in pair of sneakers… so comfortable and familiar. Unfortunately, using them in MDT task sequences can be challenging. I this post I’ll demonstrate some techniques and helper scripts that can bring Command Shell scripts into the MDT era. There are four main tasks that I’ll address today: calling other scripts and executables, getting Task Sequence Variables, setting Task Sequence Variables, and logging.
Calling Scripts and Executables
A big issue with running scripts and executables from CMD scripts in MDT is calling them from the correct path. The command shell does not support a UNC path as the current directory and defaults to %SystemRoot%\System32 when running a CMD script in this way. In order to call scripts and executables in the same folder as the CMD script itself you should always use the full path to the scripts and executables. To prevent having to hard code these paths, I use the following code to set the folder where the CMD script resides as an environment variable:
This allows you “variablize” the paths to scripts and executables in the same folder (or subfolders) as the CMD script. For example, to run foo.exe in the same folder as the CMD script you would now use this syntax:
Getting Task Sequence Variables
It would obviously be very convenient if you could access Task Sequence Variables as environment variables. To that end I created a script, EchoTSVariables.wsf, that will echo variable=value lines for all Task Sequence Variables (plus the current MDT log path) to the console. This can then be called from a FOR statement to set these as environment variables:
for /F "tokens=1,2* delims==" %%i in ('cscript //nologo "%SCRIPTDIR%\EchoTSVariables.wsf"') do set %%i=%%j>nul
Setting Task Sequence Variables
Setting Task Sequence Variables from a CMD script requires a similar helper script. Fortunately, MDT includes just such a script. ZTISetVariable.wsf can be called directly from a CMD script:
cscript //nologo "%SCRIPTDIR%\ZTISetVariable.wsf" /VariableName:Foo /VariableValue:Bar
This will set a Task Sequence Variable named Foo to a value of Bar. For this new variable to be available in the CMD script environment, you would have to rerun the FOR statement with EchoTSVariables.wsf again.
For logging the output of executables and for logging messages directly, I have used native CMD script code plus a small helper script, EchoDateTimeVars.vbs. This script simply echoes date and time variables in a locale-independent format. I then created CMD functions like the one below:
for /F "tokens=*" %%i in ('cscript //nologo "%SCRIPTDIR%\EchoDateTimeVars.vbs"') do set %%i>nul
echo ^<^^![LOG[%INPUTTEXT%]LOG]^^!^>^<time="%HOUR%:%MINUTE%:%SECOND%.000+000" date="%MONTH%-%DAY%-%YEAR%" component="%COMPONENT%" context="" type="1" thread="" file="%COMPONENT%"^>>>"%LOGFILE%"
This logs an information message in a log with the same base name as the CMD script. This function depends on having first run EchoTSVariables.wsf and then the following commands near the beginning of the script to set the COMPONENT and LOGFILE variables:
There are also LOGWARNING and LOGERROR functions as well. These are all called using this syntax:
call :LOGINFO Info text I want to log in SMS log format right now please
call :LOGWARNING Warning text I want to log in SMS log format right now please
call :LOGERROR Error text I want to log in SMS log format right now please
I created two functions for running executables, logging the output and checking the return code. The first, RUNANDLOG, will run the executable, log the output to a single log entry, and set the environment variable RETURNCODE based on the errorlevel after the command is executed. The second, RUNANDLOGLINEBYLINE, log the output line by line. This is useful when the output of a command is Unicode (like wmic) but needs to be logged as ANSI in the MDT logs. Unfortunately, RUNANDLOGLINEBYLINE does not allow the capture of the return code of the command.
Finally, the log file created by this script is appended to the master MDT log, BDD.log, using the following command
if exist "%MASTERLOG%" copy "%MASTERLOG%" + "%LOGFILE%" "%MASTERLOG%" /y
I have provided a sample script, MDTCmdScript.cmd, in the attachment to this post. It demonstrates all of these techniques mentioned above. When using this you should place the script commands you want to run between the Begin main script and Finish main script comments. I have also included all the scripts needed to make this work (except for ZTISetVariable.wsf and ZTIUtility.vbs which come with MDT). All the scripts should be in the same folder.
For some additional tips on using CMD script with MDT, see Michael Niehaus' blog post here: http://blogs.technet.com/b/mniehaus/archive/2007/03/07/adding-custom-tasks-to-bdd-2007-task-sequences.aspx.
This post was contributed by Michael Murgolo, a Senior Consultant with Microsoft Services - U.S. East Region.