How to check if a computer is ACTUALLY bound to the AD

winningham_2
Contributor

So right now, I use dsconfigad to tell if a computer is bound to our AD, and while that works beautifully, it is not complete. For example, I can have ```
dsconfigad -show
tell me I am bound, but if the computer account has been deleted in AD, from the AD-side of things, -show will still tell me I am bound, though I will not be able to
id $adUserName
```

Does anybody have a better scripting process or workflow that gives me a truer idea of whether or not I am bound and fully functioning from a directory services standpoint to our AD?

19 REPLIES 19

mm2270
Legendary Contributor III

It sounds like you already have the solution with the items in your post. Do a check first with dsconfigad -show to determine if the domain is correct, then do a id -u $someaduser and if that fails, the Mac is not properly bound. You'd have to be sure you're checking an account that never gets deleted though.

This of course all assumes you are in scope of your DC or the id command will fail anyway, so you might want to first do a quick ping check against your AD before continuing.

Something along these lines should work, although it would need to be tested for failure cases.

#!/bin/sh

ping -c 3 -o adserver.org.your.domain 1> /dev/null 2> /dev/null
# If the ping was successful, we're in range of the DC
if [[ $? == 0 ]]; then
    # Check the domain returned with dsconfigad
    domain=$( dsconfigad -show | awk '/Active Directory Domain/{print $NF}' )
    # If the domain is correct
    if [[ "$domain" == "org.your.domain" ]]; then
        # Check the id of a user
        id -u adusername
        # If the check was successful...
        if [[ $? == 0 ]]; then
            echo "The Mac is bound to AD"
        else
            # If the check failed
            echo "The Mac is not bound to AD"
        fi
    else
        # If the domain returned did not match our expectations
        echo "The Mac is not bound to domain"
    fi
else
    # We can't see the DCs, so no way to properly check
    echo "Not in range of DC"
fi

krichterjr
Contributor
Contributor

I was just putting an Extension Attribute together as well. My only concern in my was what "id" I use. Obviously you want to use an "id" that you know isn't going to quite, get laid off, or deleted due to inactivity.

# Check to see if it is bound by running the "id" command.
AD=`id XXX`

# Tell us if it's Bound or not.
if [[ "$AD" = "no such user" ]];
then
      echo "Not Bound"
else
      echo "Bound"
fi

I'm open to any suggestions as well.

cbrewer
Valued Contributor II

You could search AD for the computer's own computer record. If it can't be found you know either the computer record doesn't exist or the computer isn't talking to AD.

Something along these lines...

ad_computer_name=`dsconfigad -show | grep "Computer Account" | awk '{print $4}'`
dscl /Search read /Computers/$ad_computer_name

mm2270
Legendary Contributor III

cbrewer's suggestion is good. Using the same logic as above, check to make sure you can talk to AD first, then if the Mac is claiming its bound to AD with dsconfigad -show and finally, if it can find its own record. That makes more sense that checking a user.

@krichterjr - I would use an account that you use for things like binding purposes, if you have something that is designed for that only and no-one logs into. I assume such an account probably wouldn't get deleted from AD, or you'd have other issues to worry about. :)

krichterjr
Contributor
Contributor

@cbrewer - That does make more sense to have the computer check for it's own record than a user. Thanks

pickerin
Contributor II

Yea, the problem is that the Macintosh will think it's bound to Active Directory, even if the Computer account is no longer accessible. There's discussion over in this thread: https://jamfnation.jamfsoftware.com/discussion.html?id=7004.

Over there, I posted my Extension Attribute that has worked well for us (and identified other Computer accounts that have failed, requiring us to rebind to AD):

#!/bin/bash

domain="YOURDOMAIN"
user="someuser"

# Can we query a UPN?
domainAns=`dscl /Active Directory/${domain}/All Domains -read /Users/${user} dsAttrTypeNative:userPrincipalName`
if [[ $domainAns =~ "is not valid" ]]; then
    result="Invalid"
else
        result="Valid"
fi

echo "<result>$result</result>"

Bukira
Contributor

https://github.com/jatoben/dsstatus

Nice like command app here that checks the directory status, combine this with a launchDaemon or agent or JSS policy to check is a client can access the AD / OD etc, you could then script it to restart the opendirectoryd or rebind via script if needed

rmanly
Contributor III

@mm2270 just two quick tips :)

In this bit here:

ping -c 3 -o adserver.org.your.domain 1> /dev/null 2> /dev/null
# If the ping was successful, we're in range of the DC
if [[ $? == 0 ]]; then

You can do the following to make things a bit cleaner.

  1. if you want to redirect both stdout and stderr to the same place you can use the '&>' operator. Like so:
ping -c 1 www.intel.com &> /dev/null
  1. You can also use the command directly on the if statement line. You don't need to seperate them out and check the exit status.
if ping -c 1 www.intel.com &> /dev/null; then
    echo 'yes'
else
    echo 'no'
fi

or

if grep -q "${input}" "${file}"; then

etc. etc.

Cheers,
Ryan

pickerin
Contributor II

dsstatus is essentially performing the same lookup as the ```
dscl /Active Directory/${domain}/All Domains -read /Users/${user} dsAttrTypeNative:userPrincipalName
``` command from above and dscl is already built into all of your systems, no need to compile a binary and distribute.

mm2270
Legendary Contributor III

@rmanly,

Yeah, I do know of the &> /dev/null approach, and usually use that. The above was pulled from a different project I was working on that actually required the separate stdout and stderr redirections. Too much to explain right now, but the usual approach wasn't giving me the results I wanted, so I had to do it that way. But you're right, under normal circumstances, that does work fine.

Thanks :)

Bukira
Contributor

re - pickerin,

That line only covers AD Binds only, however DSStatus binary can cover ALL directory bindings,

Plus not everyone wants to write long lines of code and troubleshoot them,

All i do is install dsstatus on my macs (part of the complied image), then create an extension attribute as follows

+**#!/bin/sh

/usr/sbin/dsstatus

if [ $? -eq 0 ]

then

echo "<result>Pass</result>"

else

echo "<result>Fail</result>"

fi**+

Thats its, jobs a gooden, much shorted code, much easier and quicker, and times money. I could then scope it to a smart group for "is" "Fail" and then bind or rebind etc.

If i need to check a mac i can do this in Casper Remote, with a single line

dsstatus -v

and get full read out of ALL directory statuses

So much easier for me and covers my Ad and my OD status

pickerin
Contributor II

@Bukira "write long lines of code and troubleshoot them", as opposed to downloading source, firing up XCode, and compiling the binary for 32-bit and 64-bit machines. Makes sense.

Yes, the dscl line is only for AD binds, of course that's what the OP asked for.

acdesigntech
Contributor II

use ldapsearch to do a dynamic lookup of the AD and grep for the computer account. Use a service account that has no password expiration. The beauty of ldapsearch is that you can bind to the domain on the fly to do the lookup, so it will work even from unbound Macs.

#!/bin/sh
ComputerName=`scutil --get ComputerName`
LDAPName="your.AD.service.account"
LDAPPasswd="service.account.password"
LDAPAD="domain.example.com"
SearchBase="DC=domain,DC=example,DC=com"
MacOU="ou=Macintosh,cn=Computers,dc=domain,dc=example,dc=com"
IsInAD=$(ldapsearch -LLL -h "$LDAPAD" -x -D "$LDAPName"@"$LDAPAD" -w "$LDAPPasswd" -b "$SearchBase" "name=$ComputerName" | grep name)

evarona
New Contributor II

@acdesigntech,
Is there a way to do the same but avoid caching the password and NOT prompting for one either? I just hate the idea of password in the clear.

acdesigntech
Contributor II

I would think you could store a hash of the password for the AD user in a text file in a secure location, and have the script read the hash and convert it on the fly.

Andy_McCaskill
Contributor
use ldapsearch to do a dynamic lookup of the AD and grep for the computer account. Use a service account that has no password expiration. The beauty of ldapsearch is that you can bind to the domain on the fly to do the lookup, so it will work even from unbound Macs. #!/bin/sh ComputerName=scutil --get ComputerName LDAPName="your.AD.service.account" LDAPPasswd="service.account.password" LDAPAD="domain.example.com" SearchBase="DC=domain,DC=example,DC=com" MacOU="ou=Macintosh,cn=Computers,dc=domain,dc=example,dc=com" IsInAD=$(ldapsearch -LLL -h "$LDAPAD" -x -D "$LDAPName"@"$LDAPAD" -w "$LDAPPasswd" -b "$SearchBase" "name=$ComputerName" | grep name)

Could I throw this into an Extension Attribute pulling the data as a string? This is something I would like to be updated on my machines to ensure ones that are on the domain get specific actions.

acdesigntech
Contributor II

you can, but the output of that command is either nothing or the computer name. If you wanted to put some more appropriate info, you can always do something like ```
if [ $? == 0 ]; then echo "<result>your.domain.name</result>"
else echo "<result>Not Bound</result>"
fi
```

or similar when defining the EA

nessts
Valued Contributor II

bound and actually functional are two different things, computers can report bound in @acdesigntech solution, but still not be able to authenticate via AD, if a computer is moved to an OU that is for "inactive" computers. most generally the best solution i have seen is to check for an account that should not be on the computer in a local mobile account, such as using my bind account and an id command.
if the id bindaccount command returns unknown user, then its likely lost its trust relationship in windows terms, or needs rebound regardless of what dsconfigad says the bound state is.

Andy_McCaskill
Contributor

Thanks acdesigntech for the input your addition to your original code works for my process.