Being able to read and write Task Sequence variables on the fly is one of the most powerful techniques in the ConfigMgr toolkit — whether you are debugging a failing OSD deployment, dynamically adjusting TS behaviour based on hardware detection, or passing values between custom scripts and built-in steps. This updated guide expands the original single-command approach into a complete practical reference, including the critical x64/x32 COM object caveat that breaks many scripts silently.
| Detail | |
|---|---|
| Last Updated | March 2026 |
| Applies To | SCCM/ConfigMgr 2303+, Windows PE, Windows 10/11 |
| COM Object | Microsoft.SMS.TSEnvironment |
| Requires | Running inside an active Task Sequence |
| Difficulty | Intermediate |
How Task Sequence Variables Work
Task Sequence variables are key-value pairs stored in the Task Sequence environment — a runtime memory space managed by the SMS Task Sequence Manager (TSManager.exe). They are accessible to any step running within the task sequence via the Microsoft.SMS.TSEnvironment COM automation object.
Variables come in two types:
- Built-in variables — prefixed with
_SMSTSorOSD, set and read by ConfigMgr itself. Examples:_SMSTSLogPath,OSDComputerName,_SMSTSMachineName - Custom variables — created by you in the TS editor or via script. No naming restrictions other than avoiding the reserved
_SMSTSprefix. Values persist for the remainder of the running task sequence.
Critical: The x64 PowerShell COM Object Problem
This is the most common reason scripts that access TSEnvironment work in testing but silently fail in production. The Microsoft.SMS.TSEnvironment COM object only works in 32-bit (x86) PowerShell — even on 64-bit systems. If your Run PowerShell Script step is configured to run in 64-bit PowerShell, the COM object will either return empty values or fail to instantiate entirely.
The fix depends on your step configuration:
- In the Run PowerShell Script step properties, set PowerShell execution policy and ensure Run script in 64 bit PowerShell Host is unchecked — or
- Call your script explicitly via
%SYSTEMROOT%\SysWOW64\WindowsPowerShell\v1.0\powershell.exein a Run Command Line step — or - Pass TS variables as script parameters rather than reading them inside the script via COM (see the parameters approach in the examples below)
Basic Usage — Read, Write, and List Variables
The foundation — instantiate the COM object and use it to read or write any variable:
# Instantiate the TSEnvironment COM object
# Must run inside an active Task Sequence — fails outside of a TS context
$TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment
# Read a built-in variable
$logPath = $TSEnv.Value("_SMSTSLogPath")
Write-Host "TS Log Path: $logPath"
# Read another common built-in variable
$computerName = $TSEnv.Value("OSDComputerName")
Write-Host "OSD Computer Name: $computerName"
# Write / create a custom variable
$TSEnv.Value("MyCustomVariable") = "Hello from PowerShell"
# Read it back to verify
Write-Host "Custom var: $($TSEnv.Value('MyCustomVariable'))"
List all currently loaded variables — useful for debugging:
# List all TS variables currently in the environment
$TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$TSEnv.GetVariables() | Sort-Object | ForEach-Object {
Write-Host "$_ = $($TSEnv.Value($_))"
}
Convert all TS variables into native PowerShell variables for easy use within the script:
# Import all TS variables as PowerShell variables
$TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$TSEnv.GetVariables() | ForEach-Object {
Set-Variable -Name $_ -Value $TSEnv.Value($_)
}
# Now use them directly — e.g. $OSDComputerName, $_SMSTSLogPath
Write-Host "Computer Name: $OSDComputerName"
Write-Host "Log Path: $_SMSTSLogPath"
Practical Use Case: Set Variable Based on Hardware Detection
A very common pattern — detect hardware properties during the TS and set a variable that subsequent steps use as a condition. For example, detecting chassis type to decide whether to apply a laptop or desktop configuration:
# Detect chassis type and set a TS variable accordingly
$TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$chassis = (Get-CimInstance -ClassName Win32_SystemEnclosure).ChassisTypes[0]
# ChassisType values: 8,9,10,11,12,14 = laptop/portable; 3,4,5,6,7,15,16 = desktop
$chassisTypeMap = @{
3 = "Desktop"; 4 = "Desktop"; 5 = "Desktop"; 6 = "Desktop"
8 = "Laptop"; 9 = "Laptop"; 10 = "Laptop"; 11 = "Laptop"
12 = "Laptop"; 14 = "Laptop"; 30 = "Tablet"
}
$formFactor = $chassisTypeMap[[int]$chassis]
if (-not $formFactor) { $formFactor = "Unknown" }
$TSEnv.Value("HWFormFactor") = $formFactor
Write-Host "HWFormFactor set to: $formFactor"
In the TS editor, add a condition to subsequent steps: Task Sequence Variable → HWFormFactor → equals → Laptop. The step only runs on laptops — no separate task sequences needed for different form factors.
Practical Use Case: Set OSDComputerName Dynamically
One of the most common custom TS variable use cases — automatically naming the machine based on serial number, asset tag, or a naming convention without requiring technician input:
# Automatically set OSDComputerName from BIOS serial number
$TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$serial = (Get-CimInstance -ClassName Win32_BIOS).SerialNumber.Trim()
# Apply a naming convention — e.g. prefix + first 10 chars of serial
$prefix = "CORP"
$computerName = "$prefix-$($serial.Substring(0, [Math]::Min(10, $serial.Length)))"
$computerName = $computerName.ToUpper() -replace '[^A-Z0-9\-]', '' # Remove invalid chars
$TSEnv.Value("OSDComputerName") = $computerName
Write-Host "OSDComputerName set to: $computerName"
Practical Use Case: Track Step Outcomes Across the TS
Set a variable after a risky step (e.g. BIOS update or driver injection) to track whether it succeeded. Use the variable as a condition on a subsequent restart step — only restart if the BIOS was actually updated:
# At the end of a BIOS update script — record outcome in a TS variable
$TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment
# One-liner version — useful as an inline command in a Run Command Line step
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command `
"& {`$ts = New-Object -COMObject Microsoft.SMS.TSEnvironment; `$ts.Value('BIOSUpdated') = 'True'}"
# Then in the TS editor, condition the restart step:
# Task Sequence Variable → BIOSUpdated → equals → True
Passing TS Variables as Script Parameters (x64 Safe)
When the 32-bit COM object limitation is a problem — or when you want a cleaner script architecture — pass TS variables as parameters in the Run PowerShell Script step rather than reading them from inside the script:
In the Run PowerShell Script step, set the Parameters field to:
-ComputerName %OSDComputerName% -LogPath %_SMSTSLogPath% -PackageID %SMSTSPackageID%
In your script, declare matching parameters:
param (
[string]$ComputerName,
[string]$LogPath,
[string]$PackageID
)
# TS variables are now available as standard PS parameters
# No COM object needed — works reliably in both 32 and 64-bit PS
Write-Host "Deploying to: $ComputerName"
Write-Host "Log path: $LogPath"
"$(Get-Date) — Script ran for $ComputerName" | Out-File "$LogPath\custom.log" -Append
Debugging TS Variables Interactively During a Running TS
Sometimes you need to inspect or edit TS variables live — while the TS is paused at a breakpoint or waiting at a prompt. The approach differs depending on whether you are in WinPE or the full OS phase.
In Windows PE
Open a command prompt via the debug step or F8 (if enabled in boot media), then launch PowerShell and access TSEnvironment directly:
# In WinPE command prompt — first enable PowerShell if not already running
X:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
# Then access TSEnvironment
$TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$TSEnv.GetVariables() | Sort-Object | ForEach-Object { "$_ = $($TSEnv.Value($_))" }
# Edit a variable on the fly
$TSEnv.Value("OSDComputerName") = "TESTMACHINE01"
In Full OS Phase
TSEnvironment is only accessible from the SYSTEM account context in the full OS. Use PsExec from Sysinternals to launch an interactive SYSTEM PowerShell session:
# From an elevated command prompt — launch PowerShell as SYSTEM interactively
# Download PsExec from Sysinternals first
PsExec.exe -i -s PowerShell.exe
# Now in the SYSTEM PowerShell session — access TSEnvironment normally
$TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$TSEnv.Value("OSDComputerName")
Using the Built-in TS Debugger (ConfigMgr 2002+)
Since ConfigMgr 2002, the Task Sequence Debugger provides a proper GUI debugger — set breakpoints on any step and inspect/edit variables without needing PsExec or debug command lines:
- In the ConfigMgr console, right-click the task sequence → Debug
- Deploy the TS to a test device in Debug mode
- The debugger GUI launches on the device — use Set Variable to edit any variable interactively while the TS is paused
This is the recommended approach for modern ConfigMgr environments — far cleaner than the PsExec workaround.
Commonly Used Built-in Variables Reference
| Variable | Description | Read/Write |
|---|---|---|
| _SMSTSLogPath | Path where TS log files are written | Read |
| _SMSTSMachineName | Current machine name during TS | Read |
| _SMSTSPackageName | Name of the running TS | Read |
| OSDComputerName | Computer name to apply during deployment | Read/Write |
| OSDDomainName | Domain to join | Read/Write |
| SMSTSRebootDelay | Seconds before restart (set to 0 for immediate) | Read/Write |
| SMSTSPersistContent | Keeps downloaded content in TS cache after completion | Read/Write |
| OSDPreserveDriveLetter | Whether to preserve existing drive letter during Apply OS | Read/Write |
| _SMSTSInWinPE | True if TS is running in Windows PE | Read |
| _SMSTSCurrentActionName | Name of the currently running step | Read |
Common Gotchas
- COM object only works inside a running TS — if you test your script outside of a TS,
New-Object -COMObject Microsoft.SMS.TSEnvironmentwill throw an error. Add a try/catch for testing outside the TS - x64 PowerShell returns empty variables — the COM object runs in 32-bit mode. Uncheck “Run in 64-bit PowerShell” in the step, or pass variables as parameters
- Run as alternate credentials breaks COM access — TSEnvironment is only accessible from the SYSTEM account. If the step is configured to run as a named account, the COM object returns
E_ACCESSDENIED (0x80070005) - Variable names are case-sensitive —
OSDComputerNameandosdcomputernameare treated as different variables - Sensitive variables are masked in smsts.log — variables marked as sensitive in the TS editor do not appear in plain text in the log file, even when read via GetVariables()
Summary
The Microsoft.SMS.TSEnvironment COM object remains the standard mechanism for reading and writing Task Sequence variables from scripts in 2026 — nothing has changed architecturally, but the practical gotchas have become better understood. The x64 PowerShell limitation and the SYSTEM context requirement are the two issues that catch most people. The TS Debugger (available since ConfigMgr 2002) has made interactive debugging significantly cleaner and is worth adopting if you are still using PsExec workarounds.
- Always instantiate:
$TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment - Read:
$TSEnv.Value("VariableName")— Write:$TSEnv.Value("VariableName") = "value" - List all:
$TSEnv.GetVariables() | Sort-Object | ForEach-Object { "$_ = $($TSEnv.Value($_))" } - Uncheck “Run in 64-bit PowerShell” or pass variables as parameters to avoid the COM x64 issue
- Use the built-in TS Debugger for interactive variable inspection — available since ConfigMgr 2002
