Migration from old AD Domain to new Domain

catfeetstop
Contributor II

Hi everyone! I hope you're well.

I'm trying to write an AppleScript for migrating our users from one domain to another. I'm running into a problem though. I think it has to do with the space in our domain user group. Here's the part of my script that's failing:

display dialog "Enter the username(Short Name) you want to migrate" default answer ""
set shortName to text returned of result
do shell script "dscl . delete /Users/" & shortName & "" with administrator privileges
do shell script "chown -R " & shortName & ":DOMAIN\Domain Users /Users/" & shortName & "" with administrator privileges

display dialog "Don't forget to move the computer to the right OU"

It's the part with DOMAINDomain Users. I've tried doing DOMAINDomain Users but I keep getting errors on that.

1 ACCEPTED SOLUTION

bentoms
Release Candidate Programs Tester

Try setting a variable to DomainDomain Users & then in the Apple script call it as quoted form of.

View solution in original post

18 REPLIES 18

catfeetstop
Contributor II

What's weird is that when I type the command out in Terminal it works fine.

spotter
New Contributor III

how are you running the script? Casper Remote, Self Service... etc.?

Just remember if running the applescript with either it will be ran as root which maybe the issue...

catfeetstop
Contributor II

It's an AppleScript Application that I'm running manually from the local admin account on the client computer.

bentoms
Release Candidate Programs Tester

Try setting a variable to DomainDomain Users & then in the Apple script call it as quoted form of.

stevewood
Honored Contributor II
Honored Contributor II

Any reason you need to use AppleScript? I'm in the process of handling the same thing, a move from an old AD to a new one, and I'm doing this via BASH and Self Service. This is the script I'm using for it:

#!/bin/sh

# Name:  moveDomains.sh
# Date:  28 May 2014 v1.0
# Updated: 18 Jun 2014 v1.1
# Author:  Steve Wood (swood@integer.com)
# Purpose:  used to move users from one AD domain to a new one, or from OD to AD
# Updates:  v1.1 - set GroupID to static
#   - v1.1 - including message to users using jamfhelper
#           - adding code to check for domain membership
# Prequisites:  You'll need a policy in your JSS 

# Globals & Logging 
LOGPATH='<WHERE YOU STORE LOGS>'  # change this line to point to your local logging directory
if [[ ! -d "$LOGPATH" ]]; then
    mkdir $LOGPATH
fi
set -xv; exec 1> $LOGPATH/movedomainslog.txt 2>&1  # you can name the log file what you want
version=1.1
oldAD='<OLD AD DOMAIN>'
currentAD=`dsconfigad -show | grep -i "active directory domain" | awk '{ print $5 }'`

# let the user know what we are doing

banner=`/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType hud -title "Moving Domains" -heading "Moving Domains Header" -description "We are moving your user account to the new authentication domain.  When we are completed, and your computer restarts, you will be able to login to your computer with your new domain credentials." -icon <PATH-TO-YOUR-ICON> -button1 "Proceed" -button2 "Not Now" -defaultButton 1 -cancelButton 2 -timeout 60 -countdown` 

if [[ $banner == "2" ]]; then

    echo "User canceled the move."
    exit 1

fi

# Grab current user name
loggedInUser=`/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }'`

##### if you do not have OD deployed you can remove the follwing lines
# unbind from LDAP
# sinc there is no easy way to determine if bound to OD, we will just run against our OD for good measure

dsconfigldap -r <YOUR OD DOMAIN>
#####

# unbind from AD
# check to see if we are bound to our current AD or not.  If not we can skip this

if [[ "$currentAD" = "$oldAD" ]]; then

    # remove the config for our old AD
    # you need a user in your AD that has the rights to remove computers from the domain

    dsconfigad -remove $oldAD -user <networkuser> -pass <networkuserpass>   

fi

# remove the local user from the machine so we get the proper UID assigned in dscl
dscl . delete /Users/$loggedInUser

# bind to new AD
# using a JAMF policy to bind to the new AD

jamf policy -id <policynumber> # can also use a custom trigger for the policy

# reset permissions
### some of the code below is courtesy of Ben Toms (@macmule)

###
# Get the Active Directory Node Name
###
adNodeName=`dscl /Search read /Groups/Domain Users | awk '/^AppleMetaNodeLocation:/,/^AppleMetaRecordName:/' | head -2 | tail -1 | cut -c 2-`

###
# Get the Domain Users groups Numeric ID
###

domainUsersPrimaryGroupID=`dscl /Search read /Groups/Domain Users | grep PrimaryGroupID | awk '{ print $2}'`

accountUniqueID=`dscl "$adNodeName" -read /Users/$loggedInUser 2>/dev/null | grep UniqueID | awk '{ print $2}'`


chown -R $accountUniqueID:$domainUsersPrimaryGroupID /Users/$loggedInUser

# restart the computer when done
# to make sure everything is working properly we will restart

shutdown -r now

The only hang up I have right now is FileVault. If a machine has an encrypted drive, deleting the user via dscl from the local machine removes them from FV. So I'm working on a way to add the new user to FV via a LaunchDaemon, but my testing is inconclusive right now.

donmontalvo
Esteemed Contributor III

catfeetstop
Contributor II

@bentoms That's a great suggestion, I'll give that a try. Thanks!

@stevewood There's actually more going on in the script, including computer renaming and running jamf policies. I'm using AppleScript so that I can prompt the technician for input. It let's them manually type in the computer name and the user they want to migrate.

@donmontalvo Thanks for that link. I've been looking at thread a lot in this process.

stevewood
Honored Contributor II
Honored Contributor II

@catfeetstop You could just hardcode the GID in instead of using the DOMAINGROUP NAME format.

And as far as grabbing input, you can use CocoaDialog for that in a shell script. Just sayin. :-)

catfeetstop
Contributor II

@stevewood Ah, very interesting. CocoaDialog sounds very interesting, I'm no scripting pro so I'll look into that. Thank you!

bentoms
Release Candidate Programs Tester

@stevewood, no need for another tool.

You can do this with AppleScript & not need to install anything. :)

catfeetstop
Contributor II

So @bentoms, I did this:

set domainGroup to "DOMAIN\Domain Users"

It still doesn't work, the same thing happens. In fact, I have to do exactly "DOMAINDomain Users" otherwise it won't compile. If I do "DOMAINDomain Users" it doesn't compile. I get a syntax error, Expected "" but found unknown token. I'm sure there's something really simple I'm missing.

tlarkin
Honored Contributor

Hi Everyone,

I have done this several times, for several different customers. The most typical scenario was that company A purchased/acquired company B, and I had to migrate all the Macs to company A's DC, and rename user accounts accordingly. In every scenario I was a contractor/consultant, so I had zero access to any LDAP servers. So, what I did was asked the infrastructure team to provide me with 2 CSV files. The first file would contain all the old usernames and their new one, and the second file would contain all the old computer names and their new computer name (for AD BIND computer naming convention). The format was like so:

old_username1,new_username1
old_username2,new_username2
old_username3,new_username3

old_compname1,new_compname1
old_compname2,new_compname2
old_compname3,new_compname3

I also deployed a flat file that would track failures and force recons. So, step one, was to enroll every single Mac into the new JSS. Once they were all in, I could use extension attributes to track down what clients were bound to what servers. Then my script would run with a workflow like this:

1) Un-BIND from old AD DC
2) find current computer name, compare it to csv of names, and rename the Mac
3) verify computer name changed, if failed write to flat file that computer name failed to match, exit script, force recon
4) Once computer name was verified, BIND device to new AD, if device failed to BIND, write BIND failure to flat file, force recon
5) Repeat the steps above, but for old_username and new_username, if anything mismatched or failed, again I wrote that failure to a flat fire and forced a recon i. I would use the mv command to rename the home folder to the new_username ii. Apply proper permissions and ownership to new domain account iii. validated all changes
6) deleted old user record from directory services (dscl)
7) End user would log into the device with their new AD credentials, a mobile account would be created, and the home folder already existed with proper ownership and permissions

Some caveats of this workflow:

1) End users can not be logged in during this process

2) There is some room for errors, which is why I deployed a flat file, the recon will force extension attributes to look for specific failures, and then toss them into smart groups of those failure to get real time results. For example, if a computer name record did not match, it would say computer_name_match=failure. My extension attribute would catch that, and then toss that device into a smart group that had computer name mismatches.

3) Failures will probably need to be manually configured by a human.

4) You will need to write scripts that fit your AD environment, and your OS X environment. Things like dscl and the AD plugin differ from OS release to OS release. You will need to test against every OS you support.

In all my cases I was an outside entity so I was not allowed access to anything in LDAP. That is why I used awk one liners to pattern match exact strings (computer name, username, etc) and change over the proper naming convention. If you have LDAP access you can use dscl to query such things. I also had to put the CSV files on a file share that Casper could access. In version 9, scripts are stored in the database, so you would need to have that available somewhere else. You could cache it out locally, or have your script curl down the CSV file from an Apache server or something to that effect. Where as in version 8, scripts were stored in the file system, and Casper would mount a file share to run the script.

This method allowed Casper to do this stuff over a weekend so that when users came back Monday they just logged in with their new AD credentials and it was like nothing had changed. Of course, there are a lot of factors in this setup (weird white space in a csv file, misspelled names, etc) which can and will cause failure. In my experience all the AD migrations I did (and I did too many, way more than I would have liked to do), this method had a pretty high success rate, but never a 100% success rate. There were always the few clients here and there that would fail, which is why I build the flat file system with the extension attributes to put specific failures in specific smart groups. I had contingency policies in place that would be scoped to these specific failures in hopes of automating a success based on knowing how or why it failed.

I strongly suggest you roll something like this out in phases too. That way you don't create disarray in your org by migrating every user/department at once.

I know this doesn't help the Applescript specific questions, but this is how I accomplished this stuff in the past. So hopefully it is somewhat useful.

Thanks,
Tom

acdesigntech
Contributor II

you don't need the ' ' when encasing in double quotes in bash. Applescript will bawk, as you've seen. Bash will too, but it'll try to run at least.

That said, this ```
do shell script "chown -R " & shortName & ":DOMAIN\Domain Users /Users/" & shortName with administrator privileges
should work. I've found that with applescript a lot of the time you have to escape your escape when dealing with do shell scripts inside an applescript (hence the three 's rather than just the two). However, Applescript uses ':' as the file separator, so you might be running into an issue with that. I'm not an applescript guru so I'm not sure on that one. Try dropping the part where you set group permissions with
do shell script "chown -R " & shortName & " /Users/" & shortName with administrator privileges
```

Does that work? You don't TRULY need the group set anyway IMHO...

Yet another reason I hate applescript and avoid it like the plague.

catfeetstop
Contributor II

@bentoms You're a genius. It took me a while to realize that "quoted form of" was actually syntax. Once I figured that out and put that in the script it works like a charm. Thank you so much!

Thanks to everyone else too, you've all been very helpful!

jason_bracy
Contributor III

@stevewood did you ever figure out the FileVault issue?

stevewood
Honored Contributor II
Honored Contributor II

@bracyj_SAIC yes, as a matter of fact I did. I posted the final script and a rundown over here:

Change AD Domains with FV2 Enabled Users

It's important to make sure your users know their current password for the new domain. That was the number one problem I ran in to. They entered the wrong password and then they were not added to FileVault so on restart only the local admin was listed.

jason_bracy
Contributor III

Thanks @stevewood I'll test that out in my environment! Luckily we are syncing passwords to the new domain as well, so that should not be an issue.

jason_bracy
Contributor III

Just wanted to let everyone know That @stevewood's script was very helpful in my Domain Migration Project. We are about ready to kick it off and I thought I'd just add a helpful tip regarding FV enabled users.

Instead of deleting the user via DSCL I have modified the script to get the current userID, the new userID and then perform a DSCL command to change the userID and ownership. This does away with the need for a FV enabled Admin account being installed on the machine prior to migration.

loggedInUserID=`dscl . read /Users/$loggedInUser UniqueID`

Unjoin oldAD, Join newAD

domainUsersPrimaryGroupID=`dscl /Search read /Groups/Domain Users | grep PrimaryGroupID | awk '{ print $2}'`

accountUniqueID=`dscl "$adNodeName" -read /Users/$loggedInUser 2>/dev/null | grep UniqueID | awk '{ print $2}'`

dscl . -change /Users/$loggedInUser UniqueID "$loggedInUUID" "$accountUniqueID"

chown -R $accountUniqueID:$domainUsersPrimaryGroupID /Users/$loggedInUser

Let me know if anyone sees any issues with this approach. So far it has worked well in testing. I was worried that other directory information would not be changed using this method, but it seems that once the UniqueID is set that the AD plugin replaces all of the fields with the info from the new AD forest.