Migrate Local User to Domain Account
If you are migrating your machines to authenticate via Active Directory, you may need to convert your local user accounts and their home folders to an AD user account and retain the home folder. I had a script posted here but that version was Tiger only because it used NI* commands.
The following script is written in bash and can be run by double clicking (it’s a .command) as a user with sudo rights (all admin users have this right by default). It will prompt for your admin password, then present a list of numbered local users. Enter a number from the list for the user you want to migrate, then it will ask for the network ID. It runs in a loop until you select the option for “Finished” which will exit the script.
This should work with Tiger, Leopard or Snow Leopard and can easily be modified to work with OD user accounts (change the check4AD variable to check for OD instead).
To download the script, click here.
#!/bin/sh Version=1.0 # Modified 1/14/2009 # MigrateLocalUserToDomainAcct.command # Patrick Gallagher # http://macadmincorner.com # # This script should not need any modification in most enviornments. # If the script does not execute when run, you may need to 'chmod +x /path/to/thisScript' to make it executable clear netIDprompt="Please enter the network ID for this user: " listUsers="$(/usr/bin/dscl . list /Users | grep -v eccsadmin | grep -v _ | grep -v root | grep -v uucp | grep -v amavisd | grep -v nobody | grep -v messagebus | grep -v daemon | grep -v www | grep -v Guest | grep -v xgrid | grep -v windowserver | grep -v unknown | grep -v unknown | grep -v tokend | grep -v sshd | grep -v securityagent | grep -v mailman | grep -v mysql | grep -v postfix | grep -v qtss | grep -v jabber | grep -v cyrusimap | grep -v clamav | grep -v appserver | grep -v appowner) FINISHED" FullScriptName=`basename "$0"` ShowVersion="$FullScriptName $Version" check4AD=`/usr/bin/dscl localhost -list . | grep "Active Directory"` osversionlong=`sw_vers -productVersion` osvers=${osversionlong:3:1} echo "********* Running $FullScriptName Version $Version *********" # If the machine is not bound to AD, then there's no purpose going any further. if [ "${check4AD}" != "Active Directory" ]; then echo "This machine is not bound to Active Directory.\nPlease bind to AD first. "; exit 1 fi RunAsRoot() { ## Pass in the full path to the executable as $1 if [[ "${USER}" != "root" ]] ; then echo echo "*** This application must be run as root. Please authenticate below. ***" echo sudo "${1}" && exit 0 fi } RunAsRoot "${0}" until [ "$user" == "FINISHED" ]; do printf "%b" "\a\n\nSelect a user to convert or select FINISHED:\n" >&2 select user in $listUsers; do if [ "$user" = "FINISHED" ]; then echo "Finshied converting users to AD" break elif [ -n "$user" ]; then if [ `who | grep console | awk '{print $1}'` == "$user" ]; then echo "This user is logged in.\nPlease log this user out and log in as another admin" exit 1 fi # Determine location of the users home folder userHome=`/usr/bin/dscl . read /Users/$user NFSHomeDirectory | cut -c 19-` # Get list of groups echo "Checking group memberships for local user $user" lgroups="$(/usr/bin/id -Gn $user)" if [[ $? -eq 0 ]] && [[ -n "$(/usr/bin/dscl . -search /Groups GroupMembership "$user")" ]]; then # Delete user from each group it is a member of for lg in $lgroups; do /usr/bin/dscl . -delete /Groups/${lg} GroupMembership $user >&/dev/null done fi # Delete the primary group if [[ -n "$(/usr/bin/dscl . -search /Groups name "$user")" ]]; then /usr/sbin/dseditgroup -o delete "$user" fi # Get the users guid and set it as a var guid="$(/usr/bin/dscl . -read "/Users/$user" 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 /usr/bin/dscl . -delete "/Users/$user" # Verify NetID printf "\e[1m$netIDprompt" read netname /usr/bin/killall DirectoryService sleep 10 /usr/bin/id $netname # Check if there's a home folder there already, if there is, exit before we wipe it if [ -f /Users/$netname ]; then echo "Oops, theres a home folder there already for $netname.\nIf you don't want that one, delete it in the Finder first,\nthen run this script again." exit 1 else /bin/mv $userHome /Users/$netname /usr/sbin/chown -R ${netname} /Users/$netname echo "Home for $netname now located at /Users/$netname" fi break else echo "Invalid selection!" fi done done
Related posts:
- Bind to OD Script & Add to Computer Group
- Add user to admin group with Applescript
- Can Open Directory be used enterprise wide?
- Add directory services data to LANDesk inventory
- Updated: Send softwareupdate command through ARD
- Script to Configure the Mac OS X firewall
If you enjoyed this post, please consider to leave a comment or subscribe to the feed and get future articles delivered to your feed reader.




This is a fantastic script that really helps me wrap my head around the problem. I will definitely be using this resource in the future. Thank you for sharing!
Will your script migrate user accounts from one domain to another? Just replaced an existing Exchange 2003 server with an Exchange 2010 solution, but the domain and DNS servers are different…
This was the solution we found for migrating each account in Windows:
http://my.galagzee.com/2007/08/05/transparent-windows-workstation-domain-migration/
It would be nice if Snow Leopard had an easy solution for this as well…
Very cool concept, just some thoughts from having run the script on a few system; you might want to add a failsafe on line 74, to confirm that the group getting deleted does not match the “admin” group name (or any other local group name), ie. if the local account’s short name is “admin”, thus causing the “admin” group to be deleted (which removes local admin access for all local and domain accounts). Also, once a local account is selected, shouldn’t the script wait to delete the local account until a domain user is entered (in case you want to close the script and not continue with the migration)? Thanks,
How do I change this for Open Directory? I see the Check4AD variable but Im not sure what I have to change it to?
also do I run this script on the server or local machines?
Thanks
Dave
@David
You can either change the Check4AD variable to look for OD (grep for “LDAPv3″ instead of “Active Directory”) and change the section towards the top of the script (after the variables) to verify the machine is bound to OD, or you can remove that section all together and just tell your techs to make sure the machine is bound before hand.
Otherwise, I *think* the script should run as-is on an OD system. Been a while since I wrote it but glancing over it I don’t see anything that is AD specific other than that 1 section I mentioned.
I stumbled on your script today. Just what I need to help move 50+ computers with local accounts to Open Directory. One little problem so far. Our account names are in the format first_last. grep -v _ skips those accounts. Changing to grep -v ^_ will skip accounts where the first character is an underscore but include accounts with an underscore that’s not the first character.
I also noticed that grep -v unknown is listed twice. Typo?
@Mike
Without the “grep -v _” you will need a different way to to exclude those accounts. Either list each service account to be excluded or limit the scope of users based on the UID. For instance, a small script I just started using in LANrev to list local users:
for i in `dscl . list /users | grep -v _ | grep -v nobody | grep -v daemon | grep -v Guest | grep -v root`;
do uniqueID=`dscl . read /users/$i UniqueID | awk ‘{print $2}’`
if [ $uniqueID -lt 600 ] && [ $uniqueID -ge 499 ]; then
echo $i
fi
done
You could incorporate this into the above script. I don’t have time to sort test it out now.
And yes, having unknown twice is a typo.
Good job, Patrick – this should come in handy at my day gig. Much appreciated for posting.
When the script asks for network ID what do I have to put?
I tried the network account name, and the UID which was 1032, none of these worked???
Thanks
Dave
@David
You use the shortname of the network account. You are bound to AD, correct? The script verifies this by running “id username”. Try that command to verify that machine can lookup that account in AD.
Patrick thank for you fast reply, yes I am bound to OD
I have changed variable as you described above and it prompts me for the admin password.
It then presents the list of users followed by Network ID.
I tried the network shortname, does the network account need to have a home directory already?
Also does this copy all the home folder contents to the server so the user is able to log in from any machine?
@Dave
I think this script is not exactly what you are looking for. This is not to convert a local home to a network home. It also has nothing to do with OD. This is to convert a local account to an AD account with mobile home directory.
This script works great until I’m on a 10.4 computer using a firstname.lastname login convention. 10.4 command line doesn’t handle the . well. I find that I’ve had to manually do the mv and chown but escape the . with a \. Is there a way to get your script to escape the . when used in a username? In the end I use sudo mv oldname firstname\.lastname then sudo chown -R firstname\.lastname:staff firstname\.lastname.
@Richard
Have you tried using quotes instead? It would be easier to quote the shortname.
Hi Patrick, awesome job on this by the way. Just one problem, I keep ending up with the original user folder now IN my network user folder, alongside the network profile folders.
Any idea what is up?
Thanks!
@Beatlemike
This wasn’t intended for use with network homes. If you are running this on your home directory server, you will have to modify it with the correct path to your home folders. This script assumes /Users
No, sorry …. no I worded it wrong it is a mobile home…. the osx kind, not the kind in a trailer park.
@Beatlemike
Do you have user folders in a non-standard location? Are you using Centrify or Likewise?
No it is a standard setup, but it just recreates the entire local home inside the mobile home. instead of replacing the contents
Do I maybe add the AD user AFTER i migrate the user folder? I thought I tried that and it stopped me from creating a user, but I might be wrong.