lunes, 13 de febrero de 2012

Moving computers in Active Directory using a webservice

Moving computers in Active Directory using a webservice

March 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, 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
        Try
            Computer.MoveTo(OU)
            Result = True
        Catch exc As Exception
            Trace.WriteLine("MoveComputerToOU: Unhandled exception - " & exc.ToString)
        End Try
    End If

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.
That's it?
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
            End If
        End If
    End If

 
    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
                    End If
                End If
            Next
        End If

        Return Result
    End Function

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.
    [Default]
    StagingOU=OU=MDT,DC=mydomain,DC=com

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):
    [MoveComputerToOU]
    WebService=http://MyWebServer/Deployment/ad.asmx/MoveComputerToOU
    Parameters=ComputerName,MachineObjectOU
    MachineObjectOU=OUPath

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 most current version of the Webservice used in this example can be downloaded from MDTCustomizations on CodePlex. Also the example scripts from this post can be downloaded from this CodePlex project (Download Example script)
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?
UPDATE: With MDT 2010 there has been a couple of changes in the way webservices are beeing called and how the response needs to be handled. See Making custom Database and Webservice scripts work again in MDT 2010 for more information.
Enhanced by Zemanta

Setting the Computer Description in Active Directory during MDT Deployments

Setting the Computer Description in Active Directory during MDT Deployments

December 14, 2009
A quite common question I got so far  is how one would be able to set or update the computer description in Active Directory during the Deployment or maybe also later using a logon script. I personally like to store some additional information about the computer itself in the description property like Asset Tag/Service Tag or the Date of the initial or latest build, the last logged on user, etc. This way this information is available for everybody using the Active Directory Users and Computers snap in or one can use tools like the logparser or AdFind to query for specific information.
There are actually a lot of ways how to achieve this. You can use command line tools like dsmod or script everything in vbscript and a lot more. Powershell would also be a very good choice for this but as long as it is not available in WinPE it will only be second choice. As I like command line tools and custom scripts for all the stuff which vary often, I like to have common or often used scenarios implemented as easy as possible. Especially during Deployments and when running scripts in System context I prefer to use webservices. I don’t need to take care about the specifics of the local computer (32/64 Bit, Server, Old client OS, logged on user, etc). All it requires is to be able to make an http request. And with Version 6 of the Deployment Webservice you now got the possibility to set and read the computer description.
OK, let’s start.

Get the computer description

The first thing we need is the computer Description itself. Generally there are three ways to get it. Manual, automatic or a combination of them. Actually the MDT wizard does already contain the necessary bits to be able to let the user manually enter a computer description. If you just open the “DeployWiz_Definition_ENU.xml” file and search for “Computer Description" you should end up at line 328 in MDT 2010 (or line 208 in MDT 2008). There you will see a part to enter a Computer Description which has been commented out on default. So if you would like to make this available just remove the “<!--“ and the following “-->” . Now if you run the wizard it will show the Computer Description field on the same pane where you normally enter the computer name. Also the built-in scripts will make sure that the computer description is now populated into a property called “Description” which can be used during the rest of the Deployment
The easiest automatic way is to use the “Set Task Sequence Variable” step in a Task Sequence. To e.g. use the SerialNumber as Description (hey, this is just an example) you could create a Step like this:
image
If you need to have a more complex Description defined automatically, you most probably want to use a custom script to generate it. Let’s assume you want to store the Serial Number and the current Date in the Description. You could now create a small custom script which would do this for you. The function could look like
Dim sDescription 


'Create Description 
sDescription = "SN=" & oEnvironment.item("SerialNumber") 
sDescription = sDescription & " - BuiltDate=" & Date 


'Store Description for future processing 
oEnvironment.Item("Description") = sDescription
Now you can add and execute this script at any point in your Task Sequence (preferably after the gather step and before you finally update the description in AD ;-) )
image
What you exactly need to store in the description and how you create it totally depends on your local needs. This shall just give you an idea on how to do it.

Set Computer Description in Active Directory


Ok, we now have created the computer description. Time to get this information into Active Directory. In this example we will use a small script which will call the webservice function and submit the description. The information on how to reach the webservice is stored in the customsettings.ini. So let’s start with the customsettings.ini. We need to have a new section in there called “[SetComputerDescription]” (replace YourWebServer/DeploymentWebservice with the path to the webservice in your environment. I assume you already set up the Deployment Webservice and verified it is working):
[SetComputerDescription] 
WebService=http://YourWebServer/DeploymentWebservice/AD.asmx/SetComputerDescription 
Parameters=OSDComputerName, Description 
OSDComputerName=ComputerName 
Description=ComputerDescription

Now we create a small script that executes the webservice based on the information in this section. The main part looks like:
    ' Create the web service instance 
    Set oService = New WebService 
    oService.iniFile = "customsettings.ini" 
    oService.SectionName = "SetComputerDescription"

    ' Make the web service call 
    Set oXML = oService.Query 
    If oXML Is Nothing Then 
        oLogging.CreateEntry "Unable to call SetComputerDescription web service.", LogTypeWarning 
    Else 
        oXML.setProperty "SelectionNamespaces", "xmlns:mk='http://maikkoster.com/Deployment'" 
        If UCase(oXML.SelectSingleNode("mk:boolean").Text) = "TRUE" Then 
            oLogging.CreateEntry "Computer Description has been set.", LogTypeInfo 
            iRetVal = Success 
        End If 
    End If
Now we add a new “Run Command Line” step to our TaskSequence that executes this script. It should be within the “State Restore” phase of the Task Sequence. If you are running MDT 2010 preferably after the “Recover from Domain” step as your computer might not have joined the domain yet so it could become hard setting the description ;-). The webservice will take care about that it connects to the local Domain Controller of the machine calling the webservice as the new account might not be available on all Domain Controllers yet, depending on how much time it had to replicated after joining the domain.
image

OK, that’s it actually. Your are now able to set the computer description during a deployment. For your convenience all example scripts can be downloaded from CodePlex. All you need is putting them into your Scripts folder, update the customsettings.ini with the section described above and add the necessary steps to your Task Sequence. If you would like to write (or read) other properties from Active Directory, get back to this blog regularly as a step-by-step guide on extending the webservice for additional properties is coming soon.
Enhanced by Zemanta