Posted on 07-21-2015 12:43 PM
Can Casper Suite determine what ActiveDirectory OU a Mac computer is located in?
I can't think of a native OS X command line tool that can do this.
Tools such as dsconfigad, dscl, etc are unable to glean this info to my satisfaction. dscl and Directory Utility.app can see the OrganizationalUnit (top-level), but it can't drill too deep, regardless of authentication/rights.
Please enlighten me if there is an efficient (scriptable) way for a Mac to "see" what computer OU it is located in.
Can the Casper Suite tell what computer OU a specific Mac computer is located in?
Posted on 07-21-2015 12:51 PM
You would have to use ldapsearch:
ldapsearch -LLL -h domain.company.com -x -D domain.user@domain.company.com -w "domainuserpassword" -b "DC=Domain,DC=company,DC=com" "name=thecomputername"
In the output the dn: / distinguishedName attribute would be what OU the machine is in.
The only problem with this is in the output if your OU path is extremely long ldapsearch splits it onto two lines making it hard to grep.
Posted on 07-21-2015 12:56 PM
If the Mac is properly joined to and can talk to AD its possible to pull it.
#!/bin/sh
CompName=$(dsconfigad -show | awk '/Computer Account/{print $NF}' | sed 's/$$//')
## Replace "DOMAIN" in the command below with your actual domain name
OU=$(dscl "/Active Directory/DOMAIN/All Domains" read /Computers/${CompName}$ dsAttrTypeNative:distinguishedName | tail -1 | awk -F"${CompName}," '{print $2}')
echo "$OU"
The above should print something along the lines of:
OU=Computers,OU=Macs,DC=com,DC=inc,DC=acme
Posted on 07-21-2015 12:56 PM
Looks like its stored in the computer record in the distinguishedName
attribute so you could read it with:
dscl /Active Directory/SHORTDOMAINNAME/All Domains -read /Computers/computername distinguishedName
Posted on 07-21-2015 12:59 PM
We have had luck with dscl to get this info. Our extension attribute looks like this:
#!/bin/bash
computerID=$(hostname)
computerOU=$(dscl /Search read /Computers/$computerID dsAttrTypeNative:distinguishedName 2> /dev/null | sed -e 's/dsAttrTypeNative:distinguishedName://g' | tr -d "
" | cut -d "," -f 2-)
echo "<result>$computerOU</result>"
This gives us just the OU as a result.
Posted on 07-21-2015 01:19 PM
Maybe I have it all wrong, but is @chriscollins ldapsearch function asking Active Directory which OU has this hostname. Where as, the other two commands are asking the Mac OS what it believes is the OU it is in.
When things go bad, aren't there times the Mac thinks it is in one OU and AD has a record in another OU? Would it be better practice to report both and compare?
Posted on 07-21-2015 01:42 PM
Great info everyone, thanks. I always appreciate how fast people share info and ideas here.
Got my wheels turning now.
Each dept here at my company has a computer OU dedicated to each specific department. Very clean. Great for policies, reporting, security etc.
I'm asking this question because I'd like to force my IT team into move our newly-deployed Macs into a computer OU that matches the department that the Mac belongs to. This is how we deploy Windows PCs and I want to be consistent with both platforms in terms of OU location.
On the Windows side (using GPO), any Windows PC in the default "Computers" OU automatically gets denied certain functionality/access because the computer is in an unsupported OU. Thus, our IT team MUST move a new PC into the correct AD computer OU at deployment time. Standard operating procedure. But my IT team forgets the Macs in the "Computers" ghetto, because there is no consequence for leaving them in "Computers".
If my Macs or Casper can logically determine where the Macs are located (or better yet - where they are NOT located - in this case the generic MS "Computers" OU) then I can act on this information in some useful way. What that is exactly I don't know yet. Regardless, this will encourage the IT team to start putting our new Macs into the correct dept OUs like we do on the Windows side.
Posted on 07-21-2015 01:56 PM
@jhalvorson when you use dscl /Active Directory that is similar to a ldapsearch, its reading directly from AD in the OS X way.
Posted on 07-21-2015 02:14 PM
@jhalvorson I use dscl all the time for user/group information, but wasn't able to drill into the specific Computer OUs that I was looking for. I need to examine this again.
Posted on 07-21-2015 02:24 PM
Exactly what @nessts said. The dscl commands above are pulling the OU info directly from AD. The main difference between ldapsearch and dscl are that ldapsearch is doing an 'on the fly' bind, so it's possible to use it to do lookups against AD on a Mac that isn't actually bound to your AD environment. That can be useful in some cases.
Otherwise they should pull the same information.
Posted on 07-21-2015 02:30 PM
An issue with ldapsearch is the need to supply the password with the command. It can be hard to protect the password in that case.
Posted on 07-21-2015 04:28 PM
@dstranathan this is what I use if you're interested:
#!/bin/sh
ad_computer_name=`dsconfigad -show | grep "Computer Account" | awk '{print $4}'`
ad_computer_ou=`dscl /Search read /Computers/$ad_computer_name |
grep -A 1 dsAttrTypeNative:distinguishedName |
cut -d, -f2- | sed -n 's/OU=//gp' |
sed -n 's/(.*),DC=/1./gp' |
sed -n 's/DC=//gp' |
awk -F, '{
N = NF
while ( N > 1 )
{
printf "%s/",$N
N--
}
printf "%s",$1
}'`
echo "<result>$ad_computer_ou</result>"
the result looks like this: domain.com/Workstations/USA/Building/Macs
Posted on 07-22-2015 12:14 PM
@ jhalvorson: Good point. On occasion I find Macs that computer name and the hostname differ. Usually a human error on IT's fault.
@ jhbush1973 and @mm2270 uses the AD computer record name rather that computer name/hostname, which prevents avoids this problem.
@ jhbush1973 and @ dbrodjieski Any reason you are marking-up (tagging) the final output with <result> tags? Is this a JAMF thing? (I don't have Casper purchased yet...)
Posted on 07-22-2015 01:14 PM
@dstranathan
When script code echoes back items contained in <result> </result>
tags, this is for an Extension Attribute. Extension Attributes (otherwise known as "EAs") are custom database fields that can be added to the JSS to help capture any extra information outside of the normal inventory the JSS collects. Usually these 'EAs' are in the form of scripts that run on the Macs at the time of inventory collection, grab some pieces of data and then echo back the data contained in those tags. The jamf binary captures the contents between the tags and enters that into the computer record back on the JSS within the custom database field.
So for example, you can create an EA that would be called "Computer OU" and have the script run on each Mac at inventory collection and then be able to see their AD OU inside each respective Mac's record.
Hope that makes sense.
Posted on 07-23-2015 12:43 AM
If you have clients sometimes being off the corporate network (i.e. laptops), you might also think about additionally caching the result of the dscl call and using the previously cached response if the AD cannot be reached. This prevents empty EA results when a recon is done off-network.
This might not be strictly necessary for the OU record (haven't tested), but we use the same method to pull different AD attributes into the JSS inventory.
Here is an EA script doing a cached dscl lookup for OU. Well, it really should be pythonized, but for now it works.
#!/bin/bash
# adapted from "AD Attribute generic", returns CN without the first part ("CN=computername,")
# which translate to the OU of the record
# Set this to the desired AD Attribute
dsclAttribute="dsAttrTypeNative:distinguishedName"
dsclAttributeCacheName="dsAttrTypeNative:distinguishedName_OU"
# Nothing to change below
prefCacheDomain="local.extensionattribute.ds"
dsclValue=""
# we need our own computername for the OD search
# so parse response of dsconfigad -show
computerAccount=$( /usr/sbin/dsconfigad -show 2>/dev/null |
/usr/bin/sed -ne 's|^Computer Account.*= (.*)$|1|p' )
if [[ ${computerAccount} == "" ]]; then
# no computer account found
dsclValue=""
else
# look up value for computer account
dsclResponse=$(/usr/bin/dscl -q -plist "/Search" -read "/Computers/${computerAccount}" "${dsclAttribute}" 2>/dev/null)
dsclRC=$?
if [[ $dsclRC -ne 0 ]]; then
# query failed, so get cached value
dsclValue=$( defaults read "${prefCacheDomain}" "${dsclAttributeCacheName}" )
else
# query succeeded, cut away the part up to the first comma from DN (should be the OU)
dsclValue=$( echo "${dsclResponse}" |
/usr/bin/grep -v '^<!DOCTYPE.*>$' |
/usr/bin/xpath "//key[.="${dsclAttribute}"]/following-sibling::*[1]/string/text()" 2>/dev/null |
/usr/bin/cut -d "," -f 2-100
)
# cache current value
defaults write "${prefCacheDomain}" "${dsclAttributeCacheName}" -string "${dsclValue}"
fi
fi
echo "<result>${dsclValue}</result>"
exit 0
Posted on 07-23-2015 05:23 AM
Here is the function that I created to do AD lookups more easily. This function also includes parsing to resolve the line-split issue that @chriscollins mentioned:
#!/bin/bash
# Function for querying LDAP
# Parameters:
# (1) Filter to be used for querying LDAP.
# (2) Optional - Value(s) that you are searching for within LDAP.
# (3) Optional - User to perform query with
# (4) Optional - Password for user to perform query with
ldapQuery() {
# Set default return code to 0 (Success)
local iRet=0
# If an LDAP user's credentials were passed to the function, use them to query LDAP. Otherwise, use a service account
[ -n "$3" -a -n "$4" ] && { ldapUser="domain\${3}"; ldapPass="$4"; } || { ldapUser='domainuser'; ldapPass='password'; }
# Set environment-specific variables
domain="domain.company.com"; searchBase="dc=domain,dc=company,dc=com"
# Set path to temp file and create it
tmpFile=`mktemp -t ''`
# If a return value was specified
if [ -n "$2" ]; then
# Query LDAP and get desired value
result=`ldapsearch -xLLL -H "ldap://$domain" -b "$searchBase" -D "$ldapUser" -w "$ldapPass" "$1" "$2" 2> $tmpFile | perl -p00e 's/
?
//g' | grep -v -e '#' -e '^$' | grep -i "$2" | awk -F ': ' '{ print $2 }'`
# If the value is an integer that is 18 characters in length, convert to UNIX timestamp in "seconds" format
aNumber='^[0-9]+$'; [[ $result =~ $aNumber ]] && [ ${#result} -eq 18 ] && result=`expr ${result} / 10000000 - 11644473600`
# If a return value was not specified
else
# Query LDAP and get all values
result=`ldapsearch -xLLL -H "ldap://$domain" -b "$searchBase" -D "$ldapUser" -w "$ldapPass" "$1" 2> $tmpFile | perl -p00e 's/
?
//g' | grep -v -e '#' -e '^$'`
fi
# If credentials are invalid, set return code to 1 (Failure). Otherwise, use 0 (Success)
[ "$(cat $tmpFile | grep 'Invalid credentials')" != "" ] && iRet=1
# Remove temp file
rm -f "$tmpFile"
# Output result and send back return code
echo "$result"; return $iRet
}
To get the full path to the OU which the computer resides in, I use the following command:
ldapQuery "cn=$computerName" dn | sed -E -e 's/.*CN=.{0,15},//g'
Hopefully this helps!
Posted on 07-28-2016 09:02 PM
if the machine is non ADbind, is there a way to leverage LDAP which is being used to enroll machine in Jamf