├── images
├── main_image.png
└── fullscreen_jamfHelper.png
├── README.md
└── thunderbolt_data_migrator.sh
/images/main_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryangball/thunderbolt-data-migrator/HEAD/images/main_image.png
--------------------------------------------------------------------------------
/images/fullscreen_jamfHelper.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryangball/thunderbolt-data-migrator/HEAD/images/fullscreen_jamfHelper.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Thunderbolt Data Migrator
2 | A script that automates data transfer from one mac to another easily. This is intended to be used by non-technical individuals with minimal hassle on Macs managed by Jamf Pro (could be customized for other environments).
3 |
4 |
5 |
6 |
7 | ## Requirements
8 | - jamfHelper is used to display some of the the user dialogs while the script is running.
9 |
10 | *Note: There are no Jamf Pro policies required in order for this tool to function. You could easily adapt this for use in other environments using a tool like [cocoadialog](https://cocoadialog.com/).*
11 |
12 | ## Usage
13 | This script needs to be run in a BASH shell. You can do the following:
14 | ```bash
15 | # Cloning the repo is preferred
16 | git clone https://github.com/ryangball/thunderbolt-data-migrator.git
17 | cd thunderbolt-data-migrator
18 | sudo ./thunderbolt_data_migrator.sh
19 | ```
20 |
21 | *OR*
22 |
23 | ```bash
24 | # If you chose to just copy the raw script text
25 | cd /path/where/you/copied/the/script/
26 | chmod +x thunderbolt_data_migrator.sh
27 | sudo ./thunderbolt_data_migrator.sh
28 | ```
29 |
30 | *OR*
31 |
32 | ```bash
33 | # If you chose to just copy the raw script text
34 | cd /path/where/you/copied/the/script/
35 | sudo bash thunderbolt_data_migrator.sh
36 | ```
37 |
38 | ## Testing
39 | By default the script is set to not perform the rsync action when `testing="true"`.
40 | ```bash
41 | # Set to true while testing, the rsync will be bypassed and nothing permanent will done to this Mac
42 | # Set to false when used in production
43 | testing="true" # (true|false)
44 | ```
45 |
46 | ## Workflow
47 | 1. User receives a dialog box with simple instructions to start up their old machine in [Target Disk Mode](https://support.apple.com/en-us/HT201462) and connect it to their new Mac via Thunderbolt cable (top-most image)
48 | 2. If a user successfully connects their Target Disk Mode booted Mac, the Thunderbolt volume will be detected automatically and move on.
49 | 3. In the event that a Target Disk Mode booted Mac is already connected before the script begins, the user has an option to select the Thunderbolt volume themselves.
50 | 4. If a user exists on the old Mac that matches the logged in username on the new Mac, the script will automatically assume that old user's home folder is the source of our transfer.
51 | 5. If **no** user exists on the old Mac that matches the logged in username on the new Mac, the user will get an option to select from a list of user home folders to determine the source of our transfer.
52 | 6. The script then determines if there is enough space to transfer from the source home folder to the new Mac and continues if space requirements are met.
53 | 7. A full screen jamfHelper dialog is then displayed while the old home folder data is transferred to the logged in user's home folder on the new Mac (using [rsync](https://ss64.com/osx/rsync.html)) (image below).
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/thunderbolt_data_migrator.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Written by Ryan Ball
4 | # Originally obtained from: https://github.com/ryangball/thunderbolt-data-migrator
5 |
6 | # This variable can be used if you are testing the script
7 | # Set to true while testing, the rsync will be bypassed and nothing permanent will done to this Mac
8 | # Set to false when used in production
9 | testing="true" # (true|false)
10 |
11 | # The full path of the log file
12 | log="/Library/Logs/tunderbolt_data_migration.log"
13 |
14 | # The main icon displayed in jamfHelper dialogs
15 | icon="/Applications/Utilities/Migration Assistant.app/Contents/Resources/MigrateAsst.icns"
16 |
17 | # The instructions that are shown in the first dialog to the user
18 | instructions="You can now migrate your data from your old Mac.
19 |
20 | 1. Turn your old Mac off.
21 |
22 | 2. Connect your old Mac and new Mac together using the supplied Thunderbolt cable.
23 |
24 | 3. Power on your old Mac by normally pressing the power button WHILE holding the \"T\" button down for several seconds.
25 |
26 | We will attempt to automatically detect your old Mac now..."
27 |
28 | ###### Variables below this point are not intended to be modified ######
29 | scriptName=$(basename "$0")
30 | jamfHelper=/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper
31 |
32 | function writelog () {
33 | DATE=$(date +%Y-%m-%d\ %H:%M:%S)
34 | /bin/echo "${1}"
35 | /bin/echo "$DATE" " $1" >> "$log"
36 | }
37 |
38 | function finish () {
39 | writelog "======== Finished $scriptName ========"
40 | ps -p "$jamfHelperPID" > /dev/null && kill "$jamfHelperPID"; wait "$jamfHelperPID" 2>/dev/null
41 | rm /tmp/output.txt
42 | exit "$1"
43 | }
44 |
45 | function wait_for_gui () {
46 | # Wait for the Dock to determine the current user
47 | DOCK_STATUS=$(pgrep -x Dock)
48 | writelog "Waiting for Desktop..."
49 |
50 | while [[ "$DOCK_STATUS" == "" ]]; do
51 | writelog "Desktop is not loaded; waiting..."
52 | sleep 5
53 | DOCK_STATUS=$(pgrep -x Dock)
54 | done
55 |
56 | loggedInUser=$(/usr/bin/python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");')
57 | writelog "$loggedInUser is logged in and at the desktop; continuing."
58 | }
59 |
60 | function wait_for_jamfHelper () {
61 | # Make sure jamfHelper has been installed
62 | writelog "Waiting for jamfHelper to be installed..."
63 | while [[ ! -e "$jamfHelper" ]]; do
64 | sleep 2
65 | done
66 | writelog "jamfHelper detected; continuing."
67 | }
68 |
69 | function perform_rsync () {
70 | writelog "Beginning rsync transfer..."
71 | "$jamfHelper" -windowType fs -title "" -icon "$icon" -heading "Please wait as we transfer your old data to your new Mac..." \
72 | -description "This might take a few minutes. Once the transfer is complete this screen will close." &>/dev/null &
73 | jamfHelperPID=$(/bin/echo $!)
74 |
75 | if [[ "$testing" != "true" ]]; then
76 | # Perform the rsync
77 | /usr/bin/rsync -vrpog --progress --update --ignore-errors --force \
78 | --exclude='Library' --exclude='Microsoft User Data' --exclude='.DS_Store' --exclude='.Trash' \
79 | --log-file="$log" "$oldUserHome/" "/Users/$loggedInUser/"
80 |
81 | # Ensure permissions are correct
82 | /usr/sbin/chown -R "$loggedInUser" "/Users/$loggedInUser" 2>/dev/null
83 | else
84 | writelog "Sleeping for 10 to simulate rsync..."
85 | sleep 10
86 | fi
87 |
88 | ps -p "$jamfHelperPID" > /dev/null && kill "$jamfHelperPID"; wait "$jamfHelperPID" 2>/dev/null
89 | writelog "Finished rsync transfer."
90 | /usr/sbin/diskutil unmount "/Volumes/$tBoltVolume" &>/dev/null
91 | finish 0
92 | }
93 |
94 | function calculate_space_requirements () {
95 | # Determine free space on this Mac
96 | freeOnNewMac=$(df -k / | tail -n +2 | awk '{print $4}')
97 | writelog "Free space on this Mac: $freeOnNewMac KB ($((freeOnNewMac/1024)) MB)"
98 |
99 | # Determine how much space the old home folder takes up
100 | spaceRequired=$(du -sck "$oldUserHome" | grep total | awk '{print $1}')
101 | writelog "Storage requirements for \"$oldUserHome\": $spaceRequired KB ($((spaceRequired/1024)) MB)"
102 |
103 | if [[ "$freeOnNewMac" -gt "$spaceRequired" ]]; then
104 | writelog "There is more than $spaceRequired KB available on this Mac; continuing."
105 | perform_rsync
106 | else
107 | writelog "Not enough free space on this Mac; exiting."
108 | "$jamfHelper" -windowType utility -title "User Data Transfer" -icon "$icon" -description "Your new Mac does not have enough free space to transfer your old data over. If you want to try again, please contact the Help Desk." -button1 "OK" -calcelButton "1" -defaultButton "1" &>/dev/null &
109 | finish 1
110 | fi
111 | }
112 |
113 | function manually_find_old_user () {
114 | # Determine all home folders on the old Mac
115 | oldUsersArray=()
116 | while IFS='' read -ra line; do oldUsersArray+=("$line"); done < <(/usr/bin/find "/Volumes/$tBoltVolume/Users" -maxdepth 1 -mindepth 1 -type d | awk -F'/' '{print $NF}' | grep -v Shared)
117 |
118 | # Exit if we didn't find any users
119 | if [[ "${#oldUsersArray[@]}" -eq 0 ]]; then
120 | echo "No user home folders found in: /Volumes/$tBoltVolume/Users"
121 | "$jamfHelper" -windowType utility -title "User Data Transfer" -icon "$icon" -description "Could not find any user home folders on the selected Thunderbolt volume. If you have any questions, please contact the Help Desk." -button1 "OK" -calcelButton "1" -defaultButton "1" &>/dev/null &
122 | finish 1
123 | fi
124 |
125 | # Show list of home folders so that the user can choose their old username
126 | # Something like cocoadialog would be preferred here as it has a dropdown, but it's got no Dark Mode :(
127 | # Heredocs cause some weird allignment issues
128 | dialogOutput=$(/usr/bin/osascript </dev/null &
172 | finish 1
173 | fi
174 |
175 | # Allow the user to choose from a list of connected Thunderbolt volumes
176 | # Something like cocoadialog would be preferred here as it has a dropdown, but it's got no Dark Mode :(
177 | # Heredocs cause some weird allignment issues
178 | dialogOutput=$(/usr/bin/osascript < /dev/null && kill "$jamfHelperPID"; wait "$jamfHelperPID" 2>/dev/null
233 | auto_find_old_user
234 | fi
235 | fi
236 | timer=$((timer-5))
237 | done
238 | # At this point the timer has run out, kill the background jamfHelper dialog and let the user know
239 | ps -p "$jamfHelperPID" > /dev/null && kill "$jamfHelperPID"; wait "$jamfHelperPID" 2>/dev/null
240 | writelog "Unable to detect a Thunderbolt volume in the amount of time specified; exiting."
241 | "$jamfHelper" -windowType utility -title "User Data Transfer" -icon "$icon" -description "We were unable to detect your old Mac. If you want to try again, please contact the Help Desk." -button1 "OK" -calcelButton "1" -defaultButton "1" &>/dev/null &
242 | finish 1
243 | }
244 |
245 | writelog " "
246 | writelog "======== Starting $scriptName ========"
247 |
248 | # Wait for a GUI
249 | wait_for_gui
250 |
251 | # Wait for jamfHelper to be installed
252 | wait_for_jamfHelper
253 |
254 | # Display a jamfHelper dialog with instructions as a background task
255 | "$jamfHelper" -windowType utility -title "User Data Transfer" -icon "$icon" -description "$instructions" -button1 "Cancel" -button2 "I'll Pick" -calcelButton "1" -defaultButton "1" > /tmp/output.txt &
256 | jamfHelperPID=$(/bin/echo $!)
257 |
258 | # Attempt to detect a new thunderbolt volume, other funtions are chained together
259 | detect_new_tbolt_volumes
260 |
261 | finish 0
262 |
--------------------------------------------------------------------------------