October 10, 2021

Organizational Units, and Virtual Machines

I got an e-mail from a forum buddy yesterday asking about a quick and easy way to change the OU that a deployed virtual machine (or any machine for that matter) belongs to.

When machines are added to an Active Directory domain, they are automatically joined to the Computers OU. Well, actually, Computers is a Container, but that’s semantics.

Group Policy Objects can’t be applied to the Computers container, and as a result, they cannot be leveraged on computers in that container. That is, with the exception of a domain wide GPO.

So what is a Windows/VM admin supposed to do when deploying 100+ VDI Desktops, 10 new guests, or the like, and they want to ensure that they have the deployed systems in the right OU?

I worked on part of the puzzle last night, and felt pretty good about the work I had done. Then when I awoke this morning, a couple more concerns came to mind.

Well, there’s 2 ways to tackle this problem (one secure and inflexible, another insecure and flexible).

Let’s start with the insecure one:
Create a local script or executable that will move the Computer to a new OU
1. Easy to use, can be very flexible, depending on how coded
2. Add the ability to accept command line parameters to change the target OU
Different OU paths could be implemented by different .bat files in the RunOnce registry key.
One .bat for one path, another for a different one, etc.
3. Is run from the deployed system, and can be pretty much automated.
4. Is run dynamically, and automatically is run as new templates are deployed

1. The script cannot make changes to Active Directory, unless the logged in account has AD privileges.
2. To run the script as a non-authorized user, the script would have to be run with an alternate user’s credentials
3. Is there any secure way to store this process where it cannot be exploited?

Create an admin script/exe that a privileged user runs outside of the VM
1. The script is only run by an authorized user, outside of the VM
2. Leveraging existing tools, and an administrative user’s security can be better protected

1. Manual
2. Someone has to know to run it

So is there a way to have your cake and eat it too?
In a perfect world, yes. In the real world, not without some finagling.

An imperfect method, a local script with elevated privileges:
I did write a vbscript that will handle the move (minus the elevated credentials piece), but decided to turn it into an executable using AutoITScript. After downloading the AutoITScript package, and installing it, save the script below as an .au3 file, and use the AutoITScript package to compile it.

Here’s the AutoITScript:

#cs —————————————————————————-

AutoIt Version:
Author: Jase McCarty

Script Name: moveou.au3
Script Function:
Take a computer out of the Computer Org Unit,
and place it in a specific target Org Unit

Syntax: moveou.exe “OU=Target,DC=domain,DC=com”

#ce —————————————————————————-

; Setup constants for writing to the
; event log
Global Const $SUCCESS = 0
Global Const $ERROR =1

; Setup our variables so we can have
; space to work
Dim $WshShell,$oMyError,$sComputer
Dim $sTargetDN,$sRootDSE,$sDomain
Dim $sCompLongDN,$sCompShortDN
Dim $objCont,$sErrorSet

; Create a Windows Shell object,
; so we can write to the event log
$WshShell = ObjCreate(“WScript.Shell”)

; Create an AutoIT Error handler,
; for error trapping
$oMyError = ObjEvent(“AutoIt.Error”, “ComError”)

; Grab the local computer name
$sComputer = @ComputerName

; Grab the destination Org Unit’s
; Distinguished Name
$sTargetDN = “LDAP://” & $Cmdline[1]

; Grab the DefaultNamingContext so we have
; DC=company,DC=com or whatever
$sRootDSE = ObjGet(“LDAP://RootDSE”)
$sDomain = $sRootDSE.Get(“DefaultNamingContext”)

; Set the Short and Long Distinguished Name values
$sCompShortDN = “CN=” & $sComputer
$sCompLongDN = “LDAP://CN=” & $sComputer & “,CN=Computers,” & $sDomain

; Load the Target Org Unit as an object
$objCont = ObjGet($sTargetDN)

; Move the Computer into the Target Org Unit
$objCont.MoveHere($sCompLongDN, $sCompShortDN)

; If we were successful, write to the event log as such
If $sErrorSet <> 1 Then
$WshShell.LogEvent ($SUCCESS, $sComputer & ” moved to the ” & $sTargetDN & ” organizational unit”)

;COM Error function
Func ComError()
; If we weren’t successful, write to the event log as such
$WshShell.LogEvent ($ERROR, “Error running MoveOU script” )
$sErrorSet = 1
Return 0

Again, this doesn’t handle the elevated credentials piece. After the script is compiled into an exe, it would be run from the command line like this:

moveou.exe “OU=Destination Org Unit,”

And yes, the following comma is necessary.

Now on to the elevated credentials piece.
To elevate credentials, one must use a package like “runas”, a custom AutoITScript, or possibly CPAU from joeware.net.

To do this with “runas”, there is one caveat. The password cannot be passed as part of a script.

To do this with CPAU, a batch file with the following contents would have to be used:

CPAU -u user [-p password] -ex “moveou.exe ‘”OU=Destination Org Unit,'”

And the destination org unit has double quotes on the outside, and single quotes on the inside.

To do this with AutoITScript, another au3/exe would have to be created that can elevate the credentials. Here’s one that would do that for you:


Runas($CmdLines[1],$CmdLines[2],$CmdLines[3],0,”Drive:PathTomoveou.exe ” & $CmdLines[4])

This would be run as

moveelevated.exe username domain password ‘”OU=Destination Org Unit,'”
*Disclaimer… I haven’t tested this, but it should work.

Two of these three options (not runas, due to the password limitation) could be put in a RunOnce registry key, and would accomodate the move once the guest has been joined to the domain, and rebooted.

If used with a VMware VirtualCenter’s Customization Specification, this line could be put in a runonce instruction. The data (including the password) would be stored in the VC database, and not be sent across the network, except through the customization package dropoff during a guest deployment.

A better method, being run by an admin (on the back end):
1. Using DSADD (before the guest is added to the domain)
A preferred method, would be to add the computer accounts into Active Directory, in the appropriate OU beforehand, and as a result when the new machine is added to AD, it will already be in the correct OU.

This can easily be done using the dsadd command.

dsadd computer “cn=ComputerName,ou=Test Servers, dc=domain,dc=com”

Or it can be put in a batch file that will accept a parameter

dsadd computer “cn=%1,ou=Test Servers, dc=domain,dc=com”

Which would be run like this:

addcomputer.bat ComputerName

This method is very easy if you know the name of the computer you wish to add. And keep in mind, this would have to be run before the guest is joined to the domain.

2. Using the previously mentioned moveou.exe (after the guest is on the domain)
By modifying the moveou.exe script to accept the computername as a command line parameter, an administrator could run the command with the destination OU, as well as the computername, and have the script move the computer to the desired OU.

moveou.exe “OU=Destination Org Unit,” ComputerName

As before, the admin would have to do this manually.

3. Use the existing Active Directory Users & Computers interface.
I shouldn’t have to say anything about this.

How VMware could help us out:
Now, if VMware could add a piece to the deployment process, where after a machine is run, a command could be run (outside of the guest) with the rights of the VirtualCenter user that deployed the machine.

Mass deployment of guests and assignment to an appropriate OU is somewhat problematic right now. Until we get a little extra functionality from VirtualCenter, we’ll have to continue the hacking.

Update, I forgot to mention… There’s a PDF on viops.vmware.com by Chris Skinner, that also addresses this issue.

2 thoughts on “Organizational Units, and Virtual Machines

  1. Thought you would like to know that WinBatch can be used to script your “insecure” method so it is secure. WinBatch has an ADSI extendor that lets you bind to AD with alternative credentials (dsSetCredent()). Compile your script and the password is then nearly impossible to recover. (WinBatch actually boasts about this). One tip though – if you do that put the password in a variable, because if for whatever reason there was an error in that function you would not want the password posted to the error message box. Its better the error show the variable name than the password string!

    I worked at a company that used WinBatch almost exclusively for desktop management (XP based), and we did basically the same types of things you are talking about. The whole OS build was automated like this, moving the computer to the correct OU and so forth.

    WinBatch is quite flexible and powerful. It has been around a long time and that is for a reason. It is updated all the time and support is awesome. I highly recommend it.

  2. Also remember that if the domain is Server 2003 functional level or higher, you can use redircmp.exe (http://support.microsoft.com/kb/324949) to automatically set the OU in which new computer accounts are created.

    An alternative would be to use a command-line importer like csvde.exe or ldifde.exe to prestage the computer accounts in your chosen OU, and then when they were created through VDI the process would use the prestaged computer account instead of creating a new one in the default OU. It’s Microsoft best practice and saves you a lot of scripting.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.