|Moving computers in Active Directory using a webservice|
By: Maik Koster
Posted On: 6/15/2009
If you are using GPOs in your Active Directory Environment you can come into a situation during your deployments, where the settings of a GPO might have impacts on the deployment process. To avoid this and be able to have full control on what happens during the deployment, one way to solve this problem is to have a specific OU which blocks all GPOs. I refer to this as staging OU. But this raises two other problems.
When we add a new computer it's quite easy to have it added to a specific OU. But what happens with existing computer accounts if you are doing a reimage? Sure you could delete the computer object first and have it recreated. But often you have specific settings on some computers, they are member of some security groups etc. and you probably want to preserve this information. In this case you need to be able to re-use the former computer account. So we need to move the computer from the existing OU to the staging OU.
Second after the deployment process has finished, what happens to the new (or old) computers in the staging OU? Want your helpdesk have them moved to the destination OU? I would expect to have it moved automatically.
This problem has already been touched by Ben Hunter How to move a computer object in Windows PE and How to ensure the computer is in the correct OU
The idea behind it is basically to have a StagingOU and three scripts. The first script will be executed right before all settings will be written to the sysprep.inf and the computer reboots. It will try to move an existing computer account to the staging OU and swap the MachineObjectOU and StagingOU Values. This way new computers will end up in the staging OU during the sysprep.
The second script will swap the values back to it's original values, so MachineObjectOU is now pointing again to the final OU for this computer. This third script will actually move the computer account to the MachineObjectOU and should be executed quite late in the Deployment process.
Actually this is working great. Good work Ben!
There are some requirements like ADSI and credentials with proper permission to move computer objects must be available for the script, but nothing really difficult. So why would I want to write something different? Actually moving computers is a quite common task. e.g. if you have a couple of locations with assigned OUs and users are moving you might want to have a way to automate (or semi-automate) the moving of computer objects. Anyway, I wanted to have a more generic way to move computer objects. And I wanted to be able to request this move from different sources. So I ended up creating another section for my webservice.
Moving an object in AD using the .net Framework is actually quite easy. You only need to get the DirectoryEntry for the OU you would like to move the object to:
' Get OU
Dim OU As DirectoryEntry
' Remove "LDAP://" from OU Path if necessary
OU = New DirectoryEntry(Serverpath & "/" & StripLDAP(OUPath), _Username, _Password)
Then we search for the Computer object
' Search for the computer
Dim Computer As DirectoryEntry
Computer = FindComputer(ComputerName).GetDirectoryEntry
and if found, move the Computer to the OU:
' Move computer to OU
If Not Computer Is Nothing AndAlso Not OU Is Nothing Then
Result = True
Catch exc As Exception
Trace.WriteLine("MoveComputerToOU: Unhandled exception - " & exc.ToString)
The FindComputer Function which you see above is simply searching for all objects with "cn=ComputerName" and return the first result:
'Create Active Directory Searcher to get AD Object
Dim ADSearcher As New DirectorySearcher(_Root, String.Concat("(", SearchProperty, "=", SearchValue, ")"))
' Get only the first result. Search should find unique objects
Dim SResult As SearchResult = ADSearcher.FindOne
So far so good. That was easy to implement. Now we just take the scripts from Ben Hunter and change them in a way that they not move the computer themselves, instead calling the webservice.
Here comes the caveat. The webservice is running on a webserver and will probably contact the closest Domain Controller. So the Computer will be moved but due to replication intervals and topology this change can take a while, before the Domain controller of the site the computer is sitting in knows about it. Depending on the duration of your Deployment process this will have some funny results. During the Testphase of this change we even "lost" some computers somewhere.
The solution to this is to do this change on the remote Domain Controller of the site of the client requesting this change. If you look at the code above, you will find the string "Serverpath". So if you create this DirectoryEntry not with LDAP://yourdomain.com... but with LDAP://YourLocalDomainController/... everything will work as supposed. But how do we get the local Domain Controller?
First we need to find the AD Site. I posted already how to do this: Get Active Directory Site for IP Address
So have a look on this link for further references. Then we need to find a Domain Controller for this site (we just pick one as we assume replication within an ADSite is fast enough. We could change the function to take the DC as an argument but so far we haven't had any problems):
' Connect to local Domain Controller to avoid problems when moving computer accounts
If HostIPAddress <> "" AndAlso HostIPAddress.Substring(0, 3) <> "127" Then
Dim Site As String
' Get SiteCode for Host IP Address
Site = Me.GetSite(HostIPAddress)
' Get First DirectoryServer of Site
Dim DC As DirectoryEntry
DC = GetDCForSiteCode(Site)
If Not DC Is Nothing Then
' Connect to local Domain Controller
Serverpath = DC.Path.Substring(0, DC.Path.IndexOf("/", 7))
Dim DE As New DirectoryEntry(Serverpath, _Username, _Password)
If Not DE Is Nothing Then
' Change context to new DC
_Root = DE
Private Function GetDCForSiteCode(ByVal SiteCode As String) As DirectoryEntry
Dim Result As DirectoryEntry
If SiteCode <> "" Then
For Each Site As ActiveDirectorySite In Forest.GetCurrentForest.Sites
If Site.Name = SiteCode Then
'Get first DirectoryServer from Site
If Site.Servers.Count > 0 Then
Result = Site.Servers(0).GetDirectoryEntry
As we are now able to move a computer, we need to implement this into our Deployment Process. As already mentioned, we will use three scripts. The first is called Z-MoveComputer_StagingOU.wsf. If a value for the custom Property "StagingOU" has been supplied it will swap it with the value of MachineObjectOU and try to move the computer to the staging OU based on the current computername. The script needs to run before(!) the Configure task.
The Second script is called Z-Movecomputer_SwapOUValues.wsf and will simply revert the change we have done. So it needs to run after(!) the Configure task. (But before the third script :-) )
The third script is called Z-MoveComputer_HostOS.wsf and will try to move the current computer to the OU specified in MachineObjectOU (which has been set back to it's original value with the second script). So it should be executed quite late in the process somewhere in the State Restore Phase.
To be able to use these scripts, you need to add some information to your CustomSettings.ini. You need to define a custom property called StagingOU
Properties=..., StagingOU, ....
and supply a value for it.
And we need to have a section called "MoveComputerToOU" with the definition of the webservice call (you can choose a different name but then you need to adjust the scripts):
That's all. The Webservice itself expects two Parameters, Computername and OUPath. As we use the Property "MachineObjectOU" to store our value, we need to tell the MDT Script to call the webservice with the proper Parameter. That's why we need to include this "MachineObjectOU=OUPath" mapping in this section. MDT will then call the webservice using the value of MachineObjectOU for the Parameter OUPath.
There are actually other usages for this Werbservice. One I'm thinking about to implement is to move a computer to some kind of "Disabled computers" OU during the capture of computers which are about to be replaced. Or use it as part of a standardized way to move (and probably rename) computers between OUs to comply with your Business rules. It's up to you.
The Webservice can be downloaded at the end of my Post: A Webservice for OSD and MDT Deployments which has been updated with the necessary functions. Also you can download the example scripts from this post here MoveComputer_v1.zip
It seems I need a different solution to store my files. It gets confusing ;-)
Btw. I would be happy to get some feedback on the problems or additional ideas you had what could help others. The webservice seems to grow to a generic one covering a lot of functions. So what could come next?