Posted on 03-24-2014 06:18 PM
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.
#!/bin/bash
version="0.98"
#
# BackupRestoreUsers
#
# INTERACTIVE MODE
# ----------------
# usage:
#
# configure your backuprepo as below (default: backuproot="$(dirname $0)/_UserBackups")
#
# copy the script folder to a share, then launch script via the xxx.command wrappers.
#
# CASPER IMAGING MODE
#--------------------
# usage:
# as casper imaging is a little difficult with it's scripting workflows it goes like this.
#
# configure your backuprepo as below (either backuproot=/Volume/LocalHD, afpurl, smburl)
# or
# use the network segment section so you can find the nearest backupserver automatically.
#
# set 'caspermode=true'
# uncomment 'mode="backup"'
# save as 'BackupRestoreUsers-backup.sh'
# do the same for 'mode="restore"'
# save as 'BackupRestoreUsers-restore.sh'
#
# you should now have 2 scripts, a backup and restore mode script.
#
# upload both Casper Admin repo.
#
# allocate it to a configuration
#
# get info set the -backup.sh to run BEFORE, and -restore.sh to run AFTER
#
#
# bugs. GUI can get stuck on osascript. sometimes crashes? more testing.
#
# more info lachlan.stewart@interpublic.com
##############################################################
#
# 0.1 casperimaging spin off - BackupRestoreUsers.sh was getting to big for it's boots
# 0.2 added initial gui and user exclusion
# 0.3 merged functionality from old script -- can now run interactively
# 0.9 testing release
# 0.91 added functionality to deal with alternate user paritions that are mounted with fstab, and flags for /Users/.nobackup (automated wipe)
# 0.92 changed to ditto due to some bugs in tar - enabled compression by default
# 0.93 compression off, faster without
# 0.94 another day, another method.. now will create a spareimage and rsync. this should be most robust
# 0.95 fixed flush users bug, discovers caspermount server to do away with network segment code. assumes you are using cdp for userbackup location
###############################
#
# configure from here
#
###############################
caspermode=false # enable casper imaging mode (auto - nooptions to restore a different backup, logs log and doesn't recognise command switches)
#mode="backup" # set mode for use with casper - set priority to run before
#mode="restore" # set mode for use with casper - set priority to run after
userfolder="/Users"
altuserpart="/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")
excludedusers="^.|Shared|Deleted Users|admin|Library" # first line excludes all files starting with "." add anymore here...
restoreto="$userfolder"
showlog=true # open the log during
rsyncexcludes=( ".Spotlight-V100" ".fseventsd" ".TemporaryItems" "**/Library/Application Support/Google/Chrome/Safe Browsing Download/**" ) # exclude any know troublesome files from copy
debug=false # dump to debuglog - very verbose - just for me.
###############################
#
# backup repo config
#
###############################
#
# cdp mode
#
cdpmode=false
caspersharename="CasperShare" # share name to grep mount for, find your cdp mount
userbackupcredentials="userbackup:userbackup" # username:password to mount userbackup share from mounted cdp
userbackupshare="UserBackup"
#
# manual backuproot
#
backuproot="$(dirname $0)/_UserBackups" # backup to ./_UserBackups/ in this script folder
# single network destinations
# ----------------------------
# afp://[username:password]@rhost[:port]/volume
#afpurl="afp://username:password@servername/backup"
# //[domain;][user[:password]@]server[/share] path
#smburl="//username:password@servername/backup"
# network segment specific destinations
# --------------------------------------
# edit the xxx.xxx.xxx and associated xxxurl=
#
# or (comment out networksegment to disable)
#networksegment=$(ipconfig getifaddr en0 | cut -d. -f1-3) # ip and cut last quad for network segment
if [ "$networksegment" != "" ]
then
case $networksegment in
10.132.11 )
# site: BNE SMT
afpurl="afp://userbackup:userbackup@10.132.11.18/UserBackup"
;;
10.128.15 )
# MEB 520BST
afpurl="afp://userbackup:userbackup@10.128.15.19/UserBackup"
;;
10.132.9 )
# MEB McCann
afpurl="afp://userbackup:userbackup@10.132.9.21/UserBackup"
;;
10.132.108 | 10.128.20 | 10.132.3 | 10.132.12 )
# SYD McCann/CMG
afpurl="afp://userbackup:userbackup@10.128.20.120/UserBackup"
;;
*)
# default fallback
echo "Unhandled network segment -- $networksegment - using default"
afpurl="afp://userbackup:userbackup@mainserver/UserBackup"
;;
esac
fi
scriptname="BackupRestoreUsers"
#####################################
#
# End of configurable settings
#
#####################################
#
# some common fuctions
#
secho()
{
# superecho - writes to log and will display a dialog to gui with timeout (a kludge for UI feedback in CI)
message="$1"
dialogtimeout="$2"
echo "$message"
echo "$message" >> "$logto/$log"
[ "$dialogtimeout" != "" ] && osascript -e "tell app "System Events"" -e "activate" -e "display dialog "$message" buttons {"..."} giving up after $dialogtimeout" -e "end tell"
}
errorHander()
{
# Error function
message="$1"
secho "ERROR: $message"
osascript -e "tell app "System Events"" -e "activate" -e "display dialog "$message" buttons {"OK"} default button {"OK"} with title "Error"" -e "end tell"
cp "$logto/$log" "$backuppath/$log.ERROR.log"
rm "$logto/$log"
killall "Casper Imaging"
exit 1
}
askQuestion()
{
message=$1
button1=$2 #default
button2=$3
timeout=$4 #will return default after
# we'll follow apple convention... default button should be right bottommost (button1)
if [ "$timeout" != "" ]
then
buttonreturn=$(osascript -e "tell app "System Events"" -e "activate" -e "display dialog "$message
(Default: $button1 in $timeout secs)" buttons {"$button2","$button1"} default button {"$button1"} giving up after $timeout" -e "end tell")
else
# no timeout.. important questions
buttonreturn=$(osascript -e "tell app "System Events"" -e "activate" -e "display dialog "$message" buttons {"$button2","$button1"} default button {"$button1"}" -e "end tell")
fi
[ "$(echo $buttonreturn | grep "gave up:true")" != "" ] && result=1 # timeout, return default 1
[ "$(echo $buttonreturn | grep -w "$button1")" != "" ] && result=1
[ "$(echo $buttonreturn | grep -w "$button2")" != "" ] && result=2
echo $result
}
#
# core brains
#
findServer()
{
server=$(mount | grep $caspersharename | cut -d'@' -f2- | cut -d':' -f-1)
# if there was no mount with caspershare, don't set afp url
[ "$server" != "" ] && afpurl="afp://$userbackupcredentials@$server/$userbackupshare"
}
mountShare()
{
# this code is for automounting network shares, if running the script via ARD / Casper Imaging / DeployStudio
# lets try to mount network volume, no error checking here... expecting properly formed urls to be passed to mount_afp/smb
secho "Mounting the network share..." 1
share=$(echo "$networkbackuppath" | cut -d'/' -f 4-)
mountpath="/Volumes/$share"
mkdir "$mountpath"
# check if afp or smb
if [ "$afpurl" != "" ]
then
mount_afp "$afpurl" "$mountpath"
mounterror="$?"
fi
if [ "$smburl" != "" ]
then
mount_smb "$smburl" "$mountpath"
mounterror="$?"
fi
if [ "$mounterror" != "0" ]
then
errorHander "There was a problem mounting the network share $share - mount_afp/smb returned $mounterror"
exit 1
fi
backuproot="$mountpath/_UserBackups"
[ ! -d "$backuproot" ] && mkdir "$backuproot"
# end network mount
}
unmountShare()
{
#if we are using a network share, unmount it
cd /
secho "Unmounting $mountpath" 1
sleep 1
umount "$mountpath"
if [ "$?" != "0" ]
then
echo "Forcing unmount"
diskutil unmount force "$mountpath"
if [ "$?" != "0" ]
then
errorHander "There was a problem unmounting $backuproot"
fi
fi
}
excludeUsers()
{
while true
do
# generate a dialog message for exlusions
message=$(
echo -ne "Would you like to exclude any users?
"
echo -ne "
"
echo -ne " Users to backup
"
echo -ne "-------------------------
"
echo -ne "
"
for user in $(ls $userfolder | grep -vE "$excludedusers")
do
echo -ne "$user "
if [ -f "$userfolder/.$user-excluded" ]
then
echo -ne " - EXCLUDED
"
else
echo -ne "
"
fi
done
)
answer=$(askQuestion "$message" "Start Backup" "Exclude Users" 15)
if [ "$answer" == "2" ]
then
for user in $(ls $userfolder | grep -vE "$excludedusers")
do
answer=$(askQuestion "User:
$user
" "Include" "Exclude")
if [ "$answer" == "1" ]
then
# remove exclusion if it exists
[ -f "$userfolder/.$user-excluded" ] && rm "$userfolder/.$user-excluded"
else
# flag them as excluded
touch "$userfolder/.$user-excluded"
fi
done
else
# don't exclude anymore
break
fi
done
}
chooseUserfolder()
{
# in case you User partition isn't found, GUI to select a difference backup source
answer=$(askQuestion "Do you want to backup from:
$userfolder" "Yes" "Choose different..." 20)
if [ "$answer" == "2" ]
then
newuserfolder=$(/usr/bin/osascript << EOT
tell application "Finder"
activate
set theFolder to (choose folder with prompt "Choose another /Users folder to backup:")
return (POSIX path of theFolder)
end tell
EOT)
fi
# if a new location is chosen, use it
[ ! -z "$newuserfolder" ] && userfolder="$newuserfolder"
}
chooseBackup()
{
while true
do
# generate a dialog message for backuplist
message=$(
echo -ne " Available Backups
"
echo -ne "-------------------------
"
echo -ne "
"
[ -f "$backuproot/.DS_Store" ] && rm "$backuproot/.DS_Store" # just in case someone has been poking around in Users)
for backupid in $(ls "$backuproot")
do
echo -ne "$backupid
"
done
)
answer=$(askQuestion "$message" "Select a Backup..." "Quit")
if [ "$answer" == "1" ]
then
for backupid in $(ls "$backuproot")
do
answer=$(askQuestion "BackupID: $backupid" "Next" "Select")
if [ "$answer" == "2" ]
then
# set new backup
uniqueid="$backupid"
backuppath="$backuproot/$uniqueid"
backupsettings="$backuppath/BackupSettings"
return 0
fi
done
else
# jump out
return 2
fi
done
}
backupUsers()
{
[ -d "$altuserpart" ] && userfolder="$altuserpart" # if we find alternate user partion, point at it
if $caspermode
then
# if we wish to run an automated image, skipping backup... touch /Users/.nobackup before executing script
# but double check before skpping the backup.
if [ -f "$userfolder/.nobackup" ]
then
answer=$(askQuestion "### This Mac has been flagged NOT to Backup ###
($userfolder/.nobackup exists)" "Don't Backup - WIPE IT ALL" "Remove flag and continue..." 30)
if [ "$answer" == "1" ]
then
# flag to save us checking for backups to restore post imaging
touch "/tmp/.nobackup-$uniqueid"
return 0
else
# remove the flag and run as normal
rm "$userfolder/.nobackup"
fi
fi
answer=$(askQuestion "Do you want to backup user data on this Mac?" "Backup" "Don't Backup" 20)
if [ "$answer" == "2" ]
then
answer=$(askQuestion "Are you really sure?
All user data will be LOST!" "Backup" "Wipe it ALL!")
if [ "$answer" == "2" ]
then
# flag to save us checking for backups after restore
touch "/tmp/.nobackup-$uniqueid"
return 0
fi
fi
fi
# Check to see if there is an existing backup for this Mac
if [ -d "$backuppath" ]
then
# check to see if it's complete
if [ "$(defaults read $backupsettings BackupComplete)" != "1" ]
then
# if there is an incomplete backup, prompt... default to overwrite.. or give option to abort and sort it out.
answer=$(askQuestion "There is an INCOMPLETE backup, would you like to overwrite it?" "Backup and Overwrite" "Abort" 20)
[ "$answer" == "2" ] && errorHander "There is already a backup @ $backuppath ... Aborting..."
else
# if there is an COMPLETE backup, prompt... default to abort.. or give option to overwrite...
if [ "$(defaults read $backupsettings HasBeenRestored)" == "1" ]
then
answer=$(askQuestion "There is an existing COMPLETE backup which HAS been restored, would you like to overwrite it?" "Backup and Overwrite" "Abort" 20)
[ "$answer" == "2" ] && errorHander "There is already a backup @ $backuppath ... Aborting..."
else
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)
[ "$answer" == "1" ] && errorHander "There is already a backup @ $backuppath ... Aborting..."
fi
fi
# remove the old / existing backup
rm -R "$backuppath"
fi
chooseUserfolder
excludeUsers
answer=$(askQuestion "Would you like to flush user Trashes?" "Flush" "Don't Flush" 5)
if [ "$answer" == "1" ]
then
flushtrash=true
else
flushtrash=false
fi
#answer=$(askQuestion "Would you like to flush user Caches?" "Flush" "Don't Flush" 5)
#if [ "$answer" == "1" ]
#then
# flushcache=true
#else
# flushcache=false
#fi
# make paths and copy the logs to the server so we don't run out of space
mkdir "$backuppath"
mkdir "$backuppath/logs"
cp "$logto/$log" "$backuppath/logs/$log"
rm "$logto/$log"
logto="$backuppath/logs"
secho
secho "Backing up $uniqueid"
secho
secho "Scanning and sizing home folders ..." 2
secho "--------------------------------------"
secho
# open the log in console for more feedback
[ $showlog ] && open "$logto/$log"
usercount=0
totalsize=0
# get all the user folders in /User, process them and get size in bytes
for user in $(ls $userfolder | grep -vE "$excludedusers")
do
if [ ! -f "$userfolder/.$user-excluded" ] # check if they are excluded
then
if $flushtrash
then
secho "Flushing Trash for $user..."
rm -R "$userfolder/$user/.Trash"
fi
#if $flushcache
#then
# secho "Flushing Trash for $user..."
# rm -R "$userfolder/$user/Library/Caches/"
#fi
secho "Sizing home for $user..."
size=$(( $(du -sk "$userfolder/$user" | awk '{print $1}') * 1024 ))
secho "$user - $(($size / 1024 / 1024 )) MB" 2
userarray[$usercount]="$user"
sizearray[$usercount]="$size"
totalsize=$(( $totalsize + $size ))
(( usercount ++ ))
else
secho "$user - EXCLUDED"
fi
done
# get free space on back up disk
backupfree=$(( $(df -k "$backuproot" | tail -n1 | awk '{ print $4 }') * 1024 ))
secho "----------------------------------------------------------------------------------"
secho "Total for $usercount user(s) is $(( $totalsize / 1024 / 1024 )) MBs - Free space on backup volume is $(( backupfree / 1024 / 1024 )) MBs" 2
secho "----------------------------------------------------------------------------------"
if [ $totalsize -gt $backupfree ]
then
answer=$(askQuestion "There may not be enough free space on Backup volume" "Continue" "Stop" 30)
if [ "$answer" == "2" ]
then
# error out ...
errorHander "There is not enough space on the backup volume."
else
secho "WARNING: Ignoring free space error... I sure hope you know what you are doing!"
fi
fi
before=$(date +%s)
backuptogo=$totalsize
for (( i = 0 ; i < $usercount ; i++ ))
do
user="${userarray[$i]}"
usersize="${sizearray[$i]}"
usersettings="$backuppath/Users/$user/UserSettings"
# write some details about the user
defaults write $usersettings UserSize -string $usersize
# this reads the local ds via localonly (reads ds on target disk - not the system we are booted from)
networkusertest=$(dscl -f "$dslocal" localonly -read "/Local/Default/Users/$user" AuthenticationAuthority | grep LocalCachedUser)
if [ "$networkusertest" != "" ]
then
defaults write $usersettings NetworkUser -bool true
usertype="Network User"
else
defaults write $usersettings NetworkUser -bool false
usertype="Local User"
fi
secho
secho "Backing up ($(( $i + 1 ))/$usercount): $user - $(( $usersize / 1024 / 1024 ))/$(( $backuptogo / 1024 / 1024 )) MBs - $usertype" 2
secho "-------------------------------------------------------------------"
mkdir -p "$backuppath/Users/$user"
imagesize=$(( $usersize + (( $usersize / 4 )) )) # add 25% of padding to vol size
# create a sparse image on the backup store
secho "Creating image for $user of size $(( $imagesize / 1024 / 1024 )) MBs..." 1
hdiutil create -size ${imagesize} -type SPARSE -fs HFS+J -volname _backup_$user "$backuppath/Users/$user/$user.sparseimage" 2>> "$logto/$log"
error="$?"
if [ "$error" != "0" ]
then
errorHander "Error creating backup image for $user - hdiutil returned error: $error"
fi
# attach our new image
hdiutil attach -owners on "$backuppath/Users/$user/$user.sparseimage" 2>> "$logto/$log"
if [ "$error" != "0" ]
then
errorHander "Error attaching backup image for $user - hdiutil returned error: $error"
fi
# write our rsync exclude file
for rsyncexclude in ${rsyncexcludes[@]}
do
echo "$rsyncexclude" >> /tmp/rsync-excludes.txt
done
# rsync all data
rsync $rsyncopts -aE --log-file="$logto/$log" --exclude-from="/tmp/rsync-excludes.txt" "$userfolder/$user/" "/Volumes/_backup_$user/$user/"
error="$?"
rm /tmp/rsync-excludes.txt
# rsync error handler - we can ignore a couple of common errors
if [ "$error" != "0" ]
then
case $error in
23)
secho "rsync error 23 - some files were inaccessible (probably in use)"
;;
24)
secho "rsync error 24 - some files disappeared during rsync"
;;
12)
errorHander "Problem backing $user, out of disk space"
;;
*)
errorHander "Problem backing up $user, rsync returned error: $error"
;;
esac
fi
secho "Ejecting /Volumes/_backup_$user ..."
diskutil eject "/Volumes/_backup_$user"
[ "$?" != "0" ] && errorHander "There was a problem ejecting /Volumes/_backup_$user"
# decrement our backupdata to go...
backuptogo=$(( $backuptogo - $usersize ))
done
after=$(date +%s)
totalsecs=$(( $after - $before ))
# echo "Completed in $(($diff / 60)):$(($diff % 60)) min:secs"
totaltime="$(date -r $totalsecs +%M:%S) secs"
[ $showlog ] && killall Console
defaults write $backupsettings BackupDate -string "$(date)"
defaults write $backupsettings TotalTime -string "$totaltime"
defaults write $backupsettings TotalSize -string "$totalsize"
defaults write $backupsettings HasBeenRestored -bool false
defaults write $backupsettings BackupComplete -bool true
secho
secho "=========================================="
secho " Completed $(( $totalsize / 1024 / 1024 )) MBs in $totaltime" 3
secho "=========================================="
secho "finished: $(date)"
secho
secho "NOTE: this script only backs up User folders in $userfolder"
secho " any files outside of this have NOT been backup up."
secho
}
restoreUsers()
{
# check for no backup flag
if [ -f "/tmp/.nobackup-$uniqueid" ]
then
secho "No backup performed for $uniqueid..." 2
rm "/tmp/.nobackup-$uniqueid"
return 0
fi
while true
do
if [ -d "$backuppath" ] # if there is a backup for this mac restore
then
#if its already been restored, error out
if [ "$(defaults read $backupsettings HasBeenRestored)" == "1" ]
then
answer=$(askQuestion "Backup $uniqueid has already been restored" "Continue with Restore" "Choose another backup..." )
if [ "$answer" == "2" ]
then
# to choose another... reset backup path and reloop
backuppath=""
continue
fi
fi
backupdate=$(defaults read $backupsettings BackupDate)
totaltime=$(defaults read $backupsettings TotalTime)
totalsize=$(defaults read $backupsettings TotalSize)
if ! $caspermode
then
# if we are in interactive mode.. present options
# read in backup details and make a dialog message
message=$(
echo -ne "Backup Details
--------------------
$uniqueid
Totalsize: $(( $totalsize / 1024 / 1024 )) MBs
Completed in: $totaltime"
echo -ne "
"
echo -ne "Users
"
echo -ne "------
"
#
# read in all the backups on the store
#
[ -f "$backuppath/Users/.DS_Store" ] && rm "$backuppath/Users/.DS_Store" # just in case someone has been poking around in Users
for backupuserfolder in $(ls "$backuppath/Users/")
do
user=$(basename $backupuserfolder)
usersize=$(defaults read "$backuppath/Users/$backupuserfolder/UserSettings" UserSize)
echo -ne "$user - $(( $usersize / 1024 / 1024 )) MBs
"
done
echo -ne "
"
)
answer=$(askQuestion "$message" "Restore" "Choose another backup..." )
if [ "$answer" == "2" ]
then
# to choose another... reset backup path and reloop
backuppath=""
continue
fi
fi
# ask to remove backup, do it before we start the restore so it can be unattended
answer=$(askQuestion "Would you like to delete the backup once it has been restore successfully?" "Delete Backup" "Keep" 20)
if [ "$answer" == "1" ]
then
removebackup=true
else
removebackup=false
fi
# do the restore
cp "$logto/$log" "$backuppath/logs/$log"
rm "$logto/$log"
logto="$backuppath/logs"
# open the log in console for more feedback
[ $showlog ] && open "$logto/$log"
secho "$uniqueid - Restore size: $(( $totalsize / 1024 / 1024 )) MBs" 2
secho
before=$(date +%s)
backuptogo=$totalsize
[ -f "$backuppath/Users/.DS_Store" ] && rm "$backuppath/Users/.DS_Store" # just in case someone has been poking around in Users
for userroot in "$backuppath/Users/"*
do
user=$(basename $userroot)
usersettings="$backuppath/Users/$user/UserSettings"
usersize=$(defaults read $usersettings UserSize)
if [ "$(defaults read $usersettings NetworkUser)" == "1" ]
then
usertype="Network"
else
usertype="Local"
fi
secho
secho "Restoring: $user $(( $usersize / 1024 / 1024 ))/$(( $backuptogo / 1024 / 1024 )) MBs - $usertype account" 2
secho "-------------------------------------------------------------------"
hdiutil attach -owners on "$userroot/$user.sparseimage" 2>> "$logto/$log"
error="$?"
if [ "$error" != "0" ]
then
errorHander "Error attaching backup image for $user - hdiutil returned error: $error"
fi
rsync $rsyncopts -aE --log-file="$logto/$log" "/Volumes/_backup_$user/$user/" "$restoreto/$user/"
error="$?"
# rsync error handler
if [ "$error" != "0" ]
then
case $error in
23)
secho "rsync error 23 - some files were inaccessible (probably in use)"
;;
24)
secho "rsync error 24 - some files disappeared during rsync"
;;
12)
errorHander "Problem backing $user, out of disk space"
;;
*)
errorHander "Problem backing up $user, rsync returned error: $error"
;;
esac
fi
secho "Ejecting /Volumes/_backup_$user ..."
diskutil eject "/Volumes/_backup_$user"
[ "$?" != "0" ] && errorHander "There was a problem ejecting /Volumes/_backup_$user"
# decrement data
backuptogo=$(( $backuptogo - $usersize ))
done
after=$(date +%s)
totalsecs=$(( $after - $before ))
totaltime="$(date -r $totalsecs +%M:%S) secs"
defaults write $backupsettings HasBeenRestored -bool true
secho
secho "=========================================="
secho " Completed $(( $totalsize / 1024 / 1024 )) MBs in $totaltime" 3
secho "=========================================="
secho "finished: $(date)"
cp "$logto"/*.log "$target/Library/Logs/" # copy logs to target when done
[ $showlog ] && killall Console
# remove them when done
if $removebackup
then
# delete the backup
secho "Deleting $backuppath" 2
while [ -d "$backuppath" ]
do
# network shares have some locks / trashes that hold up proceedings, just keep hitting it until it dies
rm -R "$backuppath"
sleep 1
done
else
defaults write $backupsettings HasBeenRestored -bool true
fi
break
else
# no backups found for restore
if $caspermode
then
secho "There are no backups for $uniqueid" 4
break
else
if [ ! "$(ls $backuproot)" ] # check if the backup folder is empty
then
secho "There are no backups on $backuproot"
break
fi
# interactive mode choose another backup and loop back
chooseBackup
# we want to quit?
[ "$?" == "2" ] && break
fi
fi
done
}
################################
# Begin!
################################
datestamp=$(date "+%F_%H-%M-%S")
logto="/tmp"
log="$scriptname-$datestamp.log"
if $debug
then
# dump debug logs to /
set -xv; exec 1>/BackupRestoreUsers-DEBUG-$datestamp.log 2>&1
fi
dscl="/usr/bin/dscl" ### test - we should run dscl from current booted os. assumed that netboot system will be > system if upgrading.
dslocal="/var/db/dslocal/nodes/Default" # path to local directory data
target="$1" # casper passes .. $1 - target, $2 - computername, $3 username --- we only need target path.
[ "$target" == "/" ] && target="" # if we are targetting / - set target to "" so we don't end up with //Users
userfolder="$target$userfolder" # eg. /Volumes/MacHD/Users
restoreto="$target$restoreto" #
dslocal="$target$dslocal" # etc..
# if there we are in cdpmode, find the cdp server, and set url
[ $cdpmode ] && findServer
# just drop afp or smb url into this variable
networkbackuppath="$afpurl$smburl"
# uniqueid - serial or MAC address of en0
uniqueid=$(ioreg -c "IOPlatformExpertDevice" | awk -F '"' '/IOPlatformSerialNumber/ {print $4}')
[ "$uniqueid" == "" ] && uniqueid=$(ifconfig en0 | awk ' /ether/ {print $2}') # if no serial, fallback to MAC address
# set the IFS to cr
OLDIFS=$IFS
IFS=$'
'
#
# Lets Go!
#
secho
secho "$scriptname $mode $version - $uniqueid" 1
secho "================================================="
secho "started: $(date)"
secho
# if we have a network path, mount it
[ "$networkbackuppath" != "" ] && mountShare
[ ! -d "$backuproot" ] && errorHander "I can't find $backuproot"
backuppath="$backuproot/$uniqueid"
backupsettings="$backuppath/BackupSettings"
#
# Core handler .. parse mode line etc
#
if $caspermode
then
# mode is hard coded
case $mode in
backup )
backupUsers
;;
restore )
restoreUsers
;;
esac
else
# interactive mode called by wrappers
case $2 in
--backup )
backupUsers
;;
--restore )
restoreUsers
;;
* )
usage
;;
esac
fi
# unmount network share
[ "$networkbackuppath" != "" ] && unmountShare
IFS=$OLDIFS
exit
Posted on 03-24-2014 08:22 PM
this is good did you ever look at @rustymyers script https://github.com/rustymyers/BackupRestore
Posted on 03-24-2014 10:31 PM
I sure did. Rusty's scripts are great I borrowed a few ideas..!
Posted on 03-25-2014 01:30 AM
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!
man fstab
Posted on 03-25-2014 01:40 AM
I'm sure he does. However the fstab option fails completely with CoreStorage based drives, such as Fusion drives.
Posted on 03-25-2014 02:07 AM
@loceee, incase FSTAB is not something you've looked at; http://macmule.com/2012/07/31/how-to-use-fstab-within-a-casper-imaging-workflow/
Posted on 03-25-2014 04:05 AM
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.
Posted on 03-25-2014 03:51 PM
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.
Posted on 03-26-2014 06:25 PM
@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.
Posted on 03-26-2014 06:43 PM
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!
Posted on 03-30-2014 06:52 PM
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.
Posted on 04-06-2014 06:13 PM
https://interpublic.box.com/s/quqoz45h54v7k1y8ktrj
Link updated as I am going to put some more scripts in here soon.
Posted on 02-18-2015 03:43 AM
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
Posted on 02-18-2015 03:04 PM
@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
Posted on 10-22-2016 08:11 PM
Hi Loceee,
I was wondering if you still have your instruction on how to setup your BackupRestoreUsers.sh script?
Thank you,
ArielN
Posted on 11-22-2016 07:52 AM
@loceee 5 years on and i am still learning from your scripts Locky!
keep up the good work mate!