Bestehende Tasksequenz über Powershell editieren

Angenommen es soll eine (oder mehrere) Tasksequence um einen (oder mehrere) Schritte erweitert werden. Eine Lösung wäre den Auszubildenden endlich wieder eine sinnvolle Aufgabe zu übergeben 😉 Eine andere, das Problem mittels Powershell-Scripting zu lösen.

Ich habe mich für das Scripting entschieden ..

Was ist zu tun?

  1. Bestehende Tasksequenz laden (Get-CMTaskSequence)
  2. IResultObject “TaskSequence” aus dem TaskSequencePackage ziehen
  3. Neuen Tasksequenzschritt “SMS_TaskSequence_*Action” erzeugen
  4. Neuen Tasksequenzschritt in die bestehende Liste aufnehmen
  5. Speichern der Änderungen

Eine Tasksequenz aus dem SCCM auszulesen, ist grundsätzlich auch ohne die Cmdlet’s möglich, aber wozu die Mühe wenn es auch deutlich einfacher geht 😉

Also zuerst wird das Powershell-Modul von Configuration Manager geladen:

Import-Module "$env:SMS_ADMIN_UI_PATH\..\ConfigurationManager.psd1"
CD <SiteCode>:

1. Bestehende Tasksequenz laden (Get-CMTaskSequence)

Um das TaskSequencePackage zu erhalten, nutze ich die Funktion “Get-CMTaskSequence“. (Get-CMTaskSequence -Name <Name der Tasksequenz>)

Die Funktion liefert mir ein Objekt vom Typ “IResultObject” (Microsoft.ConfigurationManagement.ManagementProvider.IResultObject) zurück. Innerhalb der Objektstruktur befindet sich auch das Connection-Objekt (ConnectionManager), welches ich in den folgenden Schritte immer wieder benutzen werden.

$taskSequencePackage = Get-CMTaskSequence -Name $taskSequenceName

Im Folgenden werden die obenen genannten Schritte mit Code-Beispielen ergänzt und diese näher erläutert.

2. IResultObject “TaskSequence” aus dem TaskSequencePackage ziehen

Das TaskSequencePackage enthält die eigentliche Sequence, die wiederum die einzelnen Schritte enthält. Die Sequence erhalte ich durch Aufruf der Methode “GetSequence“. Normalerweise würde ich mir das Connection-Objekt einzeln erstellen, sodass ich eine klare Trennung habe. Für das Beispiel jedoch bin ich den bequemen/ quick-and-dirty Weg gegangen und nutze die Connection aus dem $taskSequencePackage, welches ich durch das Cmdlet “Get-CMTaskSequence” erhalten habe.

# prepare parameters for "GetSequence"
$methodParams = New-Object "System.Collections.Generic.Dictionary[string, object]"
$methodParams.Add("TaskSequencePackage", $taskSequencePackage)

# Get the sequence
$outParams = $taskSequencePackage.ConnectionManager.ExecuteMethod("SMS_TaskSequencePackage", "GetSequence", $methodParams)
$taskSequence = $outParams.GetSingleItem("TaskSequence")

Nachdem ich die eigentliche Sequenz ($taskSequence) erhalten habe, kann ich nun auch über $taskSequence.Steps auf die einzelnen Schritte zugreifen und ggf. ändern oder löschen.

In unserem Beispiel, möchte ich jetzt einen weiteren Schritt hinzufügen, als erstelle ich mir zunächst den einen neuen Schritt!

Ein kurzer Hinweis an dieser Stelle, es gibt verschiedene Tasksequenzschritt-Typen, wie bspw. “RunCommandLine” oder “InstallApplication”, etc. Bei dem eigentlichen Editor über die SCCM Console, können diese Schritte über das Menü ausgewählt werden. Will man einen Tasksequenzschritt programmatisch hinzufügen, muss auch hier zunächst ein bestimmter Typ gewählt werden. Dies geschieht, in dem man sich eine Instanz der jeweiligen WMI-Class erstellt.

Die WMI-Klassen für die Tasksequenzschritte sind alle nach dem gleichen Schema aufgebaut: “SMS_TaskSequence_<Typ>Action“. Möchte man bspw. einen Schritt “Run Command Line” erzeugen, so erstellt man eine Instanz der WMI-Klasse “SMS_TaskSequence_RunCommandLineAction“.

Ok, zurück zum eigentlichen Beispiel: Wir möchten einen neuen Tasksequenzschritt “Run Command Line” erstellen und in die Tasksequenz aufnehmen.

3. Neuen Tasksequenzschritt “SMS_TaskSequence_*Action” erzeugen

Im nächsten Schritt erstelle ich eine “leere” Hülle, einer “SMS_TaskSequence_RunCommandLineAction“, deren Eigenschaften ich später definieren und schlussendlich dann in die Tasksequenz aufnehmen möchte. Eine neue Instance würde man auf diese Weise erstellen:

$newTsStep = ([WMICLASS] "\\$($SITESERVER)\ROOT\SMS\SITE_$($SITECODE):SMS_TaskSequence_RunCommandLineAction").CreateInstance()

Leider wird später der Variablentyp nicht als Tasksequenzschritt akzeptiert. Hintergrund ist, dass die Powershell das damit erzeugte Objekt “WMIClass/ SMS_TaskSequence_RunCommandLineAction” nicht in das Objekt “IResultObject” aus dem ConfigurationManagement umwandeln kann.

Aus diesem Grund erstellen wir den Tasksequenzschritt nicht über WMICLASS sondern über den ConnetionManager, den wir aus dem $taskSequencePackage erhalten.

$newTsStep = ([WMICLASS] "\\$($SITESERVER)\ROOT\SMS\SITE_$($SITECODE):SMS_TaskSequence_RunCommandLineAction").CreateInstance()
$newTsStep = $taskSequencePackage.ConnectionManager.CreateEmbeddedObjectInstance("SMS_TaskSequence_RunCommandLineAction")

Identisch zur WMIClass können wir auch jetzt direkt auf die Eigenschaften der “SMS_TaskSequence_RunCommandLineAction” zugreifen und entsprechend definieren:

$newTsStep.CommandLine = "cmd /c echo 'hello world' >> test.txt"
$newTsStep.Description = "Custom Description"
$newTsStep.Name = "My new Step"
...

Sind alle gewünschten Eigenschaften gesetzt, kann der neue Schritt in die Tasksequenz aufgenommen werden.

4. Neuen Tasksequenzschritt in die bestehende Liste aufnehmen

Achtung! Auf die Steps greifen wir über das TaskSequence-Objekt (GetSequence), nicht über das TaskSequencePackage-Objekt (Get-CMTaskSequence) zu!

# Get array of SMS_TaskSequence_Steps
$taskSequenceSteps = $taskSequence.GetArrayItems("Steps")
if ($taskSequenceSteps -eq $null) { 
  # new array
}
else {
   $taskSequenceSteps.Add($newTsStep); 
} 

# save array 
$taskSequence.SetArrayItems("Steps", $taskSequenceSteps);

Über “SetArrayItems(..)” wird die veränderte Liste wieder in das Objekt zurückgeschrieben. Abschließend wird jetzt auch die TaskSequence wieder gespeichert.

5. Speichern der Änderungen

Dieser Schritt sollte auch über Set-CMTaskSequence funktionieren. Im Beispiel hat das auf Anhieb bei mir nicht funktioniert, deshalb habe ich das Speichern auch über die WMIClass/ Methode realisiert.

# prepare parameters for "SetSequence"
 $methodParams = New-Object "System.Collections.Generic.Dictionary[string, object]"
 $methodParams.Add("TaskSequence", $taskSequence)
 $methodParams.Add("TaskSequencePackage", $taskSequencePackage)

 # Get the sequence
 $outParams = $taskSequencePackage.ConnectionManager.ExecuteMethod("SMS_TaskSequencePackage", "SetSequence", $methodParams)

Das war’s!

Ein Blick in die Configuration Manager Console zeigt das erfolgreiche Erweitern der Tasksequenz:

TSAddStepFinal

Im Grunde gilt das Prinzip auch für die übrigen Tasksequenzschritte.

Übrigens, “Groups” könnt ihr auch bilden, in dem ihr anstelle eines “SMS_TaskSequence_*Action” Objekt, ein “SMS_TaskSequence_Group” Objekt in die Liste der Steps einfügt.

Weiterhin viel Spass!

-Ben

6 comments

  1. mschneider says:

    Hallo Ben,
    ich würde gerne mit dem von dir beschriebenen Verfahren einen vorhandenen Tasksequenzschritt ändern, bzw. wenn das nicht möglich ist, erst löschen und dann neu erstellen… Konkret geht es darum das Operating System Image Package bei dem Tasksequenzschritt “Apply Operating System” zu aktualisieren. Hast du einen Tipp für mich, wie ich das realisieren kann? Bin leider nicht ganz so fit in PowerShell 😉

    • Ben says:

      Hallo mschneider,

      du kannst bestehende Schritte auch ändern. Ich habe deine Anfrage direkt als Grund für einen neuen Beitrag genommen 🙂 http://www.thegeeksclub.de/archives/166. In dem ich das prozedere erläutere. In deinem Fall ist der TaskSequenceStep von der Klasse “SMS_TaskSequence_ApplyOperatingSystemAction”.
      Das heißt du suchst dir den bestehenden Step heraus, wie im Beitrag erklärt und änderst die Werte entsprechend ab:

      $step.Name = “string , Name des Steps”
      $step.ImagePackageID = “string , PackageID vom OS Image”
      $step.ImageIndex = “int , Index im Image”

      Bei Bedarf kann ich dir auch gerne ein Beispiel Script zur Verfügung stellen.

  2. mschneider says:

    Hallo Ben,

    vielen Dank für deine Mühe. Echt Klasse! Das hat mir schon mal sehr geholfen. Allerdings bekomme ich es nicht hin, einen Step unterhalb einer Gruppe zu editieren (Bsp. “Apply Operating System” unterhalb von “Build the Reference Machine”. Die Variable $taskSequenceSteps liefert mir nur die beiden Task Sequenz Gruppen “Build the Reference Machine” und “Capture the Reference Machine”. Ich denke da liegt das Problem. Vielleicht wäre ein Beispielscript doch ganz hilfreich 😉

    • Ben says:

      Hallo mschneider,

      kein Problem 🙂 Eine Gruppe liest du ähnlich aus wie die Tasksequenz selbst. Die Gruppe hat auch “Steps”, welche du auslesen/ verändern kannst und dann wieder in die Gruppe zurück schreibst. Leider bin ich bisher nicht dazu gekommen, aber ich werde dir hier gerne ein Beispiel zukommen lassen.

Leave a Reply

Your email address will not be published. Required fields are marked *