First thing you will need to installf Maik Koster’s “Deployment Webservice“. This will give you the ability to do cool stuff like query AD for a list of OUs, or move computers around to different OUs, and add/remove computers from SCCM collections. Any many other actions similar to that.
Once you have it installed and working, use the ad.asmx page to verify the GetOUs service functions.
Create a new package containing INI files for each action you wish to perform from your task sequence.
Here are my GetOUs examples:
My GetOUs.ini file:
[Settings] Priority=Init,GetOUs Properties=Path(*),ParentPath,Level [Init] ParentPath=OU=Laptops,OU=ViaMonstra,DC=corp,DC=viamonstra,DC=com Level=1 [GetOUs] WebService=http://cm01.corp.viamonstra.com/WebService/ad.asmx/GetOUs Parameters=ParentPath,Level
- I am creating the Path() list variable to store all of the returned Path elements.
- I then set the two required input arguments; ParentPath and Level.
To test that it works, I added a pause step to my MDT Deployment Task Sequence.
My Pause Script:
MsgBox “OK”
Then I copied a Gather step and changed the package to my newly created package containing MyGetOUs.ini.
In the “Rules files” field, I enter the full name of my INI file.
- Note: Gather steps must occur after a “Use Toolkit Package” step, because this step copies the MDT scripts local which are utilized during the Web Service calls.
- Place the GetOUs web service call before the pause, so you can review the ZTIGather.log output to ensure the PATH variable is populated.
Next we need to prompt the user with the captured OUs. For this, I used a Powershell script. So I first needed to add Powershell to my WinPE Boot Image.
Last, I added a package with my Powershell script. I do not add a program, because I’m going to call the script via a Run a Command Line step.
Here is the script contents:
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") Function Set-OSDComputerName { $ErrorProvider.Clear() $Error.Clear() if ($objComputerName.Text.Length -eq 0) { $ErrorProvider.SetError($objComputerName, "Please enter a computer name.") } #Validation Rule for computer names. elseif ($objComputerName.Text -match "^[-_]|[^a-zA-Z0-9-_]") { $ErrorProvider.SetError($objComputerName, "Computer name invalid, please correct the computer name.") } else { $OSDComputerName = $objComputerName.Text.ToUpper() $TSEnv.Value("OSDComputerName") = $OSDComputerName } } Function Set-OUPath { if (($objListBox.Items.Count -ne 0) -and ` ($objListBox.SelectedItem.Length -eq 0)) { $ErrorProvider.SetError($objListBox, "Please select an OU.") } else { $TSEnv.Value("OUPath") = $objListBox.SelectedItem } } $ErrorProvider = New-Object System.Windows.Forms.ErrorProvider $TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment $objForm = New-Object System.Windows.Forms.Form $objForm.Text = "Computer Configuration" $objForm.Size = New-Object System.Drawing.Size(300,300) $objForm.StartPosition = "CenterScreen" $objForm.KeyPreview = $True $objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") {$x=$objListBox.SelectedItem;$objForm.Close()}}) $objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape") {$objForm.Close()}}) $OKButton = New-Object System.Windows.Forms.Button $OKButton.Location = New-Object System.Drawing.Size(10,230) $OKButton.Size = New-Object System.Drawing.Size(75,23) $OKButton.Text = "OK" $OKButton.Add_Click({ Set-OSDComputerName Set-OUPath #If ($Error.Count -eq 0){$objForm.Close()} if (($ErrorProvider.GetError($objListBox) -eq "") -and ` ($ErrorProvider.GetError($objComputerName) -eq "")) { Write-Host "Populated ComputerName: $objComputerName.Text" Write-Host "Populated OUPath: $objListBox.SelectedItem.Text" $objForm.Close() } }) $objForm.Controls.Add($OKButton) $objLabel = New-Object System.Windows.Forms.Label $objLabel.Location = New-Object System.Drawing.Size(10,20) $objLabel.Size = New-Object System.Drawing.Size(280,20) $objLabel.Text = "Please Select a Target OU:" $objForm.Controls.Add($objLabel) $objListBox = New-Object System.Windows.Forms.ListBox $objListBox.Location = New-Object System.Drawing.Size(10,40) $objListBox.Size = New-Object System.Drawing.Size(260,20) $objListBox.Height = 135 $objListBox.ScrollAlwaysVisible = $true $TSEnv.GetVariables() | ?{$_ -like "PATH*"} | %{ $objListBox.Items.Add($TSEnv.Value("$_")) } If ($objListBox.Items.Count -eq 0) { Write-Error "No OUPaths located" } else {$objListBox.SetSelected(0, $true)} $objComputerNameLabel = New-Object System.Windows.Forms.Label $objComputerNameLabel.Location = New-Object System.Drawing.Size(10,180) $objComputerNameLabel.Size = New-Object System.Drawing.Size(280,20) $objComputerNameLabel.Text = "Please Specify a ComputerName:" $objForm.Controls.Add($objComputerNameLabel) $objComputerName = New-Object System.Windows.Forms.TextBox $objComputerName.Location = New-Object System.Drawing.Size(10,200) $objComputerName.Size = New-Object System.Drawing.Size(260,20) $objComputerName.MaxLength = 15 $objComputerName.Text = $TSEnv.Value("OSDComputerName") $objForm.Controls.Add($objComputerName) $objForm.Controls.Add($objListBox) $objForm.Topmost = $True $objForm.Add_Shown({$objForm.Activate()}) [void] $objForm.ShowDialog()
This script populates a TS variable named OUPath, which I use at the end of the Task Sequence calling a web service to move the computer to the selected OU. I perform the move at the end, to avoid having GPO settings interfere with my Task Sequence.
/Brian Gonzalez