Skip navigation

Directory Binding Script (Active and Open Directory)

14291 Views 34 Replies Latest reply: Jan 9, 2014 12:37 PM by atperseghin RSS
  • Kevin Trumbull Calculating status...

    I have not finished working on it yet, but if you'd like to see the direction I'm going with this I've posted a "snapshot" of the script on the Paste Bin website.

     

    The following link will expire on March 6th, 2012: http://pastebin.com/qW0QLKm2

     

    I didn't want to clutter this thread with unfinished and likely non-working code.

     

    It's probably worth mentioning that PasteBin also offers syntax highlighting, download, viewing of raw text, and expiration dates.  The download feature allows people to get a copy of your code with all of the orginal indentation intact.

     

    I plan on posting the finished script to this thread.  I also plan on putting it on Pastebin.com or possibly another one of the pastbin work-alike websites.

  • Kevin Trumbull Level 1 Level 1 (0 points)

    Sorry about the delay in replying...

     

    Regarding  command line usage, I completely agree.  What's currently there is pretty weak.  That's one of the things that I'd like to fix once it's  working.

     

    I have a couple of questions regarding your setup, mostly due to how some of the script is written...

     

    Near the top of the script there's the following line:

    check4ODtmp=`dscl localhost -list /LDAPv3 | grep -n 1 | sed 's/1://' | sed 's/2://'`

     

    Are you using an IP address to bind to the Open Directory server?

     

    I get the run the following command under 10.5, 10.6, and 10.7 I get the result listed below it:

    $ dscl localhost -list /LDAPv3

    server.domain.internal

     

    And yes, sadly we use a 'fake' TLD...

     

    The point is that there's no result that contains a "1".  Also it's kinda odd to have grep number the results (grep -n), only to remove them with sed (sed 's/1://')...

     

    Perhaps it's an uncaught error, that doesn't cause binding issues...

     

    I'll try to get around to uploading a newer version of the script soon.

     

    Anyway, thanks for your time...

  • Kevin Trumbull Level 1 Level 1 (0 points)

    seegorkesolot wrote:

     

     


    Kevin Trumbull wrote:

     

    Near the top of the script there's the following line:

    check4ODtmp=`dscl localhost -list /LDAPv3 | grep -n 1 | sed 's/1://' | sed 's/2://'`

     

    In my environment I have a few machines bound to two LDAP-Servers: OD and our Master LDAP for authentication instead of AD (which is essentially in Sync with the LDAP anyway). So I'll get two lines as a result of the command dscl localhost -list /LDAPv3.

     

    That's quite bad if I want to use this result afterwards as name for the server, as it will not work :-) So I just grep one of the two lines (by grep -n 1) and strip it off the line number. When the first server is unbound, check4ODtmp will return the second LDAP server, that's why I have two unbinding sections for LDAP Servers in the script.

     

    I admit this is ugly, a much better way to do this would be to fill an array with the ldap servers

     

    ldapArray=( $(dscl localhost -list /LDAPv3) )

     

    and then loop over the array for the unbinding process.

     

    for i in "${ldapArray[@]}"

    do

         :

    done

  • Kevin Trumbull Level 1 Level 1 (0 points)

    Apparently there is a timeout in the submission form.  It ate my reply.  For best results, use a text editor to type posts, since the submission form should not be trusted.

     

    Also, you cannot delete a post made in error.  Lame…

    ------------------------------

     

    I had originally wondered if you were trying to do the equivalent of "head -n 1" with "grep -n 1". 

     

    I got no results from "dscl localhost -list /LDAPv3 | grep -n 1" on a bound machine that showed the domain using "dscl localhost -list /LDAPv3".  So I checked the man pages.  "grep -n" numbers the lines, but the "1" is used as a matching pattern.

     

    "grep -n 1" will only show machines whose names contain a "1".

     

    Since the server in my environment has a "2" in the name, "dscl localhost -list /LDAPv3 | grep -n 2" will list it but "dscl localhost -list /LDAPv3 | grep -n 1" doesn't list anything.

     

    I'm guessing that there is a "1" somewhere in the names of your servers if they're showing up, since I've not had this work on 10.5, 10.6, or 10.7.

     

     

    I'd not thought of including support for multiple servers until I'd read this script.  It makes perfect sense to do so, and I'd  considered using an array to do it.  On the other hand, I've also been considering whether it might make  sense to put the check in a function and use a while loop to do something like that.  Using a function should make it easier to support multiple OS revisions, but might require more time than I have to put into it right now.

     

    Another intriguing thought…  Apparently in XCode 4, it's pretty easy to create automator actions that are simply wrappers for a shell script.  I'm not committing to turning this into an Automator action just yet, but it's definitely something I might look into when the script is done.

     

     

    Once again, thank you for your time.  While I've done a fair bit of shell-based automation work on MacOS X, I'm new to dealing with directory services in this environment...  I'm still trying to make sure that I'm not missing anything.

  • Kevin Trumbull Level 1 Level 1 (0 points)

    Regarding the handling of existing records...

     

    I very much like the idea of the script cleaning up after itself and not leaving zombie records, or duplicates laying around.  I've seen far too many problems crop up when things get cluttered.

     

    However, 95% of the time I'm re-imaging a machine that's already been bound to Open Directory and Active Directory at some point.  What I need is for the machine to be able to set itself back up to match it's previous configuration.  This means binding to directory services using the same name and computergroup it was previously set to use, while making it possible to alter those things if it needs to be redeployed.

     

    So the question was how best to keep persistant records of the machine specific information required to rebind the machine that will survive reformatting of the hard drive.  Years ago, a colleague and I did this using an FTP server to store a directory for each machine named using it's MAC address.  Our scripts created a config file containing all of the configuration data for each machine in it's MAC-named directory.  However, I don't want to do that this time for a number of reasons.

     

    It occurred to me that I should look into the directory services themselves for this information.  Since my knowledge of LDAP specifics was somewhat limited it took me a bit to figure the following two things out...

     

     

    The first thing is that Open Directory stores the MAC address of the wired ethernet card (en0) in computer records.

     

    The following command should use the MAC address to retrieve the computer name of any previously bound machine from Open Directory:

    /usr/bin/ldapsearch -x -h OpenDirectoryServerFQDN -b "cn=computers, dc=YourServer, dc=YourDomain, dc=TLD" "macAddress=`ifconfig en0 | awk '/ether/{print $2}'`"

    -- Note: That's a single command line...

    -- For some reason, this command did not require authentication or being on the domain to work.

     

    This also removed the dependancy on the imaging solution or me to rename the machine properly.  The command will pull the name from Open Directory regardless of how you re-image or re-install the OS.

     

     

    The second obstacle I needed remove in making a single script able to work for every machine in my environment was the issue of computer groups.

     

    The following command should hopefully use the computer name to retrieve the computer group any previously bound machine from Open Directory:

    dscl -u OpenDirectoryAdmin OpenDirectoryServerFQDN -list /LDAPv3/127.0.0.1/ComputerGroups Member | awk '/ComputerName/{print $1}'

    -- Note: That's a single command line...

    -- This requires authentication, and will probably have to be scripted using "expect" since I couldn't get the "-p" or "-P" options to work.

     

    This removes the dependancy in the script for specifying which group a machine was previously in.  There's 82 computer groups in my environment, which is more than I care to worry about keeping track of.   This should  allow for more precise handling of computers that are in multiple computer groups.

     

     

    There is another reason why I believe that querying the directory service server is the optimal way to deal with this. If I'm not mistaken, since the script is querying the "main" Open Directory server, it should always be in sync with that server.  This should make it possible to simply configure these things on the server, and the script will take care of the rest.  If possible, I'd prefer not to have to configure anything on a client machine beyond building the inital install.

     

    Since I don't have a great deal of experience with digging into directory services at this low a level, I'm not sure if the paths have changed between versions of OSX server, or how much this varies from one deployment to another.

     

    I'd appreciate whatever feedback I can get regarding those commands and environments other than my own.  I'd also appreciate whatever insight and/or improvements anyone can offer.

  • Kevin Trumbull Level 1 Level 1 (0 points)

    Kevin Trumbull wrote:

    dscl -u OpenDirectoryAdmin OpenDirectoryServerFQDN -list /LDAPv3/127.0.0.1/ComputerGroups Member | awk '/ComputerName/{print $1}'

    -- Note: That's a single command line...

    -- This requires authentication, and will probably have to be scripted using "expect" since I couldn't get the "-p" or "-P" options to work.

    I'd prefer to use "ldapsearch" to retrieve the computer group since it might work without requiring the use of "expect".  Where possible I try to avoid using "expect".

     

    I tried a number of different variations using  "ldapsearch" as well as "dscl" to get the information.  Using the above "dscl" command finally provided the information.

  • Kevin Trumbull Level 1 Level 1 (0 points)

    While it's likely that seegorkesolot understands the above two commands, I'll explain them for whomever else may come across this thread.  I also noticed an error, which is corrected below.

     

    The "dscl" command (quoted in the previous post) does the following:

    1. It uses "dscl -u OpenDirectoryAdmin OpenDirectoryServerFQDN" to search the specified server as the user "OpenDirectoryAdmin".
      1. This command will ask for a password before it provides the requested information.
      2. There are options that are supposed to allow you to specify the password as well, however it's been widely reported that they don't work.  I couldn't get them to work either.
    2. It uses "-list /LDAPv3/127.0.0.1/ComputerGroups Member" to list the "Member" information for every computer group on the server.
      1. It's possible that the path could be something other than "/LDAPv3/127.0.0.1" on other servers.  I think this is the default in 10.5 server.
      2. It lists the groups, one per line, with the name of the group followed by a list of the machines in it.
      3. There are a number of different attributes which contain a list of group members.  They are "dsAttrTypeNative:memberUid" and "GroupMembership".  I'm not sure which one of the three is best to use.  I arbitrarily picked "Members".
    3. It uses "awk '/ComputerName/{print $1}'" to get the computer group name.
      1. The text used for "ComputerName" will be what was retrieved using the "ldapsearch" command (quoted below).
      2. The above "awk" command matches every single line that contains the name of the computer.
      3. This means that if the computer is in multple groups, this command should return the names of all of them, one per line.

     

     

    I just noticed an error.  The "ldapsearch" command in the previous post should be:

    /usr/bin/ldapsearch -x -h OpenDirectoryServerFQDN -b "cn=computers, dc=YourServer, dc=YourDomain, dc=TLD" "macAddress=$(ifconfig en0 | awk '/ether/{print $2}')" | awk '/realname/{print $2}'

    The "ldapsearch" command (quoted above) does the following:

    1. It uses "$(ifconfig en0 | awk '/ether/{print $2}')" to get the information of the main (wired) ethernet card.  It passes that information to "awk" which outputs the second bit of information on the line that contains the text "ether".
      1. The result is the MAC address.
      2. Since the command is wrapped in "$()", it's run before anything and the result is inserted into the command line.
      3. I'll probably use a pre-set variable for this in the script.
      4. I initally used ` `(backticks) to wrap the command but changed it to $(), since that's generally preferred...
    2. It uses "ldapsearch -h YourServer.domain.TLD -b "cn=computers, dc=YourServer, dc=domain, dc=TLD" "macAddress=00:00:00:00:00:00"" to retrieve the computer's record from the specified Open Directory server.
      1. I used "00:00:00:00:00:00", shown above, represents the MAC address of the machine retrieved using ifconfig.
      2. It searches the "computers" container.  (It's possible that your machines may not be in this container, however "computers" should be the default.)
      3. It uses the MAC address of the machine it's run on to find it's computer record.  The MAC address can generally be trusted to be unique.
    3. It sends the information in the computer record to "awk '/realname/{print $2}'", which retrives the computer name from the record.
      1. I could have also used "awk '/cn:/{print $2}'" or "awk '/apple-realname:/{print $2}'"

     

     

    It's also worth noting that I've figured out how to retrieve a few other bits of data as well.

    1. The Open Directory UID and GUID for a machine which has been bound.
    2. The GeneratedUID attribute for Open Directory computer groups.
    3. "dscl -u OpenDirectoryAdmin OpenDirectoryServerFQDN -list /LDAPv3/127.0.0.1/ComputerGroups" should output a list showing all of the computer groups on an Open Directory server.
    4. The computer's name from Active Directory.
    5. "ldapsearch -x -D Domain\\ActiveDirectoryAdmin -W -H ldaps://Domain.TLD:636 -b "DC=Domain,DC=TLD" -LLL -v "(sn="loginname")"" will output the information contained in an Active Directory user's account.  No "private" information is disclosed; mostly it's a list various IDs, group memberships, and the sort of stuff that's listed under contacts in Outlook.

     

    I've found quite a bit more information while figuring this stuff out, but it doesn't seem applicable to the script.  As always, I could be mistaken about that...  If there are other bits of information that would smooth things out, let me know.

  • dmare Calculating status...

    What's the latest stable version of this script?  I am hoping to use it this weekend for binding a few Macs to Active Directory.  (Schema extended, so no OD server applicable.)

  • ptrondsen Level 1 Level 1 (0 points)

    Hello All,

    I am having a lot of problems with binding in 10.7.3. I had my own script based on Bombich's script, which I used for Snow Leopard, and even worked in 10.7.2, but since 10.7.3, I am having no success.

    I tried seegorkesolot's script and it still does not work, I can bind fine, but I cannot login as an AD User.

    When I change the IP6 configuration to none, or local-link only, I can login, but after a restart, I cannot login.

    I've tried all the tips to no avail.

    My current computer is on 10.7.3, and I can login, but I updated it from 10.7.2, which was already bound.

    The problem is occuring with newly imaged Macs, with a 10.7.3 build I created.

     

    Any suggestions would be helpful.

    I may go back to 10.7.2.

     

    Thanks,

    Pete

  • sthrom Calculating status...

    Hi Pete,

     

    I've been having the same issue, I'm binding to only one forest so I uncheck the any domain in forest option. but also if you do you have to have in your search policys Active Directory/DOMAIN_NAME/All Domains and /Active Directory/DOMAIN_NAME/you.domain.here. Also the way they are ordered is very important, you should have your OD server listed first then you.domain.here and the All Domains. When using a script it will always put the AD domains backwords hence All Domains first then your.domain.here. The work around I found for this is to create your image with your.domain.here in the search policys and it will be ordered correctly. I have spoke nto Apple about this and they havent had much to say except that the search policy has to be the way I mentioned above.

     

    Also I have been having issues joining my OD server, I would get the green light saying its connected but it never adds the computers to the OD computer group or lists. I've noticed the only way it will work is to Bind with authentication but the script keeps failing saying cant bind with credentials. I even added the credentials to one of the lines in the OD section of the script since it was not there and it started to work but now seems to be intermittant. I think my big issue is that im in a .local domain. Also just so your aware I can join the OD fine by using system preferences but sometimes in Directory Utility I have the same issue as scripting. I also called Apple on this and they recommended binding through system preferences because they are having issues with directory utility.

  • ptrondsen Level 1 Level 1 (0 points)
    Currently Being Moderated
    Mar 27, 2012 10:05 AM (in response to sthrom)

    Hey Sthrom,

    Thank you for the useful information.

    That really seems to work, changing the search policies. it's interesting because in Snow Leopard, it was always important to keep the AD Search paths at the top. I'm going to start all over to make sure it works again, but thanks for the info.

    The OD part of the script is working for me, which is nice.

     

    Also, originally, I used to have scutil part of the bind script, now run that as a separate script before the binding script, so it's picked up dynamically.

     

    Thanks,

    Pete

Actions

More Like This

  • Retrieving data ...

Bookmarked By (2)

This site contains user submitted content, comments and opinions and is for informational purposes only. Apple disclaims any and all liability for the acts, omissions and conduct of any third parties in connection with or related to your use of the site. All postings and use of the content on this site are subject to the Apple Support Communities Terms of Use.