Do you ever have an idea so phenomenally bad that you just had to see it through just to see the disaster? I’m talking like Electrified Water levels of bad.
Yes… that is a real thing from the early 1900s. And look, I’m not here to judge anyone or any particular time period (we’ve made our fair share of big mistakes in the new millennium). What I am here to do is take you on a journey of an idea I just couldn’t let go of. Pausing a task sequence mid-flight.
“But Nate, that’s not a bad idea. As a matter of fact, they put it into the Task Sequence Debugger in CM 1906!”
See, now, I agree with you. But here’s the thing. I really wanted to try to implement this without the task sequence debugger. Don’t ask me why – Adam Gross already gave me the third degree. In short, my reasoning is rather simple: I’m stubborn. Also, I like to make things look custom and pretty (another point that Adam vehemently disagrees with), and let’s be honest, the UI for the debugger is functional but not particularly pretty. Honestly there’s not much that’s pretty in CM – Software Center might be the closest thing. (Sorry CM dev team, nothing against you).
The tool at the end of this article is also… not pretty. It’s not how I plan on implementing this in production, but since what I’ll be implementing in production won’t be publicly available, this is what you get. Feel free to steal my code with pride and roll your own, I won’t judge.
How Does One Pause a Task Sequence?
This is a great question. My first line of thought was to “reverse engineer” the task sequence debugger to see what they’re doing (what COM calls they’re making, any classes/methods they might expose, etc.) but that came up real short, real quick. A good chunk of code that the dev team writes (including the components I needed to look at) is in a C variant (C/C++) and frankly I just don’t have the skills to reverse engineer anything other than C#, JS, and a couple other languages.
Additionally, through my testing, it doesn’t appear that you can directly launch the debugger and expect it to work. As far as I can tell, it has to be spawned by the TSProgressUI executable. So I’m guessing there is something special going on between those two.
Reading through Daniel Ratliff’s excellent post “Breaking Down the Task Sequence Debugger” confirmed a lot of my suspicions. So off to the land of crazy ideas I headed.
De.. Debug… Eureka!
I spend a decent bit of time in Visual Studio working on some side projects like (G)UI++ and WinKeg. When debugging code in Visual Studio or in nearly any language, you have the opportunity to set breakpoints (or if your code is a faulty as mine, the compiler sets a breakpoint for you when you reach an unhandled exception). The thing is – the process that you’re debugging is pretty much paused from doing anything else. No more execution. Time moves slower for the application than for the 90lb girl I saw smoke an entire spliff in about 2 minutes before Modest Mouse came on stage (true story).
How do I replicate that though? Enter kernel32.dll and a bit of interop code. I won’t bore you with the nitty gritty details. Suffice it to say that kernel32.dll exposes three methods that we can call on:
The best part is that they were super easy to implement in C#, and frankly they’re not too hard to implement in PowerShell either. As a matter of fact, when I first started testing this out, I borrowed @besimorhino’s code on GitHub: https://github.com/besimorhino/Pause-Process.
Now… What Processes to Pause?
Since a lot of the main execution of the task sequence happens through TSManager.exe, I figured that was as good a place as any to start. I fired up a task sequence and set to work pausing TSManager. This got me about halfway where I wanted to be. By pausing TSManager, the task sequence reliably pauses at the next task TSManager is… tasked with. This means that some steps pause in the middle of running (like downloading the CM client for Setup Windows and ConfigMgr), but longer running tasks like downloading the OS WIM didn’t pause until they were completed.
Okay… let’s just do a cursory dir listing of the CCM bin folder in WinPE. Oh, would you look at that? There’s a whole host of executables that start with OSD. Add those to the list of processes to “debug” and now we’re cooking. But shoot… I forgot about processes outside of WinPE.
Admittedly I was much lazier in the full OS. Let’s just add CCMExec and SMSAppInstall.
Okay… CCMExec was a bad idea, so I removed that. But now I have something that seemingly covers most if not all of the bases.
Okay… Too Many Words… Give Me the Goods
Standard disclaimer here – this is definitely and wholeheartedly unsupported by Microsoft. Use it for debugging if you wish, but you’ll probably introduce some negative effects. So far I haven’t seen any, but I’ve also tested this all of like 30 minutes.
Execution is simple – just run it while you’re running a task sequence (or start it before you’re starting a task sequence) and let it rip. Press Pause TS to pause, press Start TS to start, and if you want to know which processes are currently running (that TSPauser is aware of) press Show Debug Info.
Let me know if I missed any executables – or if you experience any ill effects. I’m happy to take a look at them and see if there’s a way around it (or at least I’ll call out the issues in this post).