Running PowerShell scripts as part of a task sequence

Both MDT 2010 Lite Touch and ConfigMgr 2007 run the same task sequencer.  This task sequencer can run any command that you want, just specify the command line to use.  That’s the simple part – the harder part is figuring out what this command line should do.  Often the command, a VBScript or PowerShell script, needs to get information from the task sequence itself, accessing variables in the task sequence environment.  Remember, these task sequence variables aren’t environment variables – they are distinctly separate, so you can’t use the PowerShell “Env:” drive.

If you are using MDT, building a VBScript that includes the ZTIUtility.vbs script makes accessing task sequence variables pretty simple, as you can then reference something like this in your script:

sValue = oEnvironment.Item("MYVAR")

But PowerShell is now the rage – what if you wanted to do the same thing using PowerShell?  Fortunately that’s not too difficult either.  Here’s a simple example that gets the value of a particular variable:

$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
Write-Host $tsenv.Value("_SMSTSLogPath")

You would then need to set up a task sequence step that ran that PowerShell script.  In the Lite Touch case, I would suggest saving the file in the “Scripts” directory on the deployment share, for example as “Test.ps1”.  You could then create a “Run command line” step in the task sequence that executes this command:

PowerShell.exe -File "%SCRIPTROOT%\Test.ps1"

If you were using MDT 2010 integrated with ConfigMgr, the same thing would work, but you would need to add the file to the “Scripts” directory of the MDT toolkit package.  Alternatively, you could create a new software distribution package containing the PowerShell script, specify to use that package on the “Run command line” step of the ConfigMgr task sequence, and then specify a command line that assumes the script is in the working directory:

PowerShell.exe -File "%SCRIPTROOT%\Test.ps1"

If you want to change a task sequence variable (or set a new one), you use the same “Value” method:

$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$tsenv.Value("MyVar") = "My Value"

Maybe you want to do something a little more involved, like create an transcript (log) of the execution of your script.  You can use the _SMSTSLogPath variable to determine where to place the file:

# Determine where to do the logging
$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$logPath = $tsenv.Value("_SMSTSLogPath")
$logFile = "$logPath\$($myInvocation.MyCommand).log"

# Start the logging
Start-Transcript $logFile

# Insert your real logic here
Write-Host "We are logging to $logFile"

# Stop logging
Stop-Transcript

Another useful example is a script that logs the values of all task sequence variables:

# Determine where to do the logging
$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$logPath = $tsenv.Value("_SMSTSLogPath")
$logFile = "$logPath\$($myInvocation.MyCommand).log"

# Start the logging
Start-Transcript $logFile

# Write all the variables and their values
$tsenv.GetVariables() | % { Write-Host "$_ = $($tsenv.Value($_))" }

# Stop logging
Stop-Transcript

Or you could use the same technique to turn all the task sequence variables into PowerShell variables:

# Determine where to do the logging
$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$logPath = $tsenv.Value("_SMSTSLogPath")
$logFile = "$logPath\$($myInvocation.MyCommand).log"

# Start the logging
Start-Transcript $logFile

# Convert the task sequence variables into PowerShell variables
$tsenv.GetVariables() | % { Set-Variable -Name "$_" -Value "$($tsenv.Value($_))" }

# Write out a specific variable value
Write-Host $_SMSTSMDataPath

# Get all the variables
Dir Variable:

# Stop logging
Stop-Transcript

(Take out all the extra stuff and this could be reduced to two lines, the one that creates the COM object and the one that calls GetVariables.)

A few other notes worth mentioning:

  • You need to make sure scripts are enabled before trying to run these via a task sequence.  In the case of MDT Lite Touch, the scripts will typically be run from a network UNC path.  For ConfigMgr, a local path (for download-on-demand or download-and-execute) or a network path (for “run from DP”) will be used.  You can include a step in the task sequence to set the needed execution policy, e.g. “powershell.exe -Command { Set-ExecutionPolicy Unrestricted }”, or configure the same via Group Policy.
  • You may want to add the “-noprofile” parameter to the PowerShell.exe command line as the profile commands may cause issues with your script.
  • The Microsoft.SMS.TSEnvironment COM object is only available while the task sequence is running, so you need to test your script inside of a task sequence.  (This can be a case where the “convert task sequence environment to PowerShell variables could come in handy: do you testing with hard-coded variables, remove the values before deploying the script.)
  • The task sequencer only registers the matching platform of Microsoft.SMS.TSEnvironment.  For example, when the x86 task sequencer is running the x86 Microsoft.SMS.TSEnvironment will be available but the x64 version will not.  For an x64 task sequence, only the x64 Microsoft.SMS.TSEnvironment will be available.
  • ConfigMgr will run the x86 version of the task sequencer even on x64 operating systems, so the x86 version of PowerShell will normally be run in this case.  (For MDT Lite Touch, the x86 version of the task sequencer is used on x86 OSes and the x64 version is used on x64 OSes.)  Make sure you are aware of which platform is running, as that might affect your PowerShell script execution.  (Note that x64 processes run via a ConfigMgr task sequence, done by disabling file system redirection for the task sequence step or by specifying “sysnative” in the path, won’t be able to create the Microsoft.SMS.TSEnvironment object because of the previous note.)
  • If you want to return an error, you should insert an “exit” statement in your PowerShell script, e.g. “exit 1234”.  This will cause PowerShell.exe to return that return code to the task sequencer.