MSEndpointMgr

Modify Task Sequence steps in ConfigMgr with PowerShell

For those that were able to attend Midwest Management Summit Mall of America (MMS MoA) last week, they got to see a demonstration of how you can export a Task Sequence in ConfigMgr with a few lines of PowerShell. In that session, the speaker demonstrated a method that indeed work, but it’s not the most optimal one. To me this was a reminder of a task that I’ve postponed for too long, namely to create a PowerShell script that is capable of modifying Task Sequence steps. For instance, as you may have noticed if you’re a frequent reader of this blog, I like to automate daunting tasks. Such a task is the reference image creation process, which I’ve successfully automated to the extent that a WIM file is imported into ConfigMgr. However, what have been missing after the import of the WIM file into ConfigMgr, is the part where we have to modify a Task Sequence and point to the new Operating System Image package. Therefor, in this blog post I’ll demonstrate how we have modify an existing Task Sequence in ConfigMgr and update the Apply Operating System step with the newly imported Operating System Image package.
If you’re not interested in an explanation of the code, simply just scroll to the end to get the whole sample code. As for those of you who’re thinking about cmdlets, forget about that. The process of modifying a Task Sequence in ConfigMgr requires knowledge about the SMS Provider (basically querying WMI) and a bit of XML with XPath. In terms of the high level approach that I’m demonstrating in this post, it involves retrieving the actual sequence from a Task Sequence Package, convert that into XML, parse the XML for the desired nodes, modify them, convert the XML back to WMI instances and then update the Task Sequence Package.

Break down of the sample code

We begin with defining a set of constant variables like $SiteServer and $SiteCode to contain required information about the SMS Provider location and specific namespace.

$SiteServer = "CM01"
$SiteCode = "P01"

Remember to change the values to reflect your environment. With that set, let’s move on.
Except for the contant variables, the first thing we need to determine is what Task Sequence object we want to modify. A Task Sequence that you see in the ConfigMgr console, is encapsulated in an instance (an instance in the WMI terminology for an object and will from now on refer to it as that) in the SMS_TaskSequencePackage class. We therefor need to retrieve the object and make sure that we also include any potential Lazy Properties (it’s a good practice to always attempt to retrieve the object directly).

# Get SMS_TaskSequencePackage WMI object
$TaskSequencePackage = Get-WmiObject -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Filter "PackageID like 'P01000CB'"
$TaskSequencePackage.Get()

As you can see from the code above, I’m filtering on the PackageID property that has a value of ‘P01000CB’. Remember to change this to a PackageID that exists in your environment.
With the SMS_TaskSequencePackage object stored in a variable, we can now use that as an argument to retrieve the actual sequence (this is the whole Task Sequence with all of it’s Groups deserialized into a WMI object). We can do that by invoking the GetSequence method on the SMS_TaskSequencePackage class. This is something that we need to in order to get the actual sequence out of the package object that we retrieved earlier.

# Get SMS_TaskSequence WMI object from TaskSequencePackage
$TaskSequence = Invoke-WmiMethod -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name "GetSequence" -ArgumentList $TaskSequencePackage

Next up is to convert the WMI object to XML, so that we can easily leverage Select-XML and XPath to determine the desired nodes that we need to amend. In order to convert the WMI object stored in the $TaskSequence variable, we pass the TaskSequence property of the object as an argument to a method called SaveToXML that exists in the SMS_TaskSequence class. We then declare that the value in the ReturnValue property as XML in order to get a XML document to parse.

# Convert WMI object to XML
$TaskSequenceResult = Invoke-WmiMethod -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequence -ComputerName $SiteServer -Name "SaveToXml" -ArgumentList $TaskSequence.TaskSequence
$TaskSequenceXML = [xml]$TaskSequenceResult.ReturnValue

Like I stated earlier in this post, we’re going to modify the Apply Operating System step with to reference a new Operating System Image package. In order to accomplish that, we need to two nodes in the XML document. For those of you that are not familiar with XPath, I suggest that read up on it since it’s a great way to easily parse an XML document together with the Select-XML cmdlet. As you can see in the code below, I’ve divided the code for the two nodes that I want to modify. As for the ‘variable’ node, I’m looking for a node called variable that contains an attribute called name with the value of ImagePackageID (remember that it’s case-sensitive), and for the ‘step’ (also referred to as command line in the variable names) I’m looking for a node called step with that contains an attribute called name with the value of Apply Operating System. For both of the nodes, I leverage regex to match for the PackageID portion and replace that with my PackageID of the Operating System Image package that I want.

# Amend variable for Apply Operating System step with new PackageID
$ApplyOperatingSystemVariableNode = Select-Xml -Xml $TaskSequenceXML -XPath "//variable[contains(@name,'ImagePackageID')]"
$ApplyOperatingSystemVariableNode.Node.'#text' = $ApplyOperatingSystemVariableNode.Node.'#text' -replace "[A-Z0-9]{3}[A-F0-9]{5}", "P0100011"
# Amend command line for Apply Operating System step with new PackageID
$ApplyOperatingSystemCommandLineNode = Select-Xml -Xml $TaskSequenceXML -XPath "//step[contains(@name,'Apply Operating System')]"
$ApplyOperatingSystemCommandLineNode.Node.action = $ApplyOperatingSystemCommandLineNode.Node.action -replace "[A-Z0-9]{3}[A-F0-9]{5}", "P0100011"

Now that we’ve made the modifications to the desired nodes, we need to convert the XML document back to an WMI object, so that we can essentially put that object back into WMI. In the SMS_TaskSequencePackage class, there’s an method called ImportSequence that takes an XML document as an argument and converts it into a WMI object.

# Convert XML back to SMS_TaskSequencePackage WMI object
$TaskSequenceResult = Invoke-WmiMethod -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name "ImportSequence" -ArgumentList $TaskSequenceXML.OuterXml

We’re now back at where we started, almost. We have the modified WMI object of our sequence and what’s left is to simply put it back where it belongs. For that we need our Task Sequence Package object that we retrieved in the beginning together with WMI object representing the modified sequence. With that at hand, we can pass them as an array to a method called SetSequence in the SMS_TaskSequencePackage class. This method supports updating existing Task Sequence Package objects in addition to creating new objects. However, in this demonstration we interested in updating an existing Task Sequence Package.

# Update SMS_TaskSequencePackage WMI object
Invoke-WmiMethod -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name "SetSequence" -ArgumentList @($TaskSequenceResult.TaskSequence, $TaskSequencePackage)

Once the last method has been invoked, if we did everything correctly, an object is returned showing the ReturnValue of 0 together with the value of the Task Sequence Package object that was updated.
That concludes the break down of how you can modify a Task Sequence step in ConfigMgr with PowerShell.

Sample code

Below is the sample code demonstrated in the break down described above:

$SiteServer = "CM01"
$SiteCode = "P01"
# Get SMS_TaskSequencePackage WMI object
$TaskSequencePackage = Get-WmiObject -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Filter "PackageID like 'P01000CB'"
$TaskSequencePackage.Get()
# Get SMS_TaskSequence WMI object from TaskSequencePackage
$TaskSequence = Invoke-WmiMethod -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name "GetSequence" -ArgumentList $TaskSequencePackage
# Convert WMI object to XML
$TaskSequenceResult = Invoke-WmiMethod -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequence -ComputerName $SiteServer -Name "SaveToXml" -ArgumentList $TaskSequence.TaskSequence
$TaskSequenceXML = [xml]$TaskSequenceResult.ReturnValue
# Amend variable for Apply Operating System step with new PackageID
$ApplyOperatingSystemVariableNode = Select-Xml -Xml $TaskSequenceXML -XPath "//variable[contains(@name,'ImagePackageID')]"
$ApplyOperatingSystemVariableNode.Node.'#text' = $ApplyOperatingSystemVariableNode.Node.'#text' -replace "[A-Z0-9]{3}[A-F0-9]{5}", "P0100011"
# Amend command line for Apply Operating System step with new PackageID
$ApplyOperatingSystemCommandLineNode = Select-Xml -Xml $TaskSequenceXML -XPath "//step[contains(@name,'Apply Operating System')]"
$ApplyOperatingSystemCommandLineNode.Node.action = $ApplyOperatingSystemCommandLineNode.Node.action -replace "[A-Z0-9]{3}[A-F0-9]{5}", "P0100011"
# Convert XML back to SMS_TaskSequencePackage WMI object
$TaskSequenceResult = Invoke-WmiMethod -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name "ImportSequence" -ArgumentList $TaskSequenceXML.OuterXml
# Update SMS_TaskSequencePackage WMI object
Invoke-WmiMethod -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name "SetSequence" -ArgumentList @($TaskSequenceResult.TaskSequence, $TaskSequencePackage)

Results

A blog post is not a blog post without any picture, so I’ve therefor included an image of my test Task Sequence before I’ve executed the sample code above and one after.

Before modificationAfter modification
199_1199_2

Nickolaj Andersen

Chief Technical Architect and Enterprise Mobility MVP since 2016. Nickolaj has been in the IT industry for the past 10 years specializing in Enterprise Mobility and Security, Windows devices and deployments including automation. Awarded as PowerShell Hero in 2015 by the community for his script and tools contributions. Creator of ConfigMgr Prerequisites Tool, ConfigMgr OSD FrontEnd, ConfigMgr WebService to name a few. Frequent speaker at conferences such as Microsoft Ignite, NIC Conference and IT/Dev Connections including nordic user groups.

6 comments

  • Has anyone found a way to change the ‘run as’ account and associated password on tasks throughout the task sequence?

  • I’m looking to utilize this script to make changes on about 90% of the task sequences on our SCCM server. I don’t want to have to create a separate script for each one, however, and the way this is currently written will require that, because I will have to run a different script for every package ID.
    The problem I’m encountering is that I can’t seem to figure out how to make this cycle through all the packages. I had thought that I could pipe them in from the Get-CMTaskSequence cmdlet, but the first line doesn’t find anything without a filter, and you can’t grab individual fields from that cmdlet.
    Any ideas?

  • Thanks for the great Article! Just a short remark: For me, the Script was not running until i add “[xml]” in Front of Line 13…

  • If you had a status filter set to fire off whenever a sequence was edited, would changing it with this script fire it off?

Sponsors