Skip to content

Instantly share code, notes, and snippets.

@bacongravy
Created February 1, 2019 21:39
Show Gist options
  • Save bacongravy/9b3b38d4c3bd9a4f13ba287df4106034 to your computer and use it in GitHub Desktop.
Save bacongravy/9b3b38d4c3bd9a4f13ba287df4106034 to your computer and use it in GitHub Desktop.
The origin of the `macinbox` project
#!/bin/sh -e
INSTALLER_APP="$1"
OUTPUT_PATH="$2"
DISK_SIZE=${DISK_SIZE:-64}
USER_NAME=${USER_NAME:-vagrant}
FULL_NAME=${FULL_NAME:-$USER_NAME}
PASS_WORD=${PASS_WORD:-$USER_NAME}
AUTO_LOGIN=${AUTO_LOGIN:-true}
#####
log_info() {
echo " \033[0;32m-- $*\033[0m" 1>&2
}
log_error() {
echo " \033[0;31m-- $*\033[0m" 1>&2
}
bail() {
log_error "$1"
exit 1
}
if [ $(id -u) -ne 0 -o -z "${SUDO_USER}" ]; then
bail "Script must be run as root with sudo."
fi
#####
VMWARE_FUSION_APP="/Applications/VMware Fusion.app"
if [ -z "${VMWARE_FUSION_APP}" -o ! -e "${VMWARE_FUSION_APP}" ]; then
bail "VMware Fusion not found."
fi
if [ -z "${INSTALLER_APP}" -o ! -e "${INSTALLER_APP}" ]; then
bail "Installer app not found."
fi
if [ -z "${OUTPUT_PATH}" ]; then
bail "No output path specified."
fi
######
TEMP_DIR="$(/usr/bin/mktemp -d -t prepare_vmdk)"
INSTALL_INFO_PLIST="${INSTALLER_APP}/Contents/SharedSupport/InstallInfo.plist"
SCRATCH_IMAGE="${TEMP_DIR}/scratch.sparseimage"
SCRATCH_MOUNTPOINT="${TEMP_DIR}/scratch_mountpoint"
SCRATCH_INSTALLER_CONFIGURATION_FILE="${SCRATCH_MOUNTPOINT}/private/var/db/.InstallerConfiguration"
SCRATCH_SUDOERS_D="${SCRATCH_MOUNTPOINT}/private/etc/sudoers.d"
SCRATCH_RC_INSTALLER_CLEANUP="${SCRATCH_MOUNTPOINT}/private/etc/rc.installer_cleanup"
SCRATCH_RC_VAGRANT="${SCRATCH_MOUNTPOINT}/private/etc/rc.vagrant"
SCRATCH_LAUNCHD_DISABLED_PLIST="${SCRATCH_MOUNTPOINT}/private/var/db/com.apple.xpc.launchd/disabled.plist"
SCRATCH_VMHGFS_FILESYSTEM_RESOURCES="${SCRATCH_MOUNTPOINT}/Library/Filesystems/vmhgfs.fs/Contents/Resources"
SCRATCH_SPC_KEXTPOLICY="${SCRATCH_MOUNTPOINT}/private/var/db/SystemPolicyConfiguration/KextPolicy"
VMWARE_TOOLS_IMAGE="${VMWARE_FUSION_APP}/Contents/Library/isoimages/darwin.iso"
VMWARE_TOOLS_MOUNTPOINT="${TEMP_DIR}/vmware_tools_mountpoint"
VMWARE_TOOLS_PACKAGE="${VMWARE_TOOLS_MOUNTPOINT}/Install VMware Tools.app/Contents/Resources/VMware Tools.pkg"
VMWARE_TOOLS_PACKAGE_DIR="${TEMP_DIR}/vmware_tools_package"
VMWARE_RAWDISKCREATOR="${VMWARE_FUSION_APP}/Contents/Library/vmware-rawdiskCreator"
VMWARE_VDISKMANAGER="${VMWARE_FUSION_APP}/Contents/Library/vmware-vdiskmanager"
#####
mkdir "${SCRATCH_MOUNTPOINT}"
mkdir "${VMWARE_TOOLS_MOUNTPOINT}"
cleanup() {
trap - EXIT INT TERM
hdiutil detach -quiet -force "${SCRATCH_MOUNTPOINT}" > /dev/null 2>&1 || true
hdiutil detach -quiet -force "${VMWARE_TOOLS_MOUNTPOINT}" > /dev/null 2>&1 || true
rm -rf "${TEMP_DIR}" > /dev/null 2>&1 || true
[[ $SIG == EXIT ]] || kill -$SIG $$ || true
}
for sig in EXIT INT TERM; do
trap "SIG=$sig; cleanup;" $sig
done
#####
log_info "Checking macOS versions..."
if [ ! -e "${INSTALL_INFO_PLIST}" ]; then
bail "InstallInfo.plist not found in installer app bundle"
fi
INSTALLER_OS_VERS=$(/usr/libexec/PlistBuddy -c 'Print :System\ Image\ Info:version' "${INSTALL_INFO_PLIST}")
INSTALLER_OS_VERS_MAJOR=$(echo ${INSTALLER_OS_VERS} | awk -F "." '{print $1}')
INSTALLER_OS_VERS_MINOR=$(echo ${INSTALLER_OS_VERS} | awk -F "." '{print $2}')
INSTALLER_OS_VERS_PATCH=$(echo ${INSTALLER_OS_VERS} | awk -F "." '{print $3}')
log_info "Installer macOS version detected: ${INSTALLER_OS_VERS_MAJOR}.${INSTALLER_OS_VERS_MINOR}.${INSTALLER_OS_VERS_PATCH}"
HOST_OS_VERS=$(sw_vers -productVersion)
HOST_OS_VERS_MAJOR=$(echo ${HOST_OS_VERS} | awk -F "." '{print $1}')
HOST_OS_VERS_MINOR=$(echo ${HOST_OS_VERS} | awk -F "." '{print $2}')
HOST_OS_VERS_PATCH=$(echo ${HOST_OS_VERS} | awk -F "." '{print $3}')
log_info "Host macOS version detected: ${HOST_OS_VERS_MAJOR}.${HOST_OS_VERS_MINOR}.${HOST_OS_VERS_PATCH}"
if [ "${INSTALLER_OS_VERS_MAJOR}" != "${HOST_OS_VERS_MAJOR}" ] || [ "${INSTALLER_OS_VERS_MINOR}" != "${HOST_OS_VERS_MINOR}" ]; then
bail "Host OS version and installer OS version do not match"
fi
#####
log_info "Creating and attaching a new scratch image..."
hdiutil create -size "${DISK_SIZE}g" -type SPARSE -fs HFS+J -volname "Macintosh HD" -uid 0 -gid 80 -mode 1775 "${SCRATCH_IMAGE}"
hdiutil attach "${SCRATCH_IMAGE}" -mountpoint "${SCRATCH_MOUNTPOINT}" -nobrowse -owners on
#####
log_info "Installing macOS..."
installer -verboseR -dumplog -pkg "${INSTALL_INFO_PLIST}" -target "${SCRATCH_MOUNTPOINT}"
#####
log_info "Installing the VMware Tools..."
hdiutil attach "${VMWARE_TOOLS_IMAGE}" -mountpoint "${VMWARE_TOOLS_MOUNTPOINT}" -nobrowse
pkgutil --expand "${VMWARE_TOOLS_PACKAGE}" "${VMWARE_TOOLS_PACKAGE_DIR}"
ditto -x -z "${VMWARE_TOOLS_PACKAGE_DIR}/files.pkg/Payload" "${SCRATCH_MOUNTPOINT}"
mkdir -p "${SCRATCH_VMHGFS_FILESYSTEM_RESOURCES}"
ln -s "/Library/Application Support/VMware Tools/mount_vmhgfs" "${SCRATCH_VMHGFS_FILESYSTEM_RESOURCES}/"
sqlite3 "${SCRATCH_SPC_KEXTPOLICY}" <<-EOF
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE kext_load_history_v3 ( path TEXT PRIMARY KEY, team_id TEXT, bundle_id TEXT, boot_uuid TEXT, created_at TEXT, last_seen TEXT, flags INTEGER );
INSERT INTO kext_load_history_v3 VALUES('/Library/Extensions/VMwareGfx.kext','EG7KH642X6','com.vmware.kext.VMwareGfx','7BD644E2-74AB-4310-8E56-D7CE28DC8CDB','2018-01-25 08:16:36','2018-01-25 08:17:47',7);
INSERT INTO kext_load_history_v3 VALUES('/Library/Application Support/VMware Tools/vmhgfs.kext','EG7KH642X6','com.vmware.kext.vmhgfs','7BD644E2-74AB-4310-8E56-D7CE28DC8CDB','2018-01-25 08:16:43','2018-01-25 08:18:26',13);
INSERT INTO kext_load_history_v3 VALUES('/Library/Application Support/VMware Tools/vmmemctl.kext','EG7KH642X6','com.vmware.kext.vmmemctl','7BD644E2-74AB-4310-8E56-D7CE28DC8CDB','2018-01-25 08:16:43','2018-01-25 08:18:26',5);
CREATE TABLE kext_policy ( team_id TEXT, bundle_id TEXT, allowed BOOLEAN, developer_name TEXT, flags INTEGER, PRIMARY KEY (team_id, bundle_id) );
INSERT INTO kext_policy VALUES('EG7KH642X6','com.vmware.kext.VMwareGfx',1,'VMware, Inc.',1);
INSERT INTO kext_policy VALUES('EG7KH642X6','com.vmware.kext.vmmemctl',1,'VMware, Inc.',1);
INSERT INTO kext_policy VALUES('EG7KH642X6','com.vmware.kext.vmhgfs',1,'VMware, Inc.',1);
CREATE TABLE kext_policy_mdm ( team_id TEXT, bundle_id TEXT, allowed BOOLEAN, payload_uuid TEXT, PRIMARY KEY (team_id, bundle_id) );
CREATE TABLE settings ( name TEXT, value TEXT, PRIMARY KEY (name) );
INSERT INTO settings VALUES('migrationPerformed','YES');
COMMIT;
EOF
#####
log_info "Customizing installed OS..."
# Automate creation of user account on first boot
cat > "${SCRATCH_INSTALLER_CONFIGURATION_FILE}" <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Users</key>
<array>
<dict>
<key>admin</key>
<true/>
<key>autologin</key>
<${AUTO_LOGIN}/>
<key>fullName</key>
<string>${FULL_NAME}</string>
<key>shortName</key>
<string>${USER_NAME}</string>
<key>password</key>
<string>${PASS_WORD}</string>
<key>skipMiniBuddy</key>
<true/>
</dict>
</array>
</dict>
</plist>
EOF
# Enable password-less sudo
cat > "${SCRATCH_SUDOERS_D}/${USER_NAME}" <<-EOF
${USER_NAME} ALL=(ALL) NOPASSWD: ALL
EOF
chmod 0440 "${SCRATCH_SUDOERS_D}/${USER_NAME}"
# Enable ssh
/usr/libexec/PlistBuddy -c 'Add :com.openssh.sshd bool False' "${SCRATCH_LAUNCHD_DISABLED_PLIST}"
# Add extra vagrant customizations
if [ "${USER_NAME}" = "vagrant" ]; then
log_info "Customizing installed OS for use with Vagrant..."
# Enable further customizations to occur on first boot
cat > "${SCRATCH_RC_INSTALLER_CLEANUP}" <<-"EOF"
#!/bin/sh
rm /etc/rc.installer_cleanup
/etc/rc.vagrant &
exit 0
EOF
chmod 0755 "${SCRATCH_RC_INSTALLER_CLEANUP}"
# Install default insecure vagrant ssh key on first boot
cat > "${SCRATCH_RC_VAGRANT}" <<-"EOF"
#!/bin/sh
rm /etc/rc.vagrant
while [ ! -e /Users/vagrant ]; do
sleep 1
done
if [ ! -e /Users/vagrant/.ssh ]; then
mkdir /Users/vagrant/.ssh
chmod 0700 /Users/vagrant/.ssh
chown `stat -f %u /Users/vagrant` /Users/vagrant/.ssh
fi
echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key" >> /Users/vagrant/.ssh/authorized_keys
chmod 0600 /Users/vagrant/.ssh/authorized_keys
chown `stat -f %u /Users/vagrant` /Users/vagrant/.ssh/authorized_keys
EOF
chmod 0755 "${SCRATCH_RC_VAGRANT}"
fi
#####
log_info "Re-attaching the scratch image..."
hdiutil detach -quiet -force "${SCRATCH_MOUNTPOINT}" || echo > /dev/null
SCRATCH_DEVICE=$(
hdiutil attach "${SCRATCH_IMAGE}" -mountpoint "${SCRATCH_MOUNTPOINT}" -nobrowse -owners on |
grep GUID_partition_scheme |
cut -f1 |
tr -d '[:space:]'
)
if [ ! -e "${SCRATCH_DEVICE}" ]; then
bail "Failed to find the device file of the image"
fi
#####
log_info "Converting the scratch image to VMDK format..."
"${VMWARE_RAWDISKCREATOR}" create "${SCRATCH_DEVICE}" fullDevice "${TEMP_DIR}/rawdisk" lsilogic
"${VMWARE_VDISKMANAGER}" -t 0 -r "${TEMP_DIR}/rawdisk.vmdk" "${TEMP_DIR}/output.vmdk"
chown "$SUDO_USER" "${TEMP_DIR}/output.vmdk"
mv "${TEMP_DIR}/output.vmdk" "${OUTPUT_PATH}"
#####
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment