We've been having an issue at our institution where our Macs have changed their Computer Name on their own (DNS Problems?). In an effort to stop this from happening because our printing solution relies on the AD name and Computer Name to match, I put together an on-going startup script to check the hostname against Active Directory and change it if needed. I also have the computer set a preference in a plist, then have an extended attribute check that. If it exists, then it joins a smart group and finally the JSS email me if a computer joins that group so I can follow up with computer.
I'd appreciate any feedback, comments or a better way to implement a solution. I got a little crazy with the "if-then" statements.
#!/bin/sh
############################################################################
## Script Name: check_hostname.sh
## Author: Steven Russell
############################################################################
## Variables:
## ________________________________________________________________________
## Active Directory Name
udn="ad_username_here"
password="ad_p@ssw0rd_here"
## Computers Hostname Set in System Preferences
host_name=`/bin/hostname -s | tr '[:lower:]' '[:upper:]'`
domain_name=".domain_name.org"
domain="domain_name.org"
## Company plist file to set preference
company_plist="/Library/Preferences/org.company.preferences.plist"
## Active Directory Name Stored in "ActiveDirectory.plist" Presumably same name in Directory Utility
ad_plist_name=`/usr/libexec/PlistBuddy -c "Print AD Computer ID" /Library/Preferences/DirectoryService/ActiveDirectory.plist | tr '[:lower:]' '[:upper:]'`
echo "[ check_hostname ] :: AD-Plist Name: $ad_plist_name"
## SCUTIL Name Lookups
scutil_name=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]'`
echo "[ check_hostname ] :: Computer Name: $scutil_name"
scutil_localname=`scutil --get LocalHostName | tr '[:lower:]' '[:upper:]'`
echo "[ check_hostname ] :: Local Host Name: $scutil_localname"
scutil_hostname=`scutil --get HostName | tr '[:lower:]' '[:upper:]' | sed 's/.$domain$//'`
echo "[ check_hostname ] :: Host Name: $scutil_hostname"
## Count the length of the Computer Name
## We have a standard 14 character length for our Computers
## But sometimes because of typos they are longer or shorter
name_length=`echo "$scutil_name" | tr -d '
' | wc -m | sed -e 's/^[ ]*//'`
echo "[ check_hostname ] :: Name Length: $name_length of 14"
## Check-File location
check_file="Library/Application Support/some_directory/Resources/check_hostname.txt"
## Create Query for AD
## Case for Length of Computer Name
## This will do a wildcard search in AD and try and use the last five
## characters because that is where our asset tag is
case $name_length in
10)
asset_tag1=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]' | cut -c1-2`
asset_tag2=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]' | cut -c5-9`
;;
11)
asset_tag1=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]' | cut -c1-2`
asset_tag2=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]' | cut -c6-10`
;;
12)
asset_tag1=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]' | cut -c1-2`
asset_tag2=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]' | cut -c7-11`
;;
13)
asset_tag1=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]' | cut -c1-2`
asset_tag2=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]' | cut -c8-12`
;;
14)
asset_tag1=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]' | cut -c1-2`
asset_tag2=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]' | cut -c9-13`
;;
15)
asset_tag1=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]' | cut -c1-2`
asset_tag2=`scutil --get ComputerName | tr '[:lower:]' '[:upper:]' | cut -c10-14`
;;
*)
echo "[ check_hostname ] :: Name Length is Invalid. Aborting Script..."
exit 1
esac
echo "[ check_hostname ] :: SearchQuery: $asset_tag1 * $asset_tag2*"
## Has this script run before?
if [ ! -f $checkfile ]; then
touch "$checkfile"
echo "[ check_hostname ] :: This is the first time this script is running on this computer. Setting Preferences..."
defaults write $company_plist OrphanedADObjects -bool false
defaults write $company_plist BindPending -bool false
defaults write $company_plist NameChanged -bool false
fi
## Query Active Directory
## ----------------------------------
ad_name=`ldapsearch -x -LLL -h $domain -D cn=$udn,cn=users,dc=put_domain_here,dc=org -w $password -b dc=put_domain_here,dc=org cn=$asset_tag1*$asset_tag2* | grep sAMAccountName | awk '{print$2}' | sed 's/[$]//g'| tr '[:lower:]' '[:upper:]'`
## Count how many results from AD
if [ "$ad_name" = "." -o "$ad_name" = "" -o "$ad_name" = " " ]; then
ad_name_count="0"
else
ad_name_count=`echo "$ad_name" | wc -l | sed -e 's/^[ ]*//'`
fi
echo "[ check_hostname ] :: # of AD Objects: $ad_name_count"
if [ "$ad_name_count" -eq "0" ]; then
## Lets try to Query AD with just the Asset Tag without the Location Code...
## The first two characters of our naming scheme specifies the location
ad_name=`ldapsearch -x -LLL -h $domain -D cn=$udn,cn=users,dc=put_domain_here,dc=org -w $password -b dc=put_domain_here,dc=org cn=*$asset_tag2* | grep sAMAccountName | awk '{print$2}' | sed 's/[$]//g'| tr '[:lower:]' '[:upper:]'`
ad_name_count=`echo "$ad_name" | wc -l | sed -e 's/^[ ]*//'`
echo "[ check_hostname ] :: # of AD Objects: $ad_name_count"
fi
echo "[ check_hostname ] :: AD Name: $ad_name"
if [ "$ad_name" = "." -o "$ad_name" = "" -o "$ad_name" = " " ]; then
echo "[ check_hostname ] :: ERROR: Cannot Find, or Read an Active Directory Name..."
echo "[ check_hostname ] :: Adding this computer to ' 2012-2013 Orphaned AD Objects' Group..."
defaults write $company_plist OrphanedADObjects -bool true
echo "[ check_hostname ] :: Forcing Computer to do inventory with JSS to update Smart Group and Preferences..."
jamf recon
echo "[ check_hostname ] :: Aborting Script..."
exit 1
fi
## Check plist for bind status...
bind_status=`defaults read $company_plist BindPending`
orphaned_status=`defaults read $company_plist OrphanedADObjects`
## Main:
## ________________________________________________________________________
## Test to see if Bind is pending...
if [ $bind_status = "1" ]; then
echo "[ check_hostname ] :: Resuming script after Reboot..."
echo "[ check_hostname ] :: Binding to Active Directory with current Computer Name: '$scutil_name'"
jamf policy -trigger SnowLeopardBind
echo "[ check_hostname ] :: Removing Preference for Reboot..."
defaults write $company_plist BindPending -bool false
echo "[ check_hostname ] :: Forcing Computer to do inventory with JSS to update Smart Group and Preferences..."
jamf recon
echo "[ check_hostname ] :: Binding complete, Script done!"
echo "[ check_hostname ] :: Rebooting..."
shutdown -r now
else
## Test to see if AD has multiple Results
if [ "$ad_name_count" -gt "1" -a "$orphaned_status" = "0" ]; then
echo "[ check_hostname ] :: There are more than one entry for this computer in Active Directory"
echo "[ check_hostname ] :: Adding this computer to ' 2012-2013 Orphaned AD Objects' Group..."
defaults write $company_plist OrphanedADObjects -bool true
sleep 2
echo "[ check_hostname ] :: Unbinding from Active Directory..."
echo "[ check_hostname ] :: Removing existing AD-Binding to '$scutil_name'"
dsconfigad -r -u "$udn" -p "$password"
echo "[ check_hostname ] :: Removing Search Path entries..."
dscl /Search -delete / CSPSearchPath /Active Directory/"$domain"
dscl /Search/Contacts -delete / CSPSearchPath /Active Directory/"$domain"
dscl /Search -delete / CSPSearchPath /Active Directory/"$domain"
dscl /Search -delete / CSPSearchPath "/Active Directory/All Domains"
dscl /Search/Contacts -delete / CSPSearchPath "/Active Directory/All Domains"
dscl /Search -delete / CSPSearchPath "/Active Directory/All Domains"
sleep 5
echo "[ check_hostname ] :: Setting Preference to Bind on next Reboot..."
defaults write $company_plist BindPending -bool true
sleep 2
echo "[ check_hostname ] :: Rebooting..."
shutdown -r now
else
## If the Name in the AD Plugin DOESN'T match the AD name AND the AD Name Count (number of matches in AD) is "1" then...
## If there were more than one match from AD, we wouldn't want the script trying to rename the computer with multiple names, that could get ugly.
if [ "$ad_plist_name" != "$ad_name" -a "$ad_name_count" -eq "1" ];then
echo "[ check_hostname ] :: The AD PLIST Name of: $ad_plist_name DOESN'T match the AD Name of: $ad_name"
/usr/libexec/PlistBuddy -c "Set AD Computer ID $ad_name" /Library/Preferences/DirectoryService/ActiveDirectory.plist
echo "[ check_hostname ] :: Fixed!"
echo "[ check_hostname ] :: Adding Computer to ' 2012-2013 Named Changed'"
defaults write $company_plist NameChanged -bool true
echo "[ check_hostname ] :: Forcing Computer to do inventory with JSS to update Smart Group and Preferences..."
jamf recon
else
## Additional Checks for the name on the Mac to match Active Directory
if [ "$scutil_name" != "$ad_name" -o "$scutil_localname" != "$ad_name" -o "$scutil_hostname" != "$ad_name" -a "$ad_name_count" -eq "1" ]; then
echo "[ check_hostname ] :: We have to fix the Computer Name to Match AD Name of: $ad_name"
scutil --set ComputerName $ad_name
scutil --set LocalHostName $ad_name
scutil --set HostName $ad_name$domain_name
echo "[ check_hostname ] :: Adding Computer to ' 2012-2013 Named Changed'"
defaults write $company_plist NameChanged -bool true
echo "[ check_hostname ] :: Forcing Computer to do inventory with JSS to update Smart Group and Preferences..."
jamf recon
else
if [ "$ad_name_count" -eq "1" -a "$orphaned_status" = "1" ]; then
echo "[ check_hostname ] :: Only one AD Object in AD; Clearing Orphaned Objects Group"
defaults write $company_plist OrphanedADObjects -bool false
echo "[ check_hostname ] :: Forcing Computer to do inventory with JSS to update Smart Group and Preferences..."
jamf recon
echo "[ check_hostname ] :: Names Match. Exiting..."
else
echo "[ check_hostname ] :: Names Match. Exiting..."
fi
fi
fi
fi
fi