Skip to main content
Question

BackupRestoreUsers script. Automatically backup user home folders during a Casper erase and image!


loceee
Forum|alt.badge.img+10

Hey friendos,

Here's a little doozy we've been using for a while.

You have problem.You environment uses local accounts, or just cached mobile directory accounts directed to the local HD. You want to re-image but have to manually backup the user's home. That sucks.

Welcome BackupRestoreUsers.sh (worst title ever).

https://interpublic.app.box.com/s/wu5zud1j2yyd0mqpcq8y

It can be copied to a sharepoint or external hard drive and run interactively via the .command wrapper scripts without modification to backup all homes on a Mac.

OR

You can make two copies of the script, one for backup and one for restore. Then hard code the mode in the script (caspermode=true, mode="restore"/"backup")

Configure cdpmode=true, and set your options and credentials. (It will search mount points when netbooted for your CasperShare, then use that server to backup user homes to). Make a sharepoint on your CDPs for backing up user homes (maybe called UserBackup)... the script will mount it. (we just use inexpensive USB drives connected to the MacMinis at each location as we don't do bulk imaging often).

Upload the scripts via Casper Admin, and set them to...

BackupRestoreUsers-backup.sh - run before
BackupRestoreUsers-restore.sh - run after

Add them to a imaging configuration that erases and reimages. The workflow has timeout which defaults to backup all users (apart from the excluded ones hardcoded in the script).

Netboot your CEO's Macbook Air *, run an erase imaging workflow, follow the prompts and backup and restore their user data without out leaving CasperImaging!

* not a great idea

-- DISCLAIMER - use at your own risk! No warranty! Code is ugly disclaimer!

I do plan on opensourcing this, along with my patch management system very soon so it can be refined.

1#!/bin/bash
2version="0.98"
3#
4# BackupRestoreUsers
5#
6# INTERACTIVE MODE
7# ----------------
8# usage:
9#
10# configure your backuprepo as below (default: backuproot="$(dirname $0)/_UserBackups")
11#
12# copy the script folder to a share, then launch script via the xxx.command wrappers.
13#
14# CASPER IMAGING MODE
15#--------------------
16# usage:
17# as casper imaging is a little difficult with it's scripting workflows it goes like this.
18#
19# configure your backuprepo as below (either backuproot=/Volume/LocalHD, afpurl, smburl)
20# or
21# use the network segment section so you can find the nearest backupserver automatically.
22#
23# set 'caspermode=true'
24# uncomment 'mode="backup"'
25# save as 'BackupRestoreUsers-backup.sh'
26# do the same for 'mode="restore"'
27# save as 'BackupRestoreUsers-restore.sh'
28#
29# you should now have 2 scripts, a backup and restore mode script.
30#
31# upload both Casper Admin repo.
32#
33# allocate it to a configuration
34#
35# get info set the -backup.sh to run BEFORE, and -restore.sh to run AFTER
36#
37#
38# bugs. GUI can get stuck on osascript. sometimes crashes? more testing.
39#
40# more info lachlan.stewart@interpublic.com
41##############################################################
42#
43# 0.1 casperimaging spin off - BackupRestoreUsers.sh was getting to big for it's boots
44# 0.2 added initial gui and user exclusion
45# 0.3 merged functionality from old script -- can now run interactively
46# 0.9 testing release
47# 0.91 added functionality to deal with alternate user paritions that are mounted with fstab, and flags for /Users/.nobackup (automated wipe)
48# 0.92 changed to ditto due to some bugs in tar - enabled compression by default
49# 0.93 compression off, faster without
50# 0.94 another day, another method.. now will create a spareimage and rsync. this should be most robust
51# 0.95 fixed flush users bug, discovers caspermount server to do away with network segment code. assumes you are using cdp for userbackup location
52
53###############################
54#
55# configure from here
56#
57###############################
58
59caspermode=false # enable casper imaging mode (auto - nooptions to restore a different backup, logs log and doesn't recognise command switches)
60#mode="backup" # set mode for use with casper - set priority to run before
61#mode="restore" # set mode for use with casper - set priority to run after
62
63userfolder="/Users"
64altuserpart="/Volumes/Data" # watch for this user partition/path and use if seen (good if using fstab method to mount a userpartion eg. "/Volumes/UserHD/Users")
65excludedusers="^.|Shared|Deleted Users|admin|Library" # first line excludes all files starting with "." add anymore here...
66restoreto="$userfolder"
67
68showlog=true # open the log during
69
70rsyncexcludes=( ".Spotlight-V100" ".fseventsd" ".TemporaryItems" "**/Library/Application Support/Google/Chrome/Safe Browsing Download/**" ) # exclude any know troublesome files from copy
71
72debug=false # dump to debuglog - very verbose - just for me.
73
74###############################
75#
76# backup repo config
77#
78###############################
79
80#
81# cdp mode
82#
83
84cdpmode=false
85caspersharename="CasperShare" # share name to grep mount for, find your cdp mount
86userbackupcredentials="userbackup:userbackup" # username:password to mount userbackup share from mounted cdp
87userbackupshare="UserBackup"
88
89#
90# manual backuproot
91#
92
93backuproot="$(dirname $0)/_UserBackups" # backup to ./_UserBackups/ in this script folder
94
95# single network destinations
96# ----------------------------
97# afp://[username:password]@rhost[:port]/volume
98#afpurl="afp://username:password@servername/backup"
99# //[domain;][user[:password]@]server[/share] path
100#smburl="//username:password@servername/backup"
101
102# network segment specific destinations
103# --------------------------------------
104# edit the xxx.xxx.xxx and associated xxxurl=
105#
106# or (comment out networksegment to disable)
107
108#networksegment=$(ipconfig getifaddr en0 | cut -d. -f1-3) # ip and cut last quad for network segment
109
110if [ "$networksegment" != "" ]
111then
112 case $networksegment in
113 10.132.11 )
114 # site: BNE SMT
115 afpurl="afp://userbackup:userbackup@10.132.11.18/UserBackup"
116 ;;
117 10.128.15 )
118 # MEB 520BST
119 afpurl="afp://userbackup:userbackup@10.128.15.19/UserBackup"
120 ;;
121 10.132.9 )
122 # MEB McCann
123 afpurl="afp://userbackup:userbackup@10.132.9.21/UserBackup"
124 ;;
125 10.132.108 | 10.128.20 | 10.132.3 | 10.132.12 )
126 # SYD McCann/CMG
127 afpurl="afp://userbackup:userbackup@10.128.20.120/UserBackup"
128 ;;
129 *)
130 # default fallback
131 echo "Unhandled network segment -- $networksegment - using default"
132 afpurl="afp://userbackup:userbackup@mainserver/UserBackup"
133 ;;
134 esac
135fi
136
137scriptname="BackupRestoreUsers"
138
139
140#####################################
141#
142# End of configurable settings
143#
144#####################################
145
146#
147# some common fuctions
148#
149
150secho()
151{
152 # superecho - writes to log and will display a dialog to gui with timeout (a kludge for UI feedback in CI)
153 message="$1"
154 dialogtimeout="$2"
155 echo "$message"
156 echo "$message" >> "$logto/$log"
157 [ "$dialogtimeout" != "" ] && osascript -e "tell app "System Events"" -e "activate" -e "display dialog "$message" buttons {"..."} giving up after $dialogtimeout" -e "end tell"
158}
159
160errorHander()
161{
162 # Error function
163 message="$1"
164 secho "ERROR: $message"
165 osascript -e "tell app "System Events"" -e "activate" -e "display dialog "$message" buttons {"OK"} default button {"OK"} with title "Error"" -e "end tell"
166 cp "$logto/$log" "$backuppath/$log.ERROR.log"
167 rm "$logto/$log"
168 killall "Casper Imaging"
169 exit 1
170}
171
172askQuestion()
173{
174 message=$1
175 button1=$2 #default
176 button2=$3
177 timeout=$4 #will return default after
178 # we'll follow apple convention... default button should be right bottommost (button1)
179 if [ "$timeout" != "" ]
180 then
181 buttonreturn=$(osascript -e "tell app "System Events"" -e "activate" -e "display dialog "$message
182
183
184(Default: $button1 in $timeout secs)" buttons {"$button2","$button1"} default button {"$button1"} giving up after $timeout" -e "end tell")
185 else
186 # no timeout.. important questions
187 buttonreturn=$(osascript -e "tell app "System Events"" -e "activate" -e "display dialog "$message" buttons {"$button2","$button1"} default button {"$button1"}" -e "end tell")
188 fi
189 [ "$(echo $buttonreturn | grep "gave up:true")" != "" ] && result=1 # timeout, return default 1
190 [ "$(echo $buttonreturn | grep -w "$button1")" != "" ] && result=1
191 [ "$(echo $buttonreturn | grep -w "$button2")" != "" ] && result=2
192 echo $result
193}
194
195#
196# core brains
197#
198
199findServer()
200{
201 server=$(mount | grep $caspersharename | cut -d'@' -f2- | cut -d':' -f-1)
202 # if there was no mount with caspershare, don't set afp url
203 [ "$server" != "" ] && afpurl="afp://$userbackupcredentials@$server/$userbackupshare"
204}
205
206
207mountShare()
208{
209 # this code is for automounting network shares, if running the script via ARD / Casper Imaging / DeployStudio
210 # lets try to mount network volume, no error checking here... expecting properly formed urls to be passed to mount_afp/smb
211 secho "Mounting the network share..." 1
212 share=$(echo "$networkbackuppath" | cut -d'/' -f 4-)
213 mountpath="/Volumes/$share"
214 mkdir "$mountpath"
215 # check if afp or smb
216 if [ "$afpurl" != "" ]
217 then
218 mount_afp "$afpurl" "$mountpath"
219 mounterror="$?"
220 fi
221 if [ "$smburl" != "" ]
222 then
223 mount_smb "$smburl" "$mountpath"
224 mounterror="$?"
225 fi
226 if [ "$mounterror" != "0" ]
227 then
228 errorHander "There was a problem mounting the network share $share - mount_afp/smb returned $mounterror"
229 exit 1
230 fi
231 backuproot="$mountpath/_UserBackups"
232 [ ! -d "$backuproot" ] && mkdir "$backuproot"
233 # end network mount
234}
235
236unmountShare()
237{
238 #if we are using a network share, unmount it
239 cd /
240 secho "Unmounting $mountpath" 1
241 sleep 1
242 umount "$mountpath"
243
244 if [ "$?" != "0" ]
245 then
246 echo "Forcing unmount"
247 diskutil unmount force "$mountpath"
248 if [ "$?" != "0" ]
249 then
250 errorHander "There was a problem unmounting $backuproot"
251 fi
252 fi
253}
254
255excludeUsers()
256{
257 while true
258 do
259 # generate a dialog message for exlusions
260 message=$(
261 echo -ne "Would you like to exclude any users?
262"
263 echo -ne "
264"
265 echo -ne " Users to backup
266"
267 echo -ne "-------------------------
268"
269 echo -ne "
270"
271 for user in $(ls $userfolder | grep -vE "$excludedusers")
272 do
273 echo -ne "$user "
274 if [ -f "$userfolder/.$user-excluded" ]
275 then
276 echo -ne " - EXCLUDED
277"
278 else
279 echo -ne "
280"
281 fi
282 done
283 )
284 answer=$(askQuestion "$message" "Start Backup" "Exclude Users" 15)
285 if [ "$answer" == "2" ]
286 then
287 for user in $(ls $userfolder | grep -vE "$excludedusers")
288 do
289 answer=$(askQuestion "User:
290
291$user
292" "Include" "Exclude")
293 if [ "$answer" == "1" ]
294 then
295 # remove exclusion if it exists
296 [ -f "$userfolder/.$user-excluded" ] && rm "$userfolder/.$user-excluded"
297 else
298 # flag them as excluded
299 touch "$userfolder/.$user-excluded"
300 fi
301 done
302 else
303 # don't exclude anymore
304 break
305 fi
306 done
307}
308
309chooseUserfolder()
310{
311 # in case you User partition isn't found, GUI to select a difference backup source
312 answer=$(askQuestion "Do you want to backup from:
313
314
315$userfolder" "Yes" "Choose different..." 20)
316 if [ "$answer" == "2" ]
317 then
318 newuserfolder=$(/usr/bin/osascript << EOT
319 tell application "Finder"
320 activate
321 set theFolder to (choose folder with prompt "Choose another /Users folder to backup:")
322 return (POSIX path of theFolder)
323 end tell
324 EOT)
325 fi
326 # if a new location is chosen, use it
327 [ ! -z "$newuserfolder" ] && userfolder="$newuserfolder"
328}
329
330chooseBackup()
331{
332 while true
333 do
334 # generate a dialog message for backuplist
335 message=$(
336 echo -ne " Available Backups
337"
338 echo -ne "-------------------------
339"
340 echo -ne "
341"
342 [ -f "$backuproot/.DS_Store" ] && rm "$backuproot/.DS_Store" # just in case someone has been poking around in Users)
343 for backupid in $(ls "$backuproot")
344 do
345 echo -ne "$backupid
346"
347 done
348 )
349 answer=$(askQuestion "$message" "Select a Backup..." "Quit")
350 if [ "$answer" == "1" ]
351 then
352 for backupid in $(ls "$backuproot")
353 do
354 answer=$(askQuestion "BackupID: $backupid" "Next" "Select")
355 if [ "$answer" == "2" ]
356 then
357 # set new backup
358 uniqueid="$backupid"
359 backuppath="$backuproot/$uniqueid"
360 backupsettings="$backuppath/BackupSettings"
361 return 0
362 fi
363 done
364 else
365 # jump out
366 return 2
367 fi
368 done
369}
370
371backupUsers()
372{
373 [ -d "$altuserpart" ] && userfolder="$altuserpart" # if we find alternate user partion, point at it
374
375 if $caspermode
376 then
377 # if we wish to run an automated image, skipping backup... touch /Users/.nobackup before executing script
378 # but double check before skpping the backup.
379 if [ -f "$userfolder/.nobackup" ]
380 then
381 answer=$(askQuestion "### This Mac has been flagged NOT to Backup ###
382
383($userfolder/.nobackup exists)" "Don't Backup - WIPE IT ALL" "Remove flag and continue..." 30)
384 if [ "$answer" == "1" ]
385 then
386 # flag to save us checking for backups to restore post imaging
387 touch "/tmp/.nobackup-$uniqueid"
388 return 0
389 else
390 # remove the flag and run as normal
391 rm "$userfolder/.nobackup"
392 fi
393 fi
394
395 answer=$(askQuestion "Do you want to backup user data on this Mac?" "Backup" "Don't Backup" 20)
396 if [ "$answer" == "2" ]
397 then
398 answer=$(askQuestion "Are you really sure?
399
400All user data will be LOST!" "Backup" "Wipe it ALL!")
401 if [ "$answer" == "2" ]
402 then
403 # flag to save us checking for backups after restore
404 touch "/tmp/.nobackup-$uniqueid"
405 return 0
406 fi
407 fi
408 fi
409
410 # Check to see if there is an existing backup for this Mac
411 if [ -d "$backuppath" ]
412 then
413 # check to see if it's complete
414 if [ "$(defaults read $backupsettings BackupComplete)" != "1" ]
415 then
416 # if there is an incomplete backup, prompt... default to overwrite.. or give option to abort and sort it out.
417 answer=$(askQuestion "There is an INCOMPLETE backup, would you like to overwrite it?" "Backup and Overwrite" "Abort" 20)
418 [ "$answer" == "2" ] && errorHander "There is already a backup @ $backuppath ... Aborting..."
419 else
420 # if there is an COMPLETE backup, prompt... default to abort.. or give option to overwrite...
421 if [ "$(defaults read $backupsettings HasBeenRestored)" == "1" ]
422 then
423 answer=$(askQuestion "There is an existing COMPLETE backup which HAS been restored, would you like to overwrite it?" "Backup and Overwrite" "Abort" 20)
424 [ "$answer" == "2" ] && errorHander "There is already a backup @ $backuppath ... Aborting..."
425 else
426 answer=$(askQuestion "WARNING! There is an existing COMPLETE backup which HAS NOT been restored, would you like to overwrite it?" "Abort" "Backup and Overwrite" 60)
427 [ "$answer" == "1" ] && errorHander "There is already a backup @ $backuppath ... Aborting..."
428 fi
429 fi
430 # remove the old / existing backup
431 rm -R "$backuppath"
432 fi
433
434 chooseUserfolder
435
436 excludeUsers
437
438 answer=$(askQuestion "Would you like to flush user Trashes?" "Flush" "Don't Flush" 5)
439 if [ "$answer" == "1" ]
440 then
441 flushtrash=true
442 else
443 flushtrash=false
444 fi
445
446 #answer=$(askQuestion "Would you like to flush user Caches?" "Flush" "Don't Flush" 5)
447 #if [ "$answer" == "1" ]
448 #then
449 # flushcache=true
450 #else
451 # flushcache=false
452 #fi
453
454 # make paths and copy the logs to the server so we don't run out of space
455 mkdir "$backuppath"
456 mkdir "$backuppath/logs"
457 cp "$logto/$log" "$backuppath/logs/$log"
458 rm "$logto/$log"
459 logto="$backuppath/logs"
460
461 secho
462 secho "Backing up $uniqueid"
463 secho
464 secho "Scanning and sizing home folders ..." 2
465 secho "--------------------------------------"
466 secho
467
468 # open the log in console for more feedback
469 [ $showlog ] && open "$logto/$log"
470
471 usercount=0
472 totalsize=0
473
474 # get all the user folders in /User, process them and get size in bytes
475 for user in $(ls $userfolder | grep -vE "$excludedusers")
476 do
477 if [ ! -f "$userfolder/.$user-excluded" ] # check if they are excluded
478 then
479
480 if $flushtrash
481 then
482 secho "Flushing Trash for $user..."
483 rm -R "$userfolder/$user/.Trash"
484 fi
485
486 #if $flushcache
487 #then
488 # secho "Flushing Trash for $user..."
489 # rm -R "$userfolder/$user/Library/Caches/"
490 #fi
491
492 secho "Sizing home for $user..."
493 size=$(( $(du -sk "$userfolder/$user" | awk '{print $1}') * 1024 ))
494 secho "$user - $(($size / 1024 / 1024 )) MB" 2
495 userarray[$usercount]="$user"
496 sizearray[$usercount]="$size"
497 totalsize=$(( $totalsize + $size ))
498 (( usercount ++ ))
499 else
500 secho "$user - EXCLUDED"
501 fi
502 done
503
504 # get free space on back up disk
505 backupfree=$(( $(df -k "$backuproot" | tail -n1 | awk '{ print $4 }') * 1024 ))
506
507 secho "----------------------------------------------------------------------------------"
508 secho "Total for $usercount user(s) is $(( $totalsize / 1024 / 1024 )) MBs - Free space on backup volume is $(( backupfree / 1024 / 1024 )) MBs" 2
509 secho "----------------------------------------------------------------------------------"
510 if [ $totalsize -gt $backupfree ]
511 then
512 answer=$(askQuestion "There may not be enough free space on Backup volume" "Continue" "Stop" 30)
513 if [ "$answer" == "2" ]
514 then
515 # error out ...
516 errorHander "There is not enough space on the backup volume."
517 else
518 secho "WARNING: Ignoring free space error... I sure hope you know what you are doing!"
519 fi
520 fi
521
522 before=$(date +%s)
523 backuptogo=$totalsize
524 for (( i = 0 ; i < $usercount ; i++ ))
525 do
526 user="${userarray[$i]}"
527 usersize="${sizearray[$i]}"
528 usersettings="$backuppath/Users/$user/UserSettings"
529 # write some details about the user
530 defaults write $usersettings UserSize -string $usersize
531 # this reads the local ds via localonly (reads ds on target disk - not the system we are booted from)
532 networkusertest=$(dscl -f "$dslocal" localonly -read "/Local/Default/Users/$user" AuthenticationAuthority | grep LocalCachedUser)
533 if [ "$networkusertest" != "" ]
534 then
535 defaults write $usersettings NetworkUser -bool true
536 usertype="Network User"
537 else
538 defaults write $usersettings NetworkUser -bool false
539 usertype="Local User"
540 fi
541 secho
542 secho "Backing up ($(( $i + 1 ))/$usercount): $user - $(( $usersize / 1024 / 1024 ))/$(( $backuptogo / 1024 / 1024 )) MBs - $usertype" 2
543 secho "-------------------------------------------------------------------"
544 mkdir -p "$backuppath/Users/$user"
545 imagesize=$(( $usersize + (( $usersize / 4 )) )) # add 25% of padding to vol size
546 # create a sparse image on the backup store
547 secho "Creating image for $user of size $(( $imagesize / 1024 / 1024 )) MBs..." 1
548 hdiutil create -size ${imagesize} -type SPARSE -fs HFS+J -volname _backup_$user "$backuppath/Users/$user/$user.sparseimage" 2>> "$logto/$log"
549 error="$?"
550 if [ "$error" != "0" ]
551 then
552 errorHander "Error creating backup image for $user - hdiutil returned error: $error"
553 fi
554 # attach our new image
555 hdiutil attach -owners on "$backuppath/Users/$user/$user.sparseimage" 2>> "$logto/$log"
556 if [ "$error" != "0" ]
557 then
558 errorHander "Error attaching backup image for $user - hdiutil returned error: $error"
559 fi
560 # write our rsync exclude file
561 for rsyncexclude in ${rsyncexcludes[@]}
562 do
563 echo "$rsyncexclude" >> /tmp/rsync-excludes.txt
564 done
565 # rsync all data
566 rsync $rsyncopts -aE --log-file="$logto/$log" --exclude-from="/tmp/rsync-excludes.txt" "$userfolder/$user/" "/Volumes/_backup_$user/$user/"
567 error="$?"
568 rm /tmp/rsync-excludes.txt
569
570 # rsync error handler - we can ignore a couple of common errors
571 if [ "$error" != "0" ]
572 then
573 case $error in
574 23)
575 secho "rsync error 23 - some files were inaccessible (probably in use)"
576 ;;
577 24)
578 secho "rsync error 24 - some files disappeared during rsync"
579 ;;
580 12)
581 errorHander "Problem backing $user, out of disk space"
582 ;;
583 *)
584 errorHander "Problem backing up $user, rsync returned error: $error"
585 ;;
586 esac
587 fi
588 secho "Ejecting /Volumes/_backup_$user ..."
589 diskutil eject "/Volumes/_backup_$user"
590 [ "$?" != "0" ] && errorHander "There was a problem ejecting /Volumes/_backup_$user"
591 # decrement our backupdata to go...
592 backuptogo=$(( $backuptogo - $usersize ))
593 done
594 after=$(date +%s)
595 totalsecs=$(( $after - $before ))
596 # echo "Completed in $(($diff / 60)):$(($diff % 60)) min:secs"
597 totaltime="$(date -r $totalsecs +%M:%S) secs"
598
599 [ $showlog ] && killall Console
600
601 defaults write $backupsettings BackupDate -string "$(date)"
602 defaults write $backupsettings TotalTime -string "$totaltime"
603 defaults write $backupsettings TotalSize -string "$totalsize"
604 defaults write $backupsettings HasBeenRestored -bool false
605 defaults write $backupsettings BackupComplete -bool true
606
607 secho
608 secho "=========================================="
609 secho " Completed $(( $totalsize / 1024 / 1024 )) MBs in $totaltime" 3
610 secho "=========================================="
611 secho "finished: $(date)"
612 secho
613 secho "NOTE: this script only backs up User folders in $userfolder"
614 secho " any files outside of this have NOT been backup up."
615 secho
616}
617
618
619restoreUsers()
620{
621 # check for no backup flag
622 if [ -f "/tmp/.nobackup-$uniqueid" ]
623 then
624 secho "No backup performed for $uniqueid..." 2
625 rm "/tmp/.nobackup-$uniqueid"
626 return 0
627 fi
628
629 while true
630 do
631 if [ -d "$backuppath" ] # if there is a backup for this mac restore
632 then
633 #if its already been restored, error out
634 if [ "$(defaults read $backupsettings HasBeenRestored)" == "1" ]
635 then
636 answer=$(askQuestion "Backup $uniqueid has already been restored" "Continue with Restore" "Choose another backup..." )
637 if [ "$answer" == "2" ]
638 then
639 # to choose another... reset backup path and reloop
640 backuppath=""
641 continue
642 fi
643 fi
644 backupdate=$(defaults read $backupsettings BackupDate)
645 totaltime=$(defaults read $backupsettings TotalTime)
646 totalsize=$(defaults read $backupsettings TotalSize)
647 if ! $caspermode
648 then
649 # if we are in interactive mode.. present options
650 # read in backup details and make a dialog message
651 message=$(
652 echo -ne "Backup Details
653--------------------
654$uniqueid
655Totalsize: $(( $totalsize / 1024 / 1024 )) MBs
656Completed in: $totaltime"
657 echo -ne "
658
659"
660 echo -ne "Users
661"
662 echo -ne "------
663"
664 #
665 # read in all the backups on the store
666 #
667 [ -f "$backuppath/Users/.DS_Store" ] && rm "$backuppath/Users/.DS_Store" # just in case someone has been poking around in Users
668 for backupuserfolder in $(ls "$backuppath/Users/")
669 do
670 user=$(basename $backupuserfolder)
671 usersize=$(defaults read "$backuppath/Users/$backupuserfolder/UserSettings" UserSize)
672 echo -ne "$user - $(( $usersize / 1024 / 1024 )) MBs
673"
674 done
675 echo -ne "
676
677"
678 )
679 answer=$(askQuestion "$message" "Restore" "Choose another backup..." )
680 if [ "$answer" == "2" ]
681 then
682 # to choose another... reset backup path and reloop
683 backuppath=""
684 continue
685 fi
686 fi
687
688 # ask to remove backup, do it before we start the restore so it can be unattended
689 answer=$(askQuestion "Would you like to delete the backup once it has been restore successfully?" "Delete Backup" "Keep" 20)
690 if [ "$answer" == "1" ]
691 then
692 removebackup=true
693 else
694 removebackup=false
695 fi
696
697 # do the restore
698 cp "$logto/$log" "$backuppath/logs/$log"
699 rm "$logto/$log"
700 logto="$backuppath/logs"
701
702 # open the log in console for more feedback
703 [ $showlog ] && open "$logto/$log"
704
705 secho "$uniqueid - Restore size: $(( $totalsize / 1024 / 1024 )) MBs" 2
706 secho
707 before=$(date +%s)
708 backuptogo=$totalsize
709 [ -f "$backuppath/Users/.DS_Store" ] && rm "$backuppath/Users/.DS_Store" # just in case someone has been poking around in Users
710 for userroot in "$backuppath/Users/"*
711 do
712 user=$(basename $userroot)
713 usersettings="$backuppath/Users/$user/UserSettings"
714 usersize=$(defaults read $usersettings UserSize)
715 if [ "$(defaults read $usersettings NetworkUser)" == "1" ]
716 then
717 usertype="Network"
718 else
719 usertype="Local"
720 fi
721 secho
722 secho "Restoring: $user $(( $usersize / 1024 / 1024 ))/$(( $backuptogo / 1024 / 1024 )) MBs - $usertype account" 2
723 secho "-------------------------------------------------------------------"
724
725 hdiutil attach -owners on "$userroot/$user.sparseimage" 2>> "$logto/$log"
726 error="$?"
727 if [ "$error" != "0" ]
728 then
729 errorHander "Error attaching backup image for $user - hdiutil returned error: $error"
730 fi
731 rsync $rsyncopts -aE --log-file="$logto/$log" "/Volumes/_backup_$user/$user/" "$restoreto/$user/"
732 error="$?"
733 # rsync error handler
734 if [ "$error" != "0" ]
735 then
736 case $error in
737 23)
738 secho "rsync error 23 - some files were inaccessible (probably in use)"
739 ;;
740 24)
741 secho "rsync error 24 - some files disappeared during rsync"
742 ;;
743 12)
744 errorHander "Problem backing $user, out of disk space"
745 ;;
746 *)
747 errorHander "Problem backing up $user, rsync returned error: $error"
748 ;;
749 esac
750 fi
751 secho "Ejecting /Volumes/_backup_$user ..."
752 diskutil eject "/Volumes/_backup_$user"
753 [ "$?" != "0" ] && errorHander "There was a problem ejecting /Volumes/_backup_$user"
754 # decrement data
755 backuptogo=$(( $backuptogo - $usersize ))
756 done
757 after=$(date +%s)
758 totalsecs=$(( $after - $before ))
759 totaltime="$(date -r $totalsecs +%M:%S) secs"
760 defaults write $backupsettings HasBeenRestored -bool true
761 secho
762 secho "=========================================="
763 secho " Completed $(( $totalsize / 1024 / 1024 )) MBs in $totaltime" 3
764 secho "=========================================="
765 secho "finished: $(date)"
766 cp "$logto"/*.log "$target/Library/Logs/" # copy logs to target when done
767
768 [ $showlog ] && killall Console
769
770 # remove them when done
771 if $removebackup
772 then
773 # delete the backup
774 secho "Deleting $backuppath" 2
775 while [ -d "$backuppath" ]
776 do
777 # network shares have some locks / trashes that hold up proceedings, just keep hitting it until it dies
778 rm -R "$backuppath"
779 sleep 1
780 done
781 else
782 defaults write $backupsettings HasBeenRestored -bool true
783 fi
784 break
785 else
786 # no backups found for restore
787 if $caspermode
788 then
789 secho "There are no backups for $uniqueid" 4
790 break
791 else
792 if [ ! "$(ls $backuproot)" ] # check if the backup folder is empty
793 then
794 secho "There are no backups on $backuproot"
795 break
796 fi
797 # interactive mode choose another backup and loop back
798 chooseBackup
799 # we want to quit?
800 [ "$?" == "2" ] && break
801 fi
802 fi
803 done
804}
805
806
807################################
808# Begin!
809################################
810
811
812datestamp=$(date "+%F_%H-%M-%S")
813
814logto="/tmp"
815log="$scriptname-$datestamp.log"
816
817if $debug
818then
819 # dump debug logs to /
820 set -xv; exec 1>/BackupRestoreUsers-DEBUG-$datestamp.log 2>&1
821fi
822
823dscl="/usr/bin/dscl" ### test - we should run dscl from current booted os. assumed that netboot system will be > system if upgrading.
824dslocal="/var/db/dslocal/nodes/Default" # path to local directory data
825
826target="$1" # casper passes .. $1 - target, $2 - computername, $3 username --- we only need target path.
827[ "$target" == "/" ] && target="" # if we are targetting / - set target to "" so we don't end up with //Users
828userfolder="$target$userfolder" # eg. /Volumes/MacHD/Users
829restoreto="$target$restoreto" #
830dslocal="$target$dslocal" # etc..
831
832# if there we are in cdpmode, find the cdp server, and set url
833[ $cdpmode ] && findServer
834
835# just drop afp or smb url into this variable
836networkbackuppath="$afpurl$smburl"
837
838# uniqueid - serial or MAC address of en0
839uniqueid=$(ioreg -c "IOPlatformExpertDevice" | awk -F '"' '/IOPlatformSerialNumber/ {print $4}')
840[ "$uniqueid" == "" ] && uniqueid=$(ifconfig en0 | awk ' /ether/ {print $2}') # if no serial, fallback to MAC address
841
842# set the IFS to cr
843OLDIFS=$IFS
844IFS=$'
845'
846
847#
848# Lets Go!
849#
850secho
851secho "$scriptname $mode $version - $uniqueid" 1
852secho "================================================="
853secho "started: $(date)"
854secho
855
856# if we have a network path, mount it
857[ "$networkbackuppath" != "" ] && mountShare
858
859[ ! -d "$backuproot" ] && errorHander "I can't find $backuproot"
860
861backuppath="$backuproot/$uniqueid"
862backupsettings="$backuppath/BackupSettings"
863
864#
865# Core handler .. parse mode line etc
866#
867
868if $caspermode
869then
870 # mode is hard coded
871 case $mode in
872 backup )
873 backupUsers
874 ;;
875 restore )
876 restoreUsers
877 ;;
878 esac
879else
880 # interactive mode called by wrappers
881 case $2 in
882 --backup )
883 backupUsers
884 ;;
885 --restore )
886 restoreUsers
887 ;;
888 * )
889 usage
890 ;;
891 esac
892fi
893
894# unmount network share
895[ "$networkbackuppath" != "" ] && unmountShare
896
897IFS=$OLDIFS
898exit

15 replies

Forum|alt.badge.img+10
  • Contributor
  • 52 replies
  • March 25, 2014

this is good did you ever look at @rustymyers script https://github.com/rustymyers/BackupRestore


loceee
Forum|alt.badge.img+10
  • Author
  • Contributor
  • 182 replies
  • March 25, 2014

I sure did. Rusty's scripts are great I borrowed a few ideas..!


Forum|alt.badge.img+12
  • Contributor
  • 529 replies
  • March 25, 2014

Do you realise you can create a separate partition and use /etc/fstab to mount this as /Users? This would mean you can reinstall as many times as you like without having to move any data!

1man fstab

Forum|alt.badge.img+23
  • Esteemed Contributor
  • 850 replies
  • March 25, 2014

I'm sure he does. However the fstab option fails completely with CoreStorage based drives, such as Fusion drives.


bentoms
Forum|alt.badge.img+35
  • Hall of Fame
  • 4331 replies
  • March 25, 2014

loceee
Forum|alt.badge.img+10
  • Author
  • Contributor
  • 182 replies
  • March 25, 2014

I am across fstab method, but as we moved away from imaging towards thin / package based deployment it's not the method I wanted to go with.

In fact, you will notice the altuserpart variable in the script, it's there to undo our fstab user partitions. If the script finds the Volume path you specify, it will backup /Users from there instead of /Users.


loceee
Forum|alt.badge.img+10
  • Author
  • Contributor
  • 182 replies
  • March 25, 2014

FYI - I've updated the link in the original post to one that actually works. Download that, it comes with .command wrapper scripts that launch it in interactive mode.

https://interpublic.app.box.com/s/wu5zud1j2yyd0mqpcq8y


Forum|alt.badge.img+10
  • Contributor
  • 52 replies
  • March 27, 2014

@loceee So i tested the back up part today nice work. Would but nice to wrap with coco dialog to have backup and restore location asked for. Good Job sir. thanks for sharing.


loceee
Forum|alt.badge.img+10
  • Author
  • Contributor
  • 182 replies
  • March 27, 2014

Hey Raul, I had decided to not use CocoaDialog for this as I didn't want it to have any external dependancies when used in a Casper Imaging netboot environment, hence the use of native applescript. It means the UI is pretty kludgey, but it gets the job done.

By design it was to be used in Casper Imaging mainly to undo our ftstab dual partitioned Macs with minimal interaction, for our L1 guys to be able to reimage Macs to eliminate system software issues... and to migrate to other Macs using the interactive mode.

Make two copies, set one to backup, one to restore mode, run before and after... put them in all your imaging workflows.

It WILL prompt to confirm where you are backing up from, and allow you to select a different user folder.. but with the correct config it should reliably hit the internal volume when netbooted, or a the user partition if your computer was imaged in a dual partition config.

I hadn't had a use case where I would need a user home restored to anywhere else but /Users, so I hadn't coded it... selfish me :) Glad it might be useful to someone!


loceee
Forum|alt.badge.img+10
  • Author
  • Contributor
  • 182 replies
  • March 31, 2014

I've had a few people come back to me, as my instructions are a bit vague.

Here's a couple of screenshots that might help picture how to use it.

https://interpublic.app.box.com/s/r7tfjhgdjxce7zje1xoz


loceee
Forum|alt.badge.img+10
  • Author
  • Contributor
  • 182 replies
  • April 7, 2014

https://interpublic.box.com/s/quqoz45h54v7k1y8ktrj

Link updated as I am going to put some more scripts in here soon.


Forum|alt.badge.img+6
  • New Contributor
  • 7 replies
  • February 18, 2015

Hi Loceee,

I have tried to pull up your links to see how you scripted the backup and restore along with repartitioning to a single partition drive. Could you forward the information to me if you still have it? We are also exploring backup to network drives using self service for our level 1 techs. Sounds like everything worked for you.

Thanks, Brad


loceee
Forum|alt.badge.img+10
  • Author
  • Contributor
  • 182 replies
  • February 18, 2015

@Goldman - howdy, since I don't working at IPG anymore those links will be dead. Sorry! I have moved the scripts I share to my github. http://github.com/loceee


Forum|alt.badge.img+4
  • Contributor
  • 23 replies
  • October 23, 2016

Hi Loceee,

I was wondering if you still have your instruction on how to setup your BackupRestoreUsers.sh script?

Thank you,
ArielN


Forum|alt.badge.img+2
  • New Contributor
  • 1 reply
  • November 22, 2016

@loceee 5 years on and i am still learning from your scripts Locky!

keep up the good work mate!


Reply


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings