├── 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 | Main Image 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 | Main Image 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 | --------------------------------------------------------------------------------