Name Changes

steve_summers
Contributor III

I'm sure I'm not alone in this situation, so bear with me as I walk through the scenario:

-Mac user gets married (typically) and changes their last name
-Our process to change the computer name and user home directory is all manual, involving logging the end user out and logging in as our "Administrator" account to complete the process
-This has to change

I've scripted the computer name change part, but have no idea if it's possible to change the name of someone's home directory without logging them out first. So, good people of Jamf Nation, does anyone has a script or automated process whereby everything is changed automatically? Hard to believe no one would. Would be nice if this could be done via Self Service, run a policy, reboot, boom, log in w/your new name. I appreciate any guidance.

9 REPLIES 9

joshuasee
Contributor III

Is there a reason to change the name of the home directory or the username of the account? Scripts generally should ask the computer what the path to home is rather than trying to divine it themselves. What about just changing the long name and using dscl to append the RecordName so both old and new username will work until the computer gets replaced?

Changing the home directory path while the user is logged in strikes me as a bad idea on many levels, though I don't think dscl will stop you from doing it.

steve_summers
Contributor III

@joshuasee , why change the home directory, well that's a good question. It's in the documentation, for now, so it's what the desk side people do. I suppose they believe it's just good practice to change it so it reflects the users proper name, or new name I should say.

I'm not familiar with changing the long name, as you mentioned. Would you know where I can find more info on that? That could be a workaround.

ryan_ball
Valued Contributor

This script will rename a chosen account to the current sAMAccountName in AD based on the GUID of the account which will always match AD. It assumes the following:
1. You have CocoaDialog installed here (can be changed): "/Applications/Utilities/CocoaDialog.app/Contents/MacOS/CocoaDialog"
2. The account is an AD account
3. You pass in credentials of a user with read privileges in AD
4. You search through it and replace "your_corp" with your own information (be sure to update the ldapsearch domain and search base)

#!/bin/bash
# written by Ryan Ball

CocoaDialog="/Applications/Utilities/CocoaDialog.app/Contents/MacOS/CocoaDialog"
userArray=($(dscl . -list /Users UniqueID | awk '{if ($2 > 1000) print $1}'))
osMinorVersion=$(sw_vers -productVersion | awk -F. '{print $2}')
stop="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns"
inform="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/ToolbarInfo.icns"
log="/Library/Logs/your_corp/Rename_Account.log"
scriptName=$(basename "$0")

#Check to see if parameters are passed from the JSS, set variables as the parameter passed
[[ -n "$4" ]] && ldapUser=$4
[[ -n "$5" ]] && ldapPass=$5

function writelog () {
    DATE=$(date +%Y-%m-%d %H:%M:%S)
    /bin/echo "${1}"
    /bin/echo "$DATE" " $1" >> "$log"
}

function finish () {
    writelog "======== Finished $scriptName ========"
    exit "$1"
}

function parse_guid () {
    writelog "Parsing user GUID..."
    user_gid=$(dscl localhost -read "/Search/Users/$1" | grep GeneratedUID | awk 'NR==1 {print; exit}' | cut -c15-)

    octet1=$(perl -le 'print reverse(shift =~ /../g)' -- "$(echo "$user_gid" | cut -f1 -d-)")
    octet2=$(perl -le 'print reverse(shift =~ /../g)' -- "$(echo "$user_gid" | cut -f2 -d-)")
    octet3=$(perl -le 'print reverse(shift =~ /../g)' -- "$(echo "$user_gid" | cut -f3 -d-)")
    octet4=$(echo "$user_gid" | cut -f4 -d-)
    octet5=$(echo "$user_gid" | cut -f5 -d-)

    readableGUID=$(echo "$octet1$octet2$octet3$octet4$octet5" | fold -w2 | paste -sd'\' - | sed 's/^/\/')
}

function home_check () {
    if [[ -f "/Users/$1" ]]; then
        writelog "A home folder already exists at /Users/$1. If you don't need /Users/$1 then please delete it in the Finder first, then run this script again."
        $CocoaDialog ok-msgbox --title "Account Rename Tool v1.1" --text "Error" --informative-text "A home folder already exists at /Users/$1.\nIf you don't need /Users/$1 then please delete it in the Finder first,\nthen run this script again." --height "128" --width "425" --icon-file $stop --string-output --no-cancel &> /dev/null
        finish 1
    fi
}

function delete_account () {
    #Determine location of the users home folder
    writelog "Beginning rename process for $1..."
    userHome=$(/usr/bin/dscl . read "/Users/$1" NFSHomeDirectory | cut -c 19-)

    # Delete the user from all groups it is a member of
    writelog "Removing $1 from all groups..."
    groupList="$(/usr/bin/id -Gn "$1")"
    if [[ $? -eq 0 ]] && [[ -n "$(/usr/bin/dscl . -search /Groups GroupMembership "$1")" ]]; then 
        for group in $groupList; do
            /usr/bin/dscl . -delete "/Groups/${group}" GroupMembership "$1" >&/dev/null
        done
    fi

    # Delete the primary group
    if [[ -n "$(/usr/bin/dscl . -search /Groups name "$1")" ]]; then
        /usr/sbin/dseditgroup -o delete "$1"
    fi

    # Remove password hash if it exists
    guid="$(/usr/bin/dscl . -read "/Users/$1" GeneratedUID | /usr/bin/awk '{print $NF;}')"
    if [[ -f "/private/var/db/shadow/hash/$guid" ]]; then
        /bin/rm -f "/private/var/db/shadow/hash/$guid"
    fi

    # Delete the user
    /bin/mv "$userHome" "/Users/old_$1"
    /usr/bin/dscl . -delete "/Users/$1"

    # Refresh Directory Services
    [[ "$osMinorVersion" -le 6 ]] && /usr/bin/killall DirectoryService
    [[ "$osMinorVersion" -ge 7 ]] && /usr/bin/killall opendirectoryd
}

function create_new_account () {
    # Wait until we can ID the user
    writelog "Waiting for the user's account to be available to this machine via AD lookups..."
    while [[ "$(/usr/bin/id "$2" 2&> /dev/null; echo $?)" -ne "0" ]]; do
        sleep 2
    done

    # Check if there's a home folder there already, if there is, exit before we wipe it
    /bin/mv "/Users/old_$1" "/Users/$2"
    /usr/sbin/chown -R "$2" "/Users/$2"
    writelog "Home for $2 now located at /Users/$2."
    if [[ "$osMinorVersion" -lt "13" ]]; then
        /System/Library/CoreServices/ManagedClient.app/Contents/Resources/createmobileaccount -n "$2" -v | while read -r LINE; do writelog "$LINE"; done;
    else
        /System/Library/CoreServices/ManagedClient.app/Contents/Resources/createmobileaccount -n "$2" -D -v | while read -r LINE; do writelog "$LINE"; done;
    fi
    writelog "Account for $2 has been created on this computer."
    accountCreated=$($CocoaDialog msgbox --title "Account Rename Tool v1.1" --text "Complete!" --informative-text "Account for $2 has been created on this computer." --height "128" --width "425" --icon-file $inform --string-output --button1 "OK" --button3 "Details" --string-output 2> /dev/null)
    buttonClicked=$(echo "$accountCreated" | awk 'NR==1{print}')
    if [[ $buttonClicked = "Details" ]]; then
        $CocoaDialog textbox --title "Account Rename Tool v1.1" --informative-text $log --text-from-file $log --height "425" --width "425"  --button1 "OK" --string-output 2> /dev/null &
    fi
    finish 0
}

/bin/mkdir -p /Library/Logs/your_corp

writelog " "
writelog "======== Starting $scriptName ========"

# Prompt user to select a username from list of AD accounts
while [[ -z "$oldUser" ]]; do
    accountSelect=$( $CocoaDialog standard-dropdown --title "Account Rename Tool v1.1" --text "Select the account to rename." --height "128" --width "375" --string-output --items "" "${userArray[@]}" 2> /dev/null)
    buttonClicked=$(echo "$accountSelect" | awk 'NR==1{print}')
    oldUser=$(echo "$accountSelect" | awk 'NR>1{print}')
    [[ $buttonClicked = "Cancel" ]] && writelog "User canceled; exiting." && finish 0
    writelog "$oldUser was selected from dropdown..."
done

# Get the user's GUID into a format readable by ldapsearch
parse_guid "$oldUser"

# Look in AD for the user's GUID and see if the samAccountName matches the local account
newUser=$(ldapsearch -LLL -h your_corp.com -x -D "$ldapUser" -w "$ldapPass" -b "dc=your_corp,dc=com" "(objectGUID=$readableGUID)" sAMAccountName | grep sAMAccountName | awk '{print$2}' | tr '[:upper:]' '[:lower:]')
if [[ -n "$newUser" ]]; then
    if [[ "$oldUser" != "$newUser" ]]; then
        writelog "$newUser has a matching GUID in Active Directory."

        finalQuestion=$($CocoaDialog msgbox --title "Account Rename Tool v1.1" --text "AD Account Found!" --informative-text "$oldUser will now become $newUser." --icon-file $inform --height "128" --width "425" --button1 "Rename" --button2 "Cancel" --string-output 2> /dev/null)
        finalAnswer=$(echo "$finalQuestion" | awk 'NR==1{print}')
        if [[ $finalAnswer = "Cancel" ]]; then
            writelog "User canceled; exiting."
            finish 0
        else
            home_check "$newUser"
            delete_account "$oldUser"
            create_new_account "$oldUser" "$newUser"
        fi
    else
        writelog "$oldUser's local account name matches their account in Active Directory; exiting."
        $CocoaDialog ok-msgbox --title "Account Rename Tool v1.1" --text "Error" --informative-text "$oldUser's local account name matches their account in Active Directory; nothing to change." --height "128" --width "425" --icon-file $stop --string-output --no-cancel 2&> /dev/null
        finish 1
    fi
else
    writelog "Cannot find an account with matching GUID in Active Directory; exiting."
    $CocoaDialog ok-msgbox --title "Account Rename Tool v1.1" --text "Error" --informative-text "Cannot find an account with matching GUID in Active Directory; cannot continue." --height "128" --width "425" --icon-file $stop --string-output --no-cancel 2&> /dev/null
    finish 1
fi

finish 0

steve_summers
Contributor III

@ryan.ball , thanks a bunch! So, question: does the machine have to be AD joined, or just an account has to exist in AD? (Which we have..)

Thanks.

ryan_ball
Valued Contributor

@steve.summers It uses ldapsearch for the AD lookup, so it does not have to be AD joined. As long as it is a mobile account, it should probably work fine.

Create a test account, log them in on a device, then logout of the device. Then rename the the first/last name field in AD as well as the two fields in the account tab of AD. Then try to login to the device, it should fail. Then login with a local account or different account, run the policy in SS to rename, choose the account you want to rename, then it should do it. Then logout, and try to log back in with the newly renamed account.

joshuasee
Contributor III

I think ryan.ball's answer is more what you're looking for, but to change the long user name:

dscl . -create /Users/${oldShortUserName} RealName "${newLongUserName}"

and to alias a new username to an old one:

dscl . -merge /Users/${oldShortUserName} RecordName ${newShortUserName}

steve_summers
Contributor III

Thanks @joshuasee and @ryan.ball

steve_summers
Contributor III

Hey @ryan.ball , question for you. I see in the script that you have Cocoa Dialog. I'm not able to find a download for it. What could I do differently in that area for this script? Thanks.

mm2270
Legendary Contributor III

@steve.summers You could swap out the cocoadialog stuff for JamfHelper calls, since that is installed on a Jamf managed device by default. It doesn't offer quite as much control over how the dialogs appear, but it should do the trick.

For example with this line:

$CocoaDialog ok-msgbox --title "Account Rename Tool v1.1" --text "Error" --informative-text "A home folder already exists at /Users/$1.\nIf you don't need /Users/$1 then please delete it in the Finder first,\nthen run this script again." --height "128" --width "425" --icon-file $stop --string-output --no-cancel &> /dev/null

You could change that to:

$JamfHelper -windowType utility -title "Account Rename Tool v1.1" -heading "Error" -description "A home folder already exists at /Users/$1.\nIf you don't need /Users/$1 then please delete it in the Finder first,\nthen run this script again." -icon $stop -button1 "OK" -defaultButton 1 &> /dev/null

You will need to define the $JamfHelper variable up near the top of the script, like follows:

JamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"

Just repeat the same swap of code for any other cocoaDialog calls, but keep in mind JamfHelper and cocoadialog have different result outputs for which button is clicked, so you will need to make further adjustments on the lines where it detects which button was clicked by the user. So sections like this as an example, will need to be reworked:

accountCreated=$($CocoaDialog msgbox --title "Account Rename Tool v1.1" --text "Complete!" --informative-text "Account for $2 has been created on this computer." --height "128" --width "425" --icon-file $inform --string-output --button1 "OK" --button3 "Details" --string-output 2> /dev/null)
buttonClicked=$(echo "$accountCreated" | awk 'NR==1{print}')
if [[ $buttonClicked = "Details" ]]; then
    $CocoaDialog textbox --title "Account Rename Tool v1.1" --informative-text $log --text-from-file $log --height "425" --width "425"  --button1 "OK" --string-output 2> /dev/null &
fi