This blog is intended to be PowerShell centric, but to also delve into the systems that I use PowerShell to either operate, manipulate or outright automate like MDT, Azure, MEMCM, MEMMI, etc. That said I’m also planning on delving into the coding side of those systems that aren’t PowerShell because strengthening other languages even just a little can help give you perspective that will help your PowerShell skill. So to that end we’re taking a break this weekend from our WinForm using Classes series to post the first in a series of my MDT code modifications. These will probably come sporadically. Today we’re going to jump into one of the simpler ones
MDT Deployment Wizard:
Friendly Organizational Unit Names
We’re going to utilize “Populate OU method #3” so before we modify the code we need to modify the source xml used in the current production code DomainOUList.xml. Now as a general rule we don’t want to break how the code currently runs so unlike some implementations of this little QOL on the internet we’re not going to replace things we’re going to instead add in what we need. Out of the box DomainOUList.xml has the following format
<?xml version="1.0" encoding="utf-8"?> <DomainOUs> <DomainOU>OU=SomeDivision,OU=SomeDepartment,OU=Departments,DC=SomeCompany,DC=US</DomainOU> <DomainOU>OU=SomeOtherDivision,OU=SomeDepartment,OU=Departments,DC=SomeCompany,DC=US</DomainOU> </DomainOUs>
We don’t want to change how that works so we’ll add in our friendly name as an attribute and leave the rest the same so our format becomes:
<?xml version="1.0" encoding="utf-8"?> <DomainOUs> <DomainOU FriendlyName="SomeDepartment SomeDivision">OU=SomeDivision,OU=SomeDepartment,OU=Departments,DC=SomeCompany,DC=US</DomainOU> <DomainOU FriendlyName="SomeDepartment SomeOtherDivision">OU=SomeOtherDivision,OU=SomeDepartment,OU=Departments,DC=SomeCompany,DC=US</DomainOU> </DomainOUs>
What’s crucial about this change is the file while it now holds our friendly names will STILL work with the production method of displaying the OU. If we can help it we don’t want to outright break how the code works out of the box.
As a bonus here’s my PowerShell script i use whenever I need to update my DomainOUList.xml
function Get-FN($Dn) { $parts = $Dn -split ',' | Where-Object { $_ -like "OU=*" } | % { ($_ -split "=")[1]} $Fn = $parts[-1..-($parts.length)] -join " " return $Fn.Replace("Departments ",[String]::Empty) } function Test-DN($Dn, $Root, $Keep, $Junk) { $Has = { param($Dn, $Values) $Values | Where-Object { $Dn -match "OU=$_" } } (& $Has $Dn $Root) -and (& $Has $Dn $Keep) -and -not (& $Has $Dn $Junk) } Function Get-ADOUs($Root, $Keep, $Junk) { Get-ADOrganizationalUnit -Fil * | ForEach-Object { [PSCustomObject]@{Dn=$_.DistinguishedName;Fn=Get-FN $_.DistinguishedName} } | Where-Object { Test-DN $_.Dn $Root $Keep $Junk } } function To-XML($Path) { $DesktopGroupOUs = Get-ADOUs ` -Root ` Departments, "Outside Agencies" ` -Keep ` Desktops, Laptops, "Kiosk Desktops", "Scanning Desktops", "CFR Desktops", "CVAC Desktops", "Interviewer Netbooks", "State Desktops", "MOW Desktops", "Virtual Desktops" ` -Junk ` RAMSOFTTesting, IVR, "QuickTime testing", "Apple QuickTime testing", "Step1 - OLD CAE Uninstallers", "Step2 - iAccess and NWPhoto 9 Deploy", mailboxes, "TEST-XPSCEPFix", "Testing NetSolutions IE Settings", "Citrix Disable Iconic Windows", "TEST Avaya NAC Settings", DocSTAR, BeaconTesting, RAMSOFTTesting $XML = New-Object XML $Root = $XML.CreateElement("DomainOUs") $XML.AppendChild($Root) $DesktopGroupOUs | Sort-Object -Property Fn | % ` { $Ou = $XML.CreateElement("DomainOU") $Fn = $XML.CreateAttribute("FriendlyName") $Fn.Value = $_.Fn $ou.Attributes.Append($Fn) $Root.AppendChild($Ou) $ou.InnerText = $_.Dn } $XML.Save($Path) } To-XML -Path "C:\temp\NewOUs.xml" | Out-Null
I’ll let you figure this one out on your own but basically it lets you pick multiple Roots, define partial matches to keep and names to avoid, then it writes it all to a new XML file feel free to take, tear it apart and change it for what you need.
Now Let’s code some VBScript!!
First thing is we’re going to find the code that adds each OU to our drop down list. It’s a little function called AddItemToMachineObjectOUOpt
. We’re going to leave this function ALONE and either above or below we are going to copy the ENTIRE function and paste it and make the following modifications
Function AddItemToMachineObjectOUWithFriendlyNameOpt(DistinguishedName,FriendlyName) Dim oOption set oOption = document.createElement("OPTION") oOption.Value = DistinguishedName oOption.Text = FriendlyName oOption.Title = FriendlyName MachineObjectOUOptional.Add oOption MachineObjectOUOptionalBtn.style.display = "inline" End Function Function AddItemToMachineObjectOUOpt(item) Dim oOption set oOption = document.createElement("OPTION") oOption.Value = item oOption.Text = item oOption.Title = item MachineObjectOUOptional.Add oOption MachineObjectOUOptionalBtn.style.display = "inline" End function
Should be pretty clear we’re doing almost the exact same thing as the production function but we’re now using 2 well named parameters and we’re assigning them to specific properties of our new Option element. But again we haven’t broken MDT’s ability to us the original function because it still exists AS it started.
So next and finally we need to modify the function that generates the entire Dropdown to allow us to CHOOSE which AddItem...
function we need based on the XML element it just read in.
Here’s what our code starts as
'''''''''''''''''''''''''''''''' ' ' Populate OU method #3 - Read ...\control\DomainOUList.xml ' ' Example: ' <?xml version="1.0" encoding="utf-8"?> ' <DomainOUs> ' <DomainOU>OU=Test1</DomainOU> ' <DomainOU>OU=Test2</DomainOU> ' </DomainOUs> ' If MachineObjectOUOptionalBtn.style.display <> "inline" then iRetVal = oUtility.FindFile( "DomainOUList.xml" , sFoundFile) if iRetVal = SUCCESS then For each oItem in oUtility.CreateXMLDOMObjectEx( sFoundFile ).selectNodes("//DomainOUs/DomainOU") AddItemToMachineObjectOUOpt oItem.text Next End If End if
We still need to loop through the XML elements so no need to change anything outside the For each
loop but we DO want to make a choice for each XML element so that’s where our code change goes focusing in on that we get
For each oItem in oUtility.CreateXMLDOMObjectEx( sFoundFile ).selectNodes("//DomainOUs/DomainOU") if ((oItem.attributes.length > 0) and (not IsNull(oItem.attributes.getNamedItem("FriendlyName")))) then AddItemToMachineObjectOUWithFriendlyNameOpt oItem.text, oItem.attributes.getNamedItem("FriendlyName").nodeValue else AddItemToMachineObjectOUOpt oItem.text end if Next
Pretty simple we just check to see if our element has any attributes and then if it has a FriendlyName attribute that actually has a value set if so we call our new AddItemToMachineObjectOUWithFriendlyNameOpt
function with said attribute value passed in. otherwise we call our original AddItemToMachineObjectOUOpt
function.
Once more to finish beating this dead equine all these code changes left the ability for the system to operate as it did before we made the code changes it didn’t break anything we can have a DomainOUList.xml file where some items have friendly names and others do not and our code will gracefully put in the friendly names for the ones that have it and just display the Distinguished Names for the ones that don’t.
And that’s it for this one. Not sure when or what the next MDT code change will be on I’ve got a Laundry list of changes I’ve implemented over the years but keep an eye out for the next one.
Hi,
You helped me with this setup back in January of this year on Reddit. I recently moved my MDT server to another machine and had to recreate the changes to the DeployWiz_ComputerName.vbs Everything is working correctly but, I noticed after I select the friendly name in the OU Drop down, it reverts back to the Distinguished Name. It’s not really a problem since the selection is already made but, it would be good to keep the Friendly Name just to make it look more polished. Do you have any tips on how to make that happen?