Home > Active Directory, Mac administration, macOS, Scripting > Updated MigrateADMobileAccounttoLocalAccount script now available to fix migration bug

Updated MigrateADMobileAccounttoLocalAccount script now available to fix migration bug

A couple of years back, I wrote a script to assist with migrating AD mobile users to local users. In my testing in 2016, everything seemed to work right and I didn’t see any problems with it on OS X El Capitan.

Fast forward a couple of years and a colleague of mine, Per Oloffson, began running into a weird problem with upgrading Macs from Sierra to High Sierra. When he upgraded Macs from macOS Sierra to macOS High Sierra, he was finding that Macs that had been migrated from AD mobile accounts to local accounts were having those same accounts break.

https://twitter.com/MagerValp/status/1007664677386637312

After a considerable amount of troubleshooting, he was able to narrow it down to the macOS High Sierra installer changing the password hash on those accounts. But why was it changing them?

https://twitter.com/MagerValp/status/1007664753362243585

In short, it was changing them because of a bug in my original MigrateADMobileAccounttoLocalAccount.command interactive migration script. Sorry, Per. For more details, please see below the jump.

The problematic sections are highlighted below. When the script backed up the AD mobile account’s password and then restored it, it was adding single quotes to the beginning and end of the password hash string.

Screen Shot 2018 06 15 at 7 32 06 PM

The password hash string should have looked like this:

Screenshot 2018 06 15 13 31 17

Instead, it looked like this:

Screenshot 2018 06 15 13 31 18

The odd part of the situation is that macOS Sierra was seemingly OK with the extra characters in the password string. It wasn’t until the macOS High Sierra installer re-checked and altered the account plist that the problem occurred.

To fix the migration process, I’ve updated the script to better handle the account password backup and restoration process. The backup process is now querying dscl for the correct XML output and restoring it, which should address the problem with the script.

Screen Shot 2018 06 15 at 7 55 24 PM

In my testing, the password hash is now appearing correctly in the account’s plist file.

Screen Shot 2018 06 15 at 8 23 03 PM

Testing

This script has been tested and verified to migrate AD mobile accounts to local accounts on the following versions of macOS:

  • macOS 10.13.5

In that testing, I did the following:

Testing on logged-in AD mobile user account:

  1. I set up an AD-bound VM and created an AD mobile account with admin privileges.
  2. I logged into the AD mobile account and ran the script while logged in as that account.
  3. Once the account had been migrated, I rebooted and verified that I could log in at the OS login window.
  4. I changed the password for the local account to a new one and rebooted.
  5. I verified that I could log in at the OS login window with the new password.

Testing on logged-out AD mobile user account:

  1. I set up an AD-bound VM and created an AD mobile account with admin privileges.
  2. I logged into the VM using a local account which was not the AD mobile account and ran the script while logged in as that account.
  3. Once the account had been migrated, I logged out and verified that I could log in at the OS login window with the just-migrated account.
  4. I changed the password for the newly-migrated local account to a new one and rebooted.
  5. I verified that I could log in at the OS login window with the new password.

Note: I did not test with FileVault-enabled accounts.

Advisory: Older versions of OS X and macOS were not tested and I have no idea if the script will work on those older OS versions.

Warning: I was able to test in my shop’s AD environment and verified that everything worked. That does not guarantee it will work in your environment. Test thoroughly before deploying in your own AD environment.

The updated script is available below, and also available on GitHub at the following address:

https://github.com/rtrouton/rtrouton_scripts/tree/master/rtrouton_scripts/migrate_ad_mobile_account_to_local_account


#!/bin/bash
# Modified 4/5/2019
Version=1.4
# Original source is from MigrateUserHomeToDomainAcct.sh
# Written by Patrick Gallagher – https://twitter.com/patgmac
#
# Guidance and inspiration from Lisa Davies:
# http://lisacherie.com/?p=239
#
# Modified by Rich Trouton
#
# Version 1.0 – Migrates an Active Directory mobile account to a local account by the following process:
# 1. Detect if the Mac is bound to AD and offer to unbind the Mac from AD if desired
# 2. Display a list of the accounts with a UID greater than 1000
# 3. Remove the following attributes from the specified account:
#
# cached_groups
# cached_auth_policy
# CopyTimestamp – This attribute is used by the OS to determine if the account is a mobile account
# SMBPrimaryGroupSID
# OriginalAuthenticationAuthority
# OriginalNodeName
# SMBSID
# SMBScriptPath
# SMBPasswordLastSet
# SMBGroupRID
# PrimaryNTDomain
# AppleMetaRecordName
# MCXSettings
# MCXFlags
#
# 4. Selectively modify the account's AuthenticationAuthority attribute to remove AD-specific attributes.
# 5. Restart the directory services process
# 6. Check to see if the conversion process succeeded by checking the OriginalNodeName attribute for the value "Active Directory"
# 7. If the conversion process succeeded, update the permissions on the account's home folder.
# 8. Prompt if admin rights should be granted for the specified account
#
# Version 1.1
#
# Changes:
#
# 1. After conversion, the specified account is added to the staff group. All local accounts on this Mac are members of the staff group,
# but AD mobile accounts are not members of the staff group.
# 2. The "accounttype" variable is now checking the AuthenticationAuthority attribute instead of the OriginalNodeName attribute.
# The reason for Change 2's attributes change is that the AuthenticationAuthority attribute will exist following the conversion
# process while the OriginalNodeName attribute may not.
#
#
# Version 1.2
#
# Changes:
#
# Add RemoveAD function to handle the following tasks:
#
# 1. Force unbind the Mac from Active Directory
# 2. Deletes the Active Directory domain from the custom /Search and /Search/Contacts paths
# 3. Changes the /Search and /Search/Contacts path type from Custom to Automatic
#
# Thanks to Rick Lemmon for the suggested changes to the AD unbind process.
#
# Version 1.3
#
# Changes:
#
# Fix to account password backup and restore process. Previous versions
# of the script were adding extra quote marks to the account's plist
# file located in /var/db/dslocal/nodes/Default/users/.
#
# Version 1.4
#
# Changes:
#
# macOS 10.14.4 will remove the the actual ShadowHashData key immediately
# if the AuthenticationAuthority array value which references the ShadowHash
# is removed from the AuthenticationAuthority array. To address this, the
# existing AuthenticationAuthority array will be modified to remove the Kerberos
# and LocalCachedUser user values.
#
# Thanks to the anonymous reporter who provided the bug report and fix.
clear
listUsers="$(/usr/bin/dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}') FINISHED"
FullScriptName=`basename "$0"`
ShowVersion="$FullScriptName $Version"
check4AD=`/usr/bin/dscl localhost -list . | grep "Active Directory"`
osvers=$(sw_vers -productVersion | awk -F. '{print $2}')
/bin/echo "********* Running $FullScriptName Version $Version *********"
RunAsRoot()
{
## Pass in the full path to the executable as $1
if [[ "${USER}" != "root" ]] ; then
/bin/echo
/bin/echo "*** This application must be run as root. Please authenticate below. ***"
/bin/echo
sudo "${1}" && exit 0
fi
}
RemoveAD(){
# This function force-unbinds the Mac from the existing Active Directory domain
# and updates the search path settings to remove references to Active Directory
searchPath=`/usr/bin/dscl /Search -read . CSPSearchPath | grep Active\ Directory | sed 's/^ //'`
# Force unbind from Active Directory
/usr/sbin/dsconfigad -remove -force -u none -p none
# Deletes the Active Directory domain from the custom /Search
# and /Search/Contacts paths
/usr/bin/dscl /Search/Contacts -delete . CSPSearchPath "$searchPath"
/usr/bin/dscl /Search -delete . CSPSearchPath "$searchPath"
# Changes the /Search and /Search/Contacts path type from Custom to Automatic
/usr/bin/dscl /Search -change . SearchPolicy dsAttrTypeStandard:CSPSearchPath dsAttrTypeStandard:NSPSearchPath
/usr/bin/dscl /Search/Contacts -change . SearchPolicy dsAttrTypeStandard:CSPSearchPath dsAttrTypeStandard:NSPSearchPath
}
PasswordMigration(){
# macOS 10.14.4 will remove the the actual ShadowHashData key immediately
# if the AuthenticationAuthority array value which references the ShadowHash
# is removed from the AuthenticationAuthority array. To address this, the
# existing AuthenticationAuthority array will be modified to remove the Kerberos
# and LocalCachedUser user values.
AuthenticationAuthority=$(/usr/bin/dscl -plist . -read /Users/$netname AuthenticationAuthority)
Kerberosv5=$(echo "${AuthenticationAuthority}" | xmllint –xpath 'string(//string[contains(text(),"Kerberosv5")])')
LocalCachedUser=$(echo "${AuthenticationAuthority}" | xmllint –xpath 'string(//string[contains(text(),"LocalCachedUser")])')
# Remove Kerberosv5 and LocalCachedUser
if [[ ! -z "${Kerberosv5}" ]]; then
/usr/bin/dscl -plist . -delete /Users/$netname AuthenticationAuthority "${Kerberosv5}"
fi
if [[ ! -z "${LocalCachedUser}" ]]; then
/usr/bin/dscl -plist . -delete /Users/$netname AuthenticationAuthority "${LocalCachedUser}"
fi
}
RunAsRoot "${0}"
# Check for AD binding and offer to unbind if found.
if [[ "${check4AD}" = "Active Directory" ]]; then
/usr/bin/printf "This machine is bound to Active Directory.\nDo you want to unbind this Mac from AD?\n"
select yn in "Yes" "No"; do
case $yn in
Yes) RemoveAD; /bin/echo "AD binding has been removed."; break;;
No) /bin/echo "Active Directory binding is still active."; break;;
esac
done
fi
until [ "$user" == "FINISHED" ]; do
/usr/bin/printf "%b" "\a\n\nSelect a user to convert or select FINISHED:\n" >&2
select netname in $listUsers; do
if [ "$netname" = "FINISHED" ]; then
/bin/echo "Finished converting users to local accounts"
exit 0
fi
accounttype=`/usr/bin/dscl . -read /Users/"$netname" AuthenticationAuthority | head -2 | awk -F'/' '{print $2}' | tr -d '\n'`
if [[ "$accounttype" = "Active Directory" ]]; then
mobileusercheck=`/usr/bin/dscl . -read /Users/"$netname" AuthenticationAuthority | head -2 | awk -F'/' '{print $1}' | tr -d '\n' | sed 's/^[^:]*: //' | sed s/\;/""/g`
if [[ "$mobileusercheck" = "LocalCachedUser" ]]; then
/usr/bin/printf "$netname has an AD mobile account.\nConverting to a local account with the same username and UID.\n"
else
/usr/bin/printf "The $netname account is not a AD mobile account\n"
break
fi
else
/usr/bin/printf "The $netname account is not a AD mobile account\n"
break
fi
# Remove the account attributes that identify it as an Active Directory mobile account
/usr/bin/dscl . -delete /users/$netname cached_groups
/usr/bin/dscl . -delete /users/$netname cached_auth_policy
/usr/bin/dscl . -delete /users/$netname CopyTimestamp
/usr/bin/dscl . -delete /users/$netname AltSecurityIdentities
/usr/bin/dscl . -delete /users/$netname SMBPrimaryGroupSID
/usr/bin/dscl . -delete /users/$netname OriginalAuthenticationAuthority
/usr/bin/dscl . -delete /users/$netname OriginalNodeName
/usr/bin/dscl . -delete /users/$netname SMBSID
/usr/bin/dscl . -delete /users/$netname SMBScriptPath
/usr/bin/dscl . -delete /users/$netname SMBPasswordLastSet
/usr/bin/dscl . -delete /users/$netname SMBGroupRID
/usr/bin/dscl . -delete /users/$netname PrimaryNTDomain
/usr/bin/dscl . -delete /users/$netname AppleMetaRecordName
/usr/bin/dscl . -delete /users/$netname PrimaryNTDomain
/usr/bin/dscl . -delete /users/$netname MCXSettings
/usr/bin/dscl . -delete /users/$netname MCXFlags
# Migrate password and remove AD-related attributes
PasswordMigration
# Refresh Directory Services
if [[ ${osvers} -ge 7 ]]; then
/usr/bin/killall opendirectoryd
else
/usr/bin/killall DirectoryService
fi
sleep 20
accounttype=`/usr/bin/dscl . -read /Users/"$netname" AuthenticationAuthority | head -2 | awk -F'/' '{print $2}' | tr -d '\n'`
if [[ "$accounttype" = "Active Directory" ]]; then
/usr/bin/printf "Something went wrong with the conversion process.\nThe $netname account is still an AD mobile account.\n"
exit 1
else
/usr/bin/printf "Conversion process was successful.\nThe $netname account is now a local account.\n"
fi
homedir=`/usr/bin/dscl . -read /Users/"$netname" NFSHomeDirectory | awk '{print $2}'`
if [[ "$homedir" != "" ]]; then
/bin/echo "Home directory location: $homedir"
/bin/echo "Updating home folder permissions for the $netname account"
/usr/sbin/chown -R "$netname" "$homedir"
fi
# Add user to the staff group on the Mac
/bin/echo "Adding $netname to the staff group on this Mac."
/usr/sbin/dseditgroup -o edit -a "$netname" -t user staff
/bin/echo "Displaying user and group information for the $netname account"
/usr/bin/id $netname
# Prompt to see if the local account should be give admin rights.
/bin/echo "Do you want to give the $netname account admin rights?"
select yn in "Yes" "No"; do
case $yn in
Yes) /usr/sbin/dseditgroup -o edit -a "$netname" -t user admin; /bin/echo "Admin rights given to this account"; break;;
No ) /bin/echo "No admin rights given"; break;;
esac
done
break
done
done

view raw

gistfile1.txt

hosted with ❤ by GitHub

  1. Matt
    June 18, 2018 at 6:59 pm

    Is there a way to fix this issue on an affected machine post migration?

  2. June 18, 2018 at 8:33 pm

    because a new user is being created, I assume an assigned secureToken is lost in the process? Is there anyway to transfer that as part of the process? Otherwise FV2 users will lose Decryption capabilities.

    • June 18, 2018 at 8:47 pm

      Oh, I see… the AuthenticationAuthority Key contains the “;SecureToken;”. So when you delete the key, you lose the token.

      Still this now renders FV2 user incapable of decryption in 10.13.

      • June 18, 2018 at 10:59 pm

        So I’ve solved my issue… I am not sure why this works, and every ounce of me tells me that it shouldn’t. But I’ve tested and retested 10 times and it works every time.

        I added:

        /usr/bin/dscl . -create /users/$netname AuthenticationAuthority SecureToken

        Below:

        /usr/bin/dscl . -create /users/$netname AuthenticationAuthority “${shadowhash}”

        And now FV2 is not broken for me in 10.13 when I run the script. I can reboot and login to FV2 immediately after running it. I verified that the account is fully migrated from mobile to local as well.

      • June 18, 2018 at 11:30 pm

        Ok so some weirdness… it does not have to be SecureToken, any string will work. I just tried it with TEST…

        I also just realized that at some point in me messing with it I rolled back to the previous:

        shadowhash=$(/usr/bin/dscl . -read /Users/$user AuthenticationAuthority | grep ” ;ShadowHash;HASHLIST:<")

        and

        /usr/bin/dscl . -create /users/$user AuthenticationAuthority \'$shadowhash\'

        So what I am assuming is happening is that $shadowhash is also grabbing ;SecureToken; as well as the password hash. An echo of $shadowhash shows as much.

        For whatever reason however, that doesn't leave me at a status where I can authenticate to FV2 in 10.13.5 on reboot. But if I append a:

        /usr/bin/dscl . -create /users/$netname AuthenticationAuthority AnyString

        afterwards… I can. I have no idea WHY this is working for me… but it is.

  3. lisacherie
    June 20, 2018 at 4:12 pm

    Using the original perl version of the script with 10.13.4, in very minimal testing, does not appear to have problems unlocking file vault, logging in, or changing passwords (and then unlocking FV after the password change) following an account conversion. More extensive testing is required to investigate what Rich has found for the original perl version.
    Note the perl version is using a string within a string for the “system” function to execute the dscl command rather than as a command in bash.

  4. Matt
    July 9, 2018 at 8:55 pm

    In using your updated script, we have seen a few cases where the user is no longer able to log in. This is without a High Sierra Upgrade. When I attempt to reset their password from the command line I get the following:
    DS Error: -14165 (eDSAuthPasswordQualityCheckFailed)

    I have tried a few different passwords as well as verified that the machine is unbound and is now a local account.

    Any insight on a resolution?

  5. JoeLee
    January 11, 2019 at 1:53 pm

    Has anyone tried using this script with Mojave?

    • January 23, 2019 at 3:49 am

      @JoeLee, I just did this evening. It seems to work just fine.

      • Abraham Tarverdi
        May 21, 2019 at 2:11 pm

        @joelee was your Mac enrolled in an MDM? This script seems to cause an issue for user certs and systems that are enrolled in mdm running 10.13.3 or higher. The cert profile gets removed and can’t be redeployed until you re-enroll the system with the user’s approval.

  6. May 6, 2019 at 1:23 pm

    Is there a chance we could have this script in a non interactive mode?
    So it can be run from Self Service on the currently logged in user.
    1. Unbind from AD (if binded) the currently logged in user.
    2. Convert from Mobile to Local the currently logged in user.
    3. Make the currently logged in user admin.

    Thank you for your time and consideration.

  1. No trackbacks yet.

Leave a reply to hkabik Cancel reply