Migrate Local User to Domain Account

by on January 14, 2010 » Add more comments.

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

Find more like this: AD Integration, Directory Services, Mac, Scripting , , , , , ,


62 Responses to Migrate Local User to Domain Account

  • Wyatt says:

    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!

  • Guy says:

    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…

  • rmmiles says:

    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,

  • David says:

    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

  • Patrick says:

    @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.

  • Mike says:

    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?

  • Patrick says:

    @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.

  • mcmacguy says:

    Good job, Patrick – this should come in handy at my day gig. Much appreciated for posting.

  • David says:

    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

  • Patrick says:

    @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.

  • Dave says:

    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?

  • Patrick says:

    @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.

  • Richard says:

    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.

  • Patrick says:

    @Richard

    Have you tried using quotes instead? It would be easier to quote the shortname.

  • Beatlemike says:

    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!

  • Patrick says:

    @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

  • Beatlemike says:

    No, sorry …. no I worded it wrong it is a mobile home…. the osx kind, not the kind in a trailer park.

  • Patrick says:

    @Beatlemike
    Do you have user folders in a non-standard location? Are you using Centrify or Likewise?

  • Beatlemike says:

    No it is a standard setup, but it just recreates the entire local home inside the mobile home. instead of replacing the contents

  • Beatlemike says:

    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.

  • This is exactly what I am looking for, thanks for the post. One question, and pardon my ignorance, but the network ID, is that just the username? or domain\username? or username@domain? does it need to include the root? .com or .org??? Thanks in advance for the help! I have tried several ways but it never works, have to login as root and rename and create the account back otherwise its just in limbo.

  • Patrick says:

    @Jacob
    Network ID is just the user name. Machine has to be bound to AD for this to work.

  • figured that was the case, but just so happens our local account has the same names… firstinitiallastname. If nothing has changed the local account gets moved, but when you login with the network it sets up a new account.

    I have also tried changing the account as well as logging in with root and changing the user folder, then tried to migrate to no avail, any advice is greatly appreciated.

  • Pingback: Another day in the life of a lonely Mac | Charles L'Abri Anderson

  • Kevin Wilson says:

    I have two issues I need help on, please:

    The domain I’m migrating Mac’s into uses usernames of firstname.lastname. I created an account on the mac for the new domain login, using “domainname\username” as the account name, and “firstnamelastname” (with no period since it’s not allowed) for the short name, using the same pw as the domain account.

    For example using myself, my domain login kevin.wilson got an account created on the mac of kevinwilson (shortname). The existing local account on the mac was just “kevin”.

    I ran the script, telling it to move “kevin” to “kevinwilson”. It ended up putting the “kevin” profile as a subfolder in the “kevinwilson” account.

    Do I have to change the domain logins to not use a period? Or is there another way to do it?

    Also, is there a way to reverse the changes so I can do it correctly without fighting with permissions on every file in every profile?

    Your help is much appreciated.

    Kevin

  • Peter Trondsen says:

    Very cool script.
    Thank You for posting it.
    I am looking to do something similar, and I have been picking through your script.
    Basically, my users are moving to another domain, and once they are moved, I need to delete the reference to the AD User to allow them to login in the new domain,
    This command works for this:
    dscl . -delete /Users/$user

    But some Macs have multiple users, so I am looking for a way to delete all the AD Users, and not the Local Admin, and root, and the other invisible users.
    I’m going to keep at it, any suggestions, would be great.

    Thanks,
    Pete

  • Peter Trondsen says:

    Okay, here it is, just needed some small mods…..thanks again:

    #!/bin/sh
    Version=1.0
    # Modified 04/20/2010

    # DeleteLocalUserAcct.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

    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 delete FINISHED:\n” >&2
    select user in $listUsers; do

    if [ “$user” = “FINISHED” ]; then
    echo “Finshied!”
    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
    #u serHome=`/usr/bin/dscl . read /Users/$user NFSHomeDirectory | cut -c 19-`

    # Delete the user
    /usr/bin/dscl . -delete “/Users/$user”

    fi
    done
    done

  • Harry says:

    I am pretty new to Mac and Apple world. I would like to use Windows AD to authenticate the Mac users who all have windows ad account. Is this requred system admin’s involvement? In other word, can I use this script to do it by myself? Thanks

  • Peter Trondsen says:

    I have updated the deleteUser script, which worked great for my migration.
    Many thanks again to Patrick for the original script. The following Script will prompt to delete the local User folder, not the User Account. The User will be able to login, retaining all data.

    #!/bin/sh
    Version=1.0
    # Modified 04/20/2010

    # DeleteLocalUserAcct.command
    # Peter Trondsen (Modified Patrick Gallagher’s script)

    clear

    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 | grep -v ladmin) 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}”

        printf “%b” “\a\n\nSelect a user to delete:\n” >&2
        select user in $listUsers; do

    if [ “$user” = “FINISHED” ]; then
    echo “Finished Deleting Users…”
    exit 1

        fi        
    # Delete the user
                /usr/bin/dscl . -delete “/Users/$user”        
                
                done
                done

  • Andrew says:

    One quick question, I’m the only user on the machine, can I convert my account to a domain account while I’m logged in, or should a create a second user to do this?

    Thanks

    Andrew.

  • Patrick says:

    @Andrew

    I would run this from another account.

  • Dan Chan says:

    Patrick, thanks for writing the script. It works great! but has anyone ever ask you, instead of just moving the local user home directory to the domain user home directory\LocalUserName to just replace the existing folders for the domain user home directory?

    for example: after the migration is completed; The results are as followed;
    DomainUser\LocalUserAccount\Desktop
    DomainUser\LocalUserAccount\Documents
    DomainUser\LocalUserAccount\Library
    DomainUser\LocalUserAccount\Music

    Instead, can we output it to replace the existing folders and files for the Domain Account? For example
    DomainUser\Desktop
    DomainUser\Documents
    DomainUser\Library
    DomainUser\Music

    ? Thanks!!

  • Patrick says:

    @Dan

    I’m not sure how you got that result. The intended result is domainuser\folders.

  • Dan Chan says:

    Hi Patrick,

    Thanks for getting back to me so quickly. I tried the script a few times now; and every time its finishes, the end result would just move the local account into a folder inside the domain user mobile account. i.e DomainAccount\UsersLocal\* instead of \DomainAccount\*

    Do you have to create the Domain Mobile Account first, before running the script?

    Thanks man!

  • Patrick says:

    No, I don’t create the mobile account first. The script will fail if there’s a home folder there already. I’ll look at this a bit more when I have some time.

  • Dan Chan says:

    Thanks Man! I greatly appreciate. If you don’t create the mobile account first, it will create the correct \DomainAccount\* but when you try to login with the domain account afterwards, it fails to login with the domain home directory.

    And if you create the mobile account before running the script, it moves everything from your local account to \DomainAccount\LocalAccount\* where it would be awesome if it would just replace all the existing in the \DomainAccount\Folders.

    Thanks again and take your time.

  • Sampath Ganji says:

    Would this process work with Lion as well without any modifications?

  • Patrick says:

    @Sampath

    I don’t know, I haven’t had time to try it yet.

  • Steven Hanlon says:

    @Sampath and @Patrick

    I can confirm this script does work on Lion 10.7.1

    Since the URL to download the script was broken, I opened up terminal, typed nano, and pasted in the code, saved it as scriptname.command.

    The first time I ran it, it did not work because my mac wasn’t able to login to network accounts for some reason, so when I moved it, I couldn’t login. I was able to resolve this by setting the Sharing name, hostname, and computerID in the AD directory utility to the same name, and rebound to the domain.

    I did not login to the network account before running this script as well.

    I tested this with Parallels 7 using a Lion virtual machine, clean install, and I restored all my data from my macbook pro to the virtual machine to simulate this properly. Upon logging into my network account, I was greeted with my same background, dock icons, and of course my files. The script ran very fast, as I think it only moves the data, does not copy it. My host OS uses an Apple SSD as well.

  • Patrick says:

    File links fixed, sorry about that.

    @Steven. Yes, this only mv’s the data so it should be very quick.

    All, I have not tested this on Lion yet as I can not get AD auth work at all in Lion. We’ve had to use local accounts on any MBA’s we buy for now.

  • Steven Hanlon says:

    @Patrick

    Is there any way to make this script do the reverse effect? Move a network account to a local account?

  • Steven Hanlon says:

    Also, here is my terminal output for running this on Lion.

    ********* Running LocalUsertoDomainUserMigration.command Version 1.0 *********

    Select a user to convert or select FINISHED:
    1) intrust
    2) steve
    3) FINISHED
    #? 2
    Checking group memberships for local user steve
    Please enter the network ID for this user: hanlonsm
    No matching processes were found
    uid=1972190779(hanlonsm) gid=2004300143(INTERNAL\Domain Users) groups=2004300143(INTERNAL\Domain Users),404(com.apple.sharepoint.group.3),745431359(INTERNAL\NAS_Users),402(com.apple.sharepoint.group.1),1272231317(INTERNAL\TS Users),403(com.apple.sharepoint.group.2),12(everyone),62(netaccounts),1345651367(INTERNAL\Service Staff),1642528183(INTERNAL\Public Folder Users),1691986051(INTERNAL\CERTSVC_DCOM_ACCESS),406(com.apple.sharepoint.group.5)
    Home for hanlonsm now located at /Users/hanlonsm

    Select a user to convert or select FINISHED:
    1) intrust
    2) steve
    3) FINISHED
    #?

  • Patrick says:

    @Steven
    Yes, the script could be modified to convert network user to local. You would need to add the commands needed to create a local user.

    That output looks normal, except for the “no matching processes were found” but that would be because Apple no longer has the “DirectoryService” process. I only kill that to force it to re-read it’s config.

  • josh says:

    This script works perfectly! Thank You.

  • Craig Richardson says:

    Ok with Lion you just need to replace the killing of DirectoryService with the following and it should work fine.

    if [ “$osvers” -gt “6” ]; then
    /usr/bin/killall opendirectoryd
    else
    /usr/bin/killall DirectoryService
    fi

  • Craig Richardson says:

    I’ve also found I need to add a 20 second sleep after the killing of opendirectoryd to allow launchd to restart it properly.

  • Pierre says:

    Hi,

    We migrated 21 computers using this script. All but four worked perfectly. One had the user folder on a different partition, didn´t check that first 😉 That messed things up a bit, spent an hour getting it right after that. It was also a and old silver macbook (2gb ram running lion) so every step took ages to finish.

    We now have issues on three computers. The rainbow wheel often appears and everything is sluggish. I have, of course run Disk permissions repair several times but the problems are still there. I have also noticed that the new AD-user doesn’t always have the correct permissions to some applications.

    Are these problems unique to these computers or has anyone else gotten these issues? The main issues here are the sluggishness and rainbow wheel. If You know the cause, please respond.

    P.S Nothing interesting appears in logs.

    Cheers

  • Dustin Day says:

    This looks like a great script, however I had a power failure when I was at the portio0n of the script that called for which user to select. Now the network account is still the same as it was (by default) but the local user account and all of its data has disappeared. What should I do?

  • Patrick says:

    @Dustin.
    The home folder is never deleted. Only renamed. Look in your /Users folder, you should still see a folder named after the local user. You should be able to fix it manually by running:
    sudo mv /Users/localhomefolder /Users/networkuser
    sudo chown -R networkuser /Users/networkuser

  • Dustin Day says:

    @Patrick

    The only folder that I see is the new network account and the guest folder. I have tried to use Disk Drill to recover these files before you replied to my question. The results that came up from that utility were all temp files, I still can’t find the user’s documents, email files, etc.

    Any help is greatly appreciated!

    Thanks Again Patrick

Leave a Reply

Your email address will not be published. Required fields are marked *