ConfigMgr Build 1910 released with a wholesome load of features, one in particular I’ll cover here, the addition of Output to task sequence variable in the existing task sequence Run Command Line and Run PowerShell steps.
This is a really handy feature.
I’ve been achieving the same result in a clunky manner for a while now. Execute something client-side during the task sequence, store the (cleaned up) output in a task sequence variable, do something with that variable further into the task sequence. Good example would be evaluating a registry key value. Not so easy pre-Build 1910.
If I wanted specific output that I can manipulate within the task sequence I’d write a bespoke script to get at it first, then I’d add to the tail end of the script some code to inject the output into a task sequence variable, then yield back to the task sequence engine.
Back in the ‘day you’d do it this way using either atypical scripting channels PowerShell or retro VBScript.
set env = CreateObject("Microsoft.SMS.TSEnvironment")
env("HelloWorld") = "SomeValue"
$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$tsenv.Value("HelloWorld") = “SomeValue”
The drawback with this approach is that every script needs modification to clean up the output a little if needed, and then to store it away in a task sequence variable before returning back to the task sequence engine.
There is a way to overcome this with a reusable bare-bones wrapper\launcher script that does a couple of things, launches what you want to launch, be it a script or executable, and then stores the result in a task sequence variable and yields back.
On the whole this latter approach opens up the door to reuse of existing scripts et al without having to resort to making changes to them, you just wrap them and handle their output. It is still very clunky though, not ideal, all we want to do is bring back some output and look and do something with it.
We can now dispense with one layer of that solution, bringing the value back in a task sequence variable automatically, which means most of that script-bridging that I talk of above can be done away with. Great.
We can still run custom scripts or command lines to tailor output in-situ (filter it down), or resort to executing a script or process and returning its output in full. Returned in full the output will be trimmed to the 1,000th character.
What we do not need to do is run a few lines of script each time so as to gain access to the task sequence engines variables to store something away. That’s the difference here. Which turns out to be very enabling.
Worth noting that if we tailor the output in-script we can return a single value to do comparisons against further into the task sequence, nice and tidy, or return all the output, assuming whatever you want to find is within the first 1,000 characters, and use comparisons operators such as like to tease out what it is we’re looking for.
Here’s the Run PowerShell and Run Command Line steps with the Output to task sequence variable addition:
Once we have a value stored away we can evaluate it using the built-in task sequence engine logic, such as via a task sequence step’s Conditions:
That’s an explicit or absolute comparison (equals), which would mean that the output has to be something you can do a direct comparison against, whatever is outputting has to output a predictable singular value or predictable sentence as a result of the operation or via filtering.
If the output is unprocessed\unfiltered its no hassle, we can just as easily perform a wildcard match on the variables value, using the like operator for the condition:
There’s more that we can do, if we turn to the Set Dynamic Variable we can transform the existing value or spawn a new variable based on some complex-enough conditional checks, such as if variable is or is like X then set this or another variable to Y as an example:
So to summarise, if whatever you are executing returns a string of predictable characters, a single value, a sentence, you are good to go, just launch it and steer the output into a task sequence variable using this feature, evaluate it later on.
However, if you need to do some string manipulation you can do it using piping, filtering and the FOR commands /F switch within the CMD shell, or do it slightly differently with PowerShell. I’ll go at this within the CMD shell and use the Run Command Line step.
Let’s take SC QUERY as an example, here’s the output when pointed at a specific service:
TYPE : 30 WIN32
STATE : 4 RUNNING
(STOPPABLE, PAUSABLE, ACCEPTS_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
We want to know if this service is running, its state.
There’s two options here, run SC QUERY WINMGMT, return all its output and then use the like operator to see if Running is present, or, we can filter the output and return the state whether it is running, paused or stopped.
I’ve got a couple of examples for the Run Command Line step that use that classic FOR /F command and switch along with FIND /I to pick at specific elements in the output, these stand as examples that you can modify and cast out from.
Bring back the STATE of a specific Windows service:
FOR /F "tokens=1,2,3,* delims= " %A in ('sc query Winmgmt ^| Find /I "state"') do @echo %DRetrieve
SC Query Winmgmt | FOR /F "tokens=1,2,3,* delims= " %A in ('Find /I "state"') do @echo %D
Either of these approaches will bring back the state value of a specific service cleanly.
Retrieve a specific Registry value:
FOR /f "tokens=1,2,* delims= " %A in ('reg.exe query HKLM\Software\7-zip /v Path') do echo %C
The 7-zip Path value will be returned as-is.
Check if the device is Azure AD joined:
FOR /F "tokens=1,* delims=: " %A in ('dsregcmd /status ^| Find /I "AzureAdJoined"') do @echo %B
It’ll return a YES or a NO to evaluate later on.
Check if a process running:
FOR /F "tokens=1,* delims= " %A in ('tasklist /NH /FI "ImageName eq notepad.exe"') do @echo %A
This will bring back the processes name if it is running, a like or equal comparison on notepad.exe or notepad will flag it up as running, and an action can be taken in the task sequence.
I think I’ll leave this here, as you can see this is a very enabling feature that could easily be underestimated or undervalued in amongst all the other bolder and brassier features that Build 1910 is laden with.
I tweeted about this feature recently, put out a few interesting but clumsy examples. Would be interesting to see some examples of how you use this new feature, feel free to tweet them there or leave in the comments here: