Skip to content

Instantly share code, notes, and snippets.

@scriptsandthings
Forked from acidprime/makeardlist.sh
Created February 1, 2020 15:43
Show Gist options
  • Save scriptsandthings/b799595136b3964dc594ad2adec28dc6 to your computer and use it in GitHub Desktop.
Save scriptsandthings/b799595136b3964dc594ad2adec28dc6 to your computer and use it in GitHub Desktop.
Apple Remote Desktop List creator from CSV
#!/bin/bash
declare -x uuidgen="/usr/bin/uuidgen"
declare -x plistbuddy="/usr/libexec/PlistBuddy"
declare -x awk="/usr/bin/awk"
declare -x cat="/bin/cat"
declare -x cp="/bin/cp"
declare -x dscl="/usr/bin/dscl"
declare -x defaults="/usr/bin/defaults"
declare -x du="/usr/bin/du"
declare -x id="/usr/bin/id"
declare -x ntpdate="ntpdate"
declare -x scutil="/usr/sbin/scutil"
declare -x perl="/usr/bin/perl"
declare -x basename="/usr/bin/basename"
declare -x date="/bin/date"
declare -x defaults="/usr/bin/defaults"
declare -x dscl="/usr/bin/dscl"
declare -x find="/usr/bin/find"
declare -x groups="/usr/bin/groups"
declare -x ls="/bin/ls"
declare -x mv="/bin/mv"
declare -x rm="/bin/rm "
declare -x rmdir="/bin/rmdir"
declare -x sudo="/usr/bin/sudo"
declare -x serversetup="/System/Library/ServerSetup/serversetup"
declare -x plistbuddy="/usr/libexec/PlistBuddy"
# -- Runtime varibles
declare -x REQCMDS="$awk $dscl $deaults $ntpdate $perl $scutil"
declare -x SCRIPT="${0##*/}" ; SCRIPTNAME="${SCRIPT%%\.*}"
declare -x SCRIPTPATH="$0" RUNDIRECTORY="${0%/*}"
declare -x SYSTEMVERSION="/System/Library/CoreServices/SystemVersion.plist"
declare -x OSVER="$("$defaults" read "${SYSTEMVERSION%.plist}" ProductVersion )"
#declare -x CONFIGFILE="${RUNDIRECTORY:?}/${SCRIPTNAME}.conf"
declare -x BUILDVERSION="2009051"
[ "$EUID" != 0 ] && printf "%s\n" "This script requires root access ($EUID)!" && exit 1
# -- Start the script log
# Set to "VERBOSE" for more logging prior to using -v
declare -x LOGLEVEL="NORMAL" SCRIPTLOG="/Library/Logs/${SCRIPT%%\.*}.log"
declare -i CURRENT_LOG_SIZE="$("$du" -hm "${SCRIPTLOG:?}" |
"$awk" '/^[0-9]/{print $1;exit}')"
if [ ${CURRENT_LOG_SIZE:=0} -gt 50 ] ; then
"$rm" "$SCRIPTLOG"
statusMessage "LOGSIZE:$CURRENT_LOG_SIZE, too large removing"
fi
exec 2>>"${SCRIPTLOG:?}" # Redirect standard error to log file
# Strip any extention from scriptname and log stderr to script log
if [ -n ${SCRIPTLOG:?"The script log has not been specified"} ] ; then
printf "%s\n" \
"STARTED:$SCRIPTNAME:EUID:$EUID:$("$date" +%H:%M:%S): Mac OS X $OSVER:BUILD:$BUILDVERSION" >>"${SCRIPTLOG:?}"
printf "%s\n" "Log file is: ${SCRIPTLOG:?}"
fi
statusMessage() { # Status message function with type and now color!
# Requires SCRIPTLOG STATUS_TYPE=1 STATUS_MESSAGE=2
declare date="${date:="/bin/date"}"
declare DATE="$("$date" -u "+%Y-%m-%d")"
declare STATUS_TYPE="$1" STATUS_MESSAGE="$2"
if [ "$ENABLECOLOR" = "YES" ] ; then
# Background Color
declare REDBG="41" WHITEBG="47" BLACKBG="40"
declare YELLOWBG="43" BLUEBG="44" GREENBG="42"
# Foreground Color
declare BLACKFG="30" WHITEFG="37" YELLOWFG="33"
declare BLUEFG="36" REDFG="31"
declare BOLD="1" NOTBOLD="0"
declare format='\033[%s;%s;%sm%s\033[0m\n'
# "Bold" "Background" "Forground" "Status message"
printf '\033[0m' # Clean up any previous color in the prompt
else
declare format='%s\n'
fi
# Function only seems to work on intel and higher.
showUIDialog(){
statusMessage header "FUNCTION: # $FUNCNAME" ; unset EXITVALUE TRY
"$killall" -HUP "System Events" 2>/dev/null
declare -x UIMESSAGE="$1"
"$osascript" <<EOF
try
with timeout of 0.1 seconds
tell application "System Events"
set UIMESSAGE to (system attribute "UIMESSAGE") as string
activate
display dialog UIMESSAGE with icon 2 giving up after "3600" buttons "Dismiss" default button "Dismiss"
end tell
end timeout
end try
EOF
return 0
} # END showUIDialog()
case "${STATUS_TYPE:?"Error status message with null type"}" in
progress) \
[ -n "$LOGLEVEL" ] &&
printf $format $NOTBOLD $WHITEBG $BLACKFG "PROGRESS:$STATUS_MESSAGE" ;
printf "%s\n" "$DATE:PROGRESS: $STATUS_MESSAGE" >> "${SCRIPTLOG:?}" ;;
# Used for general progress messages, always viewable
notice) \
printf "%s\n" "$DATE:NOTICE:$STATUS_MESSAGE" >> "${SCRIPTLOG:?}" ;
[ -n "$LOGLEVEL" ] &&
printf $format $NOTBOLD $YELLOWBG $BLACKFG "NOTICE :$STATUS_MESSAGE" ;;
# Notifications of non-fatal errors , always viewable
error) \
printf "%s\n\a" "$DATE:ERROR:$STATUS_MESSAGE" >> "${SCRIPTLOG:?}" ;
[ -n "$LOGLEVEL" ] &&
printf $format $NOTBOLD $REDBG $YELLOWFG "ERROR :$STATUS_MESSAGE" ;;
# Errors , always viewable
verbose) \
printf "%s\n" "$DATE:VERBOSE: $STATUS_MESSAGE" >> "${SCRIPTLOG:?}" ;
[ "$LOGLEVEL" = "VERBOSE" ] &&
printf $format $NOTBOLD $WHITEBG $BLACKFG "VERBOSE :$STATUS_MESSAGE" ;;
# All verbose output
header) \
[ "$LOGLEVEL" = "VERBOSE" ] &&
printf $format $NOTBOLD $BLUEBG $BLUEFG "VERBOSE :$STATUS_MESSAGE" ;
printf "%s\n" "$DATE:PROGRESS: $STATUS_MESSAGE" >> "${SCRIPTLOG:?}" ;;
# Function and section headers for the script
passed) \
[ "$LOGLEVEL" = "VERBOSE" ] &&
printf $format $NOTBOLD $GREENBG $BLACKFG "SANITY :$STATUS_MESSAGE" ;
printf "%s\n" "$DATE:SANITY: $STATUS_MESSAGE" >> "${SCRIPTLOG:?}" ;;
# Sanity checks and "good" information
graphical) \
[ "$GUI" = "ENABLED" ] &&
showUIDialog "$STATUS_MESSAGE" ;;
esac
return 0
} # END statusMessage()
die() { # die Function
statusMessage header "FUNCTION: # $FUNCNAME" ; unset EXITVALUE
declare LASTDIETYPE="$1" LAST_MESSAGE="$2" LASTEXIT="$3"
declare LASTDIETYPE="${LASTDIETYPE:="UNTYPED"}"
if [ ${LASTEXIT:="192"} -gt 0 ] ; then
statusMessage error "$LASTDIETYPE :$LAST_MESSAGE:EXIT:$LASTEXIT"
# Print specific error message in red
else
statusMessage verbose "$LASTDIETYPE :$LAST_MESSAGE:EXIT:$LASTEXIT"
# Print specific error message in white
fi
statusMessage verbose "COMPLETED:$SCRIPT IN $SECONDS SECONDS"
"$killall" "System Events"
exit "${LASTEXIT}" # Exit with last status or 192 if none.
return 1 # Should never get here
} # END die()
cleanUp() { # -- Clean up of our inportant sessions variables and functions.
statusMessage header "FUNCTION: # $FUNCNAME" ; unset EXITVALUE
statusMessage verbose "TIME: $SCRIPT ran in $SECONDS seconds"
unset -f ${!check*}
[ "${ENABLECOLOR:-"ENABLECOLOR"}" = "YES" ] && printf '\033[0m' # Clear Color
if [ "$PPID" == 1 ] ; then # LaunchD is always PID 1 in 10.4+
: # Future LaunchD code
fi
exec 2>&- # Reset the error redirects
return 0
} # END cleanUp()
# Check script options
statusMessage header "GETOPTS: Processing script $# options:$@"
# ABOVE: Check to see if we are running as a postflight script,the installer creates $SCRIPT_NAME
[ $# = 0 ] && statusMessage verbose "No options given"
# If we are not running postflight and no parameters given, print usage to stderr and exit status 1
while getopts vCud:f: SWITCH ; do
case $SWITCH in
v ) export LOGLEVEL="VERBOSE" ;;
C ) export ENABLECOLOR="YES" ;;
u ) export GUI="ENABLED" ;;
d ) export SAVE_DIRECTORY="$OPTARG" ;;
f ) export CSV_FILE="$OPTARG" ;;
esac
done # END getopts
checkLineEndings(){
declare -i FUNCSECONDS="$SECONDS" # Capture start time
declare FILE_TO_CHECK="$1"
statusMessage header "FUNCTION: # ${FUNCNAME}" ; unset EXITVALUE
if [ -f "$FILE_TO_CHECK" ] ; then
if ! $perl -ne "exit 1 if m/\r\n/;" "$FILE_TO_CHECK" ; then
statusMessage notice \
"Incorrect line endings detected (probobly due to Mircosoft edit)"
statusMessage notice \
"Backup: $CSV_FILE.bak"
$cp -f "$FILE_TO_CHECK" "$FILE_TO_CHECK".bak
statusMessage verbose 'Resetting line endings \r/\n/ to \n'
$perl -i -pe 's/\r/\n/g' "$FILE_TO_CHECK"
elif ! $perl -ne "exit 1 if m/\r/;" "$FILE_TO_CHECK" ; then
statusMessage notice \
"Incorrect line endings detected (DOS?) fixing backup: $FILE_TO_CHECK.bak"
$cp -f "$FILE_TO_CHECK" "$FILE_TO_CHECK".bak
statusMessage verbose 'Resetting line endings \r/\n/'
$perl -i -pe 's/\r/\n/g' "$FILE_TO_CHECK"
fi
else
statusMessage error "File: $FILE_TO_CHECK does not exist"
die ERROR "Invalid file specified: $FILE_TO_CHECK"
fi
statusMessage verbose "TIME:$FUNCNAME:Took $FUNCTIME seconds to EXIT:$EXITVALUE"
}
checkCommands() { # CHECK_CMDS Required Commands installed check using the REQCMDS varible.
declare -i FUNCSECONDS="$SECONDS" # Capture start time
statusMessage header "FUNCTION: # ${FUNCNAME}" ; unset EXITVALUE
declare REQCMDS="$1"
for RQCMD in ${REQCMDS:?} ; do
if [ -x "$RQCMD" ] ; then
statusMessage passed "PASSED: $RQCMD is executable"
else
# Export the command Name to the die status message can refernce it"
export RQCMD ; return 1
fi
done
return 0
declare -i FUNCTIME=$(( ${SECONDS:?} - ${FUNCSECONDS:?} ))
[ "${FUNCTIME:?}" != 0 ] &&
statusMessage verbose "TIME:$FUNCNAME:Took $FUNCTIME seconds to EXIT:$EXITVALUE"
} # END checkCommands()
checkSystemVersion() {
# CHECK_OS Read the /Sys*/Lib*/CoreSer*/S*Version.plist value for OS version
statusMessage header "FUNCTION: # ${FUNCNAME}" ; unset EXITVALUE
declare OSVER="$1"
case "${OSVER:?}" in
10.0* | 10.1* | 10.2* | 10.3*) \
die ERROR "$FUNCNAME: Unsupported OS version: $OSVER." 192 ;;
10.4*) \
statusMessage passed "CHECK_OS: OS check: $OSVER successful!";
return 0;;
10.5*) \
statusMessage passed "CHECK_OS: OS check: $OSVER successful!";
return 0;;
10.6*) \
die ERROR "$FUNCNAME:$LINENO Unsupported OS:$OSVER is too new." 192 ;;
*) \
die ERROR "CHECK_OS:$LINENO Unsupported OS:$OSVER unknown error" 192 ;;
esac
return 1
checkDNS(){
declare SERVER_HOSTNAME="$1" SERVER_IP="$2"
statusMessage header "$SERVER_HOSTNAME ($SERVER_IP)"
if host "${SERVER_HOSTNAME:-null}" &>/dev/null ; then
statusMessage passed "FORWARD[A]:$SERVER_HOSTNAME ($SERVER_IP)"
else
statusMessage error "FORWARD[A]:$SERVER_HOSTNAME ($SERVER_IP)"
fi
if host "${SERVER_IP:-null}" &>/dev/null ; then
statusMessage passed "REVERSE[PTR]:$SERVER_HOSTNAME ($SERVER_IP)"
else
statusMessage error "REVERSE[PTR]:$SERVER_HOSTNAME ($SERVER_IP)"
fi
printf "\n"
}
} # END checkSystemVersion()
statusMessage verbose "Using file: $CSV_FILE"
checkLineEndings "$CSV_FILE"
declare -x PLIST="${SAVE_DIRECTORY:="."}/${CSV_FILE%%.csv}.plist"
$plistbuddy -c "Add :listName string ${CSV_FILE%%.csv}" "$PLIST"
$plistbuddy -c "Add :uuid string $($uuidgen)" "$PLIST"
OLDIFS="$IFS"
IFS=$'\n'
declare -ix N=0
for LINE in `$cat "${CSV_FILE:?"Must specify a file with -f"}"` ; do
let LINE_NUM++
if [ $LINE_NUM -eq 1 ] ; then
statusMessage progress "Skipping first line:$LINE"
continue
fi
# statusMessage verbose "processing $LINE"
declare -x SITE_ID="$(printf "$LINE" | $awk -F',' '{print $1;exit}')"
declare -x SERVER_ETHERNET[0]="$(printf "$LINE" | $awk -F',' '{print $12;exit}')"
declare -x SERVER_ETHERNET[1]="$(printf "$LINE" | $awk -F',' '{print $13;exit}')"
declare -x SERVER_HW_SERIAL="$(printf "$LINE" | $awk -F',' '{print substr($15,1,8);exit}')"
# AdminUser
declare -x ADMIN_USER_EXISTS="false"
declare -x ADMIN_USER_NAME="$(printf "$LINE" | $awk -F',' '{print $16;exit}')"
declare -x ADMIN_USER_PASSWORD="$(printf "$LINE" | $awk -F',' '{print $17;exit}')"
declare -x ADMIN_USER_REALNAME="$ADMIN_USER_NAME"
declare -ix ADMIN_USER_UID="501"
# HostName
declare -x HOST_NAME="$(printf "$LINE" | $awk -F',' '{print $4;exit}')"
statusMessage verbose "HOST_NAME: $HOST_NAME"
# Bonjour
declare -x BONJOUR_ENABLED="true"
declare -x BONJOUR_NAME="$(printf $HOST_NAME | $awk -F'.' '{print $1;exit}')"
# ComputerName
declare -x COMPUTER_NAME="$(printf $HOST_NAME | $awk -F'.' '{print $1;exit}')"
# DS
declare -x DS_TYPE="Standalone"
# DefaultGroupName
declare -x DEFAULT_GROUP_LONGNAME="Work Group"
declare -x DEFAULT_GROUP_SHORTNAME="workgroup"
# EnableARD
declare -x ENABLE_ARD="true"
# InstallLanguage
declare -x INSTALL_LAUNGUAGE="English"
# Keyboard
declare -x KEYBOARD_DEFAULT_FORMAT="0"
declare -x KEYBOARD_DEFAULT_SCRIPT="0"
declare -x KEYBOARD_RESNAME="U.S."
declare -ix KEYBOARD_SCRIPTID="0"
declare -ix KEYBOARD_KBRESID="0"
# NetworkInterfaces
declare -ax INTERFACE_ACTIVEAT[0]="false"
declare -ax INTERFACE_ACTIVETCPIP[0]="true"
declare -ax DNS_DOMAIN[0]="$(printf "$LINE" | $awk -F',' '{print $11;exit}')"
declare -ax DNS_SERVER_PRIMARY[0]="$(printf "$LINE" | $awk -F',' '{print $8;exit}')"
declare -ax DNS_SERVER_SECONDARY[0]="$(printf "$LINE" | $awk -F',' '{print $9;exit}')"
declare -ax DNS_SERVER_TERIARY[0]="$(printf "$LINE" | $awk -F',' '{print $10;exit}')"
declare -ax INTERFACE_DEVICENAME[0]="en0"
declare -ax ETHERNET_ADDRESS[0]="00:00:00:00:00:00"
declare -ax IPV6_TYPE[0]="3"
declare -ax PORT_NAME[0]="Ethernet 1"
declare -ax IP_ADDRESS[0]="$(printf "$LINE" | $awk -F',' '{print $5;exit}')"
declare -ax ROUTER[0]="$(printf "$LINE" | $awk -F',' '{print $7;exit}')"
declare -ax SUBNET_MASK[0]="$(printf "$LINE" | $awk -F',' '{print $6;exit}')"
declare -ax INTERFACE_TYPE[0]="Manual Configuration"
declare -ax INTERFACE_ACTIVEAT[1]="false"
declare -ax INTERFACE_ACTIVETCPIP[1]="false"
declare -ax INTERFACE_DEVICENAME[1]="en1"
declare -ax ETHERNET_ADDRESS[1]="00:00:00:00:00:00"
declare -ax IPV6_TYPE[1]="3"
declare -ax PORT_NAME[1]="Ethernet 2"
declare -ax IP_ADDRESS[1]="192.168.50.90"
declare -ax ROUTER[1]="0.0.0.0"
declare -ax SUBNET_MASK[1]="255.255.255.0"
declare -ax INTERFACE_TYPE[1]="Manual Configuration"
# PrimaryLanguage
declare -x PRIMARY_LANGUAGE="English"
# Serial Number
declare -x SERIAL_NUMBER="$(printf "$LINE" | $awk -F',' '{print $14;exit}')"
# Service NTP
declare -x HOST_NTP="false"
declare -x HOST_NTP_SERVER="$(printf "$LINE" | $awk -F',' '{print $21;exit}')"
declare -x USE_NTP="true"
# TimeZone
declare -x TIME_ZONE="$(printf "$LINE" | $awk -F',' '{print $20;exit}')"
# Version Number
declare -ix VERSION_NUMBER="3"
declare SERVER_MAC="$(printf "%s" "${SERVER_ETHERNET[0]}" | $awk 'BEGIN{FS=""}{print $1$2":"$3$4":"$5$6":"$7$8":"$9$10":"$11$12}')"
$plistbuddy -c "Add :items array" "$PLIST"
$plistbuddy -c "Add :items: dict" "$PLIST"
$plistbuddy -c "Add :items:$N::hardwareAddress string $SERVER_MAC" "$PLIST"
$plistbuddy -c "Add :items:$N::hostname string $HOST_NAME" "$PLIST"
$plistbuddy -c "Add :items:$N::name string $HOST_NAME" "$PLIST"
$plistbuddy -c "Add :items:$N::networkAddress string ${IP_ADDRESS[0]}" "$PLIST"
$plistbuddy -c "Add :items:$N::preferHostname bool false" "$PLIST"
let N++
done
OLDIFS="$IFS"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment