Your one stop formatting shop

Update 22 Dec 2016

So I finally got around to creating a new NBI based on 10.12.
When I tried to run this script on 10.12, I found that Apple had changed the output in the diskutil command.

So previously I was searching for the text of either yes or no for the removable media label.
Under 10.12, this is no longer the case, with Apple replacing the word “No” with “fixed”

To combat this, the only thing to do is, of course, use python, for which I have a love hate relationship. So in the interests of just getting it done, I have updated the script replacing the following:

 $(echo "$DISK_INFO" | awk '/Solid State:/ {print $3}') = "No")

with

$(diskutil info -plist $DISK | plutil -convert json -o - - | python -c 'import sys, json; print json.load(sys.stdin)["SolidState"]')

This gets the output from diskutil as a plist, converts it into json and then uses python to print out the value for the key ‘SolidState’ which is returned as a boolean (true/false)

This is much better than parsing text which may change in the future.

Update – 9 Aug 2016

Well so it turns out I made some assumptions in my first draft of the formatting script around how to identify a machine for Fusion Drive. Turns out I also left out what to do if a FileVault enabled disk was discovered. I have updated the script to handle all these cases.

The script should now detect the correct device ID’s for the SSD and HDD if found. It will also check to see if a FileVault disk is locked or unlocked. If it is unlocked, it will proceed to delete the core storage volume. If the FileVault volume is locked, it will throw an error to the user via a CocoaDialog message box.

The script will also now check to ensure that the drives it formats are internal, physical, non removable media disks. As SD cards can often present as internal, physical this could be a complication. Luckily they also show up as removable, so checking this we can avoid formatting any SD cards that may be in the machine.

As I also do a lot of testing with VMware Fusion, I have a check in the script to ensure that VMware disks are formatted correctly as well. This is because VMware fusion disks show up as external, physical disks rather than internal, physical disks.

 

 

 

In my environment I use DeployStudio and Imagr to deploy our “image” to client devices.

Recently I came across an issue with some iMacs that have a Fusion drive.

When I use DeployStudio, I was targeting the restore task to “First drive available”

Screen Shot 2016-06-22 at 10.56.39 AM

This had always worked very well for me in the past, however I noticed that a few of the latest iMacs had failed to complete the image build (via Munki) due to a full HD.

When I checked their computer record in Munki Report it was pretty clear what had happened.

storage

storage2

For some reason, the fusion drive has ‘un-fused’ and DeployStudio has installed our base OS image onto the SSD.

Turns out this is a bit of an issue with DeployStudio. There are quite a few posts on the deploy studio forums about this.

There are a few solutions out there like having a workflow that is for Fusion Drive Mac’s that uses DeployStudio’s Fusion Drive task first and then you can just target the volume name of your new Fusion Drive in the Restore Task

Screen Shot 2016-06-22 at 11.13.10 AM

But I really like to have One Workflow To Rule Them All! So I didn’t like that solution, also users don’t know if their Mac has a fusion drive or not so there is confusion there.

Instead I came up with a script that will check a machine to see if it has a Fusion Drive or atleast the components for a fusion drive i.e. SSD and HDD.

The script will then create a new Fusion Drive, deleting any current FD if it already exists, create a new volume on the Fusion Drive called Macintosh HD.

The script will also be able to tell if the machine does not have a fusion drive, in this case the script will simply locate the internal HDD or SSD and format it and create a volume called Macintosh HD.

So now I simply run this script as the first item in the workflow and ensure that my Restore Task targets my new volume called Macintosh HD whether it be on a Fusion Drive LVG or a regular JHFS+ Partition.

The contents of the script are as below:


#!/bin/bash
########################################################################
# Author: Calum Hunter #
# Date: 21/12/2016 #
# Version: 0.7 #
# Purpose: Fusion Drive Detection and general HD formatting before #
# imaging tasks. #
# #
########################################################################
SCRIPT_NAME="050_Format_Hard_Drive.sh"
VERS="0.7"
# Setup Logging
# Get the machines serial number to start with
SERIAL_NUMBER=$(ioreg -c IOPlatformExpertDevice -d 2 | awk -F\" '/IOPlatformSerialNumber/{print $(NF-1)}')
LOG_FILE="/Volumes/imagr_repo/logs/${SERIAL_NUMBER}_Debug.log"
touch "$LOG_FILE"
LOG_FILE_SIZE=$(du -k $LOG_FILE | awk '{print $1}')
if [ $LOG_FILE_SIZE -gt 1024 ]; then
rm $LOG_FILE
echo $(date "+%a %b %d %H:%M:%S") "========================================================================" >> $LOG_FILE
echo $(date "+%a %b %d %H:%M:%S") " — Log file rotated on $(date "+%a %b %d %H:%M:%S") —" >> $LOG_FILE
fi
# Redirect all output to log file (and also send to STDOUT and STDERR)
exec > >(tee -a ${LOG_FILE} )
exec 2> >(tee -a ${LOG_FILE} >&2)
# Start with some variables
MAC_MODEL=$(sysctl hw.model | awk '{print $2}')
# Location of CocoaDialog
CD="/Applications/Strapper.app/Contents/Resources/cocoaDialog.app/Contents/MacOS/cocoaDialog"
CHECK_FOR_FUSION(){
# Check for the presence of multiple internal drives. If we have 2 or more we should check for SSD's and HDDS
echo $(date "+%a %b %d %H:%M:%S") " – Checking for Multiple Internal Disks…"
NUM_INT_HDS=$(diskutil list | grep "internal" | grep -v "virtual" -c)
if [ "$NUM_INT_HDS" -ge "2" ]; then
echo $(date "+%a %b %d %H:%M:%S") " [OK] Detected $NUM_INT_HDS internal disks, checking for SSD and HDD.."
CHECK_FOR_SSD
else
echo $(date "+%a %b %d %H:%M:%S") " [OK] Detected $NUM_INT_HDS internal disk, not possible to create a fusion drive. Moving on."
FUSION="FALSE"
fi
}
CHECK_FOR_SSD(){
# Loop through the disks and see if we can find an internal SSD
# Start with the getting the list of internal disks
echo $(date "+%a %b %d %H:%M:%S") ""
echo $(date "+%a %b %d %H:%M:%S") " – Checking $NUM_INT_HDS internal disks looking for SSD or HDD and Non Removable Media …"
DEV_DISK=$(diskutil list | grep "internal" | grep -v "virtual" | awk '/dev/ {print $1}')
for DISK in $DEV_DISK; do
echo $(date "+%a %b %d %H:%M:%S") ""
echo $(date "+%a %b %d %H:%M:%S") " – Checking $DISK …"
SSD_STATUS=$(diskutil info -plist $DISK | plutil -convert json -o – – | python -c 'import sys, json; print json.load(sys.stdin)["SolidState"]')
REMOVABLE_STATUS=$(diskutil info -plist $DISK | plutil -convert json -o – – | python -c 'import sys, json; print json.load(sys.stdin)["Removable"]')
if [[ "$SSD_STATUS" = "False" ]] && [[ "$REMOVABLE_STATUS" = "False" ]]; then
echo $(date "+%a %b %d %H:%M:%S") " [OK] Disk Type: HDD – Non Removable"
HDD_DISK_DEV=$DISK
continue
#elif [[ $(echo "$DISK_INFO" | awk '/Solid State:/ {print $3}') = "Yes" ]] && [[ $(echo "$DISK_INFO" | awk '/Removable Media:/ {print $3}') = "No" ]]; then
elif [[ "$SSD_STATUS" = "True" ]] && [[ "$REMOVABLE_STATUS" = "False" ]]; then
echo $(date "+%a %b %d %H:%M:%S") " [OK] Disk Type: SSD – Non Removable"
SSD_DISK_DEV=$DISK
continue
else
echo $(date "+%a %b %d %H:%M:%S") " [ERROR] Disk $DISK does not meet our requirements of SSD or HDD and Non Removable Media. Moving on."
fi
done
if [[ ! -z $SSD_DISK_DEV ]] && [[ ! -z $HDD_DISK_DEV ]]; then
echo $(date "+%a %b %d %H:%M:%S") ""
echo $(date "+%a %b %d %H:%M:%S") " [OK] SSD is on: $SSD_DISK_DEV"
echo $(date "+%a %b %d %H:%M:%S") " [OK] HDD is on: $HDD_DISK_DEV"
FUSION="TRUE"
else
echo $(date "+%a %b %d %H:%M:%S") " [ERROR] Did not find a SSD AND HDD!"
FUSION="FALSE"
fi
}
THROW_FV_ERROR(){
# If filevault is locked, let the people know they need to turn it off
FV_LOCKED=($($CD msgbox –title "Error!" –icon stop –text "Filevault Enabled and Locked" –no-newline –informative-text "Filevault Disk Encryption is enabled on this machine.
Please reboot this machine, disable Filevault and try again" –button1 "Shutdown" –string-output))
if [ "$FV_LOCKED" = "Shutdown" ]; then
echo $(date "+%a %b %d %H:%M:%S") "[ERROR] – User alerted to FV being locked and they selected shutdown now"
shutdown -h now
fi
}
CHECK_FOR_FV(){
# Check to see if we have a FileVault volume and if its locked or unlocked.
echo $(date "+%a %b %d %H:%M:%S") " – Checking for FileVault Encryption …."
FV_STATUS=$(diskutil cs list | awk '/Encryption Status:/ {print $3}')
if [[ "$FV_STATUS" ]]; then
for FV in $FV_STATUS; do
if [ "$FV" = "Locked" ]; then
echo $(date "+%a %b %d %H:%M:%S") " [ERROR] FileVault Enabled. Status: $FV …."
THROW_FV_ERROR
elif [ "$FV" = "Unlocked" ]; then
echo $(date "+%a %b %d %H:%M:%S") " [WARN] FileVault Enabled. Status: $FV …."
echo $(date "+%a %b %d %H:%M:%S") " – Proceeding to remove CS LVG"
DELETE_CS_VOLUME
fi
done
else
echo $(date "+%a %b %d %H:%M:%S") " [OK] FileVault not enabled"
DELETE_CS_VOLUME
fi
}
DELETE_CS_VOLUME(){
# Remove existing CS Group
echo $(date "+%a %b %d %H:%M:%S") " – Locating CoreStorage LVG …"
CS_LVG=$(/usr/sbin/diskutil cs list | awk '/– Logical Volume Group / {print $NF}')
if [[ "$CS_LVG" ]]; then
for LVG in $CS_LVG; do
echo $(date "+%a %b %d %H:%M:%S") " [OK] – Located existing CoreStorage LVG with ID: $LVG"
echo $(date "+%a %b %d %H:%M:%S") " – Removing CoreStorage LVG ID: $LVG …"
echo $(date "+%a %b %d %H:%M:%S") ""
/usr/sbin/diskutil cs delete "$LVG"
sleep 4
done
if [ "$FUSION" = "TRUE" ]; then
CREATE_NEW_FUSION_LVG
else
FORMAT_REGULAR_DRIVE
fi
else
echo $(date "+%a %b %d %H:%M:%S") " [ERROR] – Unable to detect a CoreStorage LVG !"
exit 1
fi
}
CREATE_NEW_FUSION_LVG(){
echo $(date "+%a %b %d %H:%M:%S") ""
echo $(date "+%a %b %d %H:%M:%S") " – Creating Fusion Drive …"
echo $(date "+%a %b %d %H:%M:%S") " – Using the following disks: $SSD_DISK_DEV and $HDD_DISK_DEV"
echo $(date "+%a %b %d %H:%M:%S") ""
/usr/sbin/diskutil cs create "FusionDrive" $SSD_DISK_DEV $HDD_DISK_DEV
sleep 4
LVG_ID=$(/usr/sbin/diskutil cs list | awk '/– Logical Volume Group / {print $NF}')
# Create a new Fusion Volume
echo $(date "+%a %b %d %H:%M:%S") ""
echo $(date "+%a %b %d %H:%M:%S") " – Creating Volume (Macintosh HD) on Fusion LVG: $LVG_ID …"
echo $(date "+%a %b %d %H:%M:%S") ""
/usr/sbin/diskutil cs createVolume "$LVG_ID" jhfs+ "Macintosh HD" 100%
sleep 2
echo $(date "+%a %b %d %H:%M:%S") ""
}
CHECK_FOR_CS(){
# Check for any CS volumes
echo $(date "+%a %b %d %H:%M:%S") " – Checking for CoreStorage Volumes…"
if [ "$(diskutil cs list)" = "No CoreStorage logical volume groups found" ]; then
echo $(date "+%a %b %d %H:%M:%S") " [OK] – No CoreStorage Volumes found."
if [ "$FUSION" = "TRUE" ]; then
CREATE_NEW_FUSION_LVG
else
FORMAT_REGULAR_DRIVE
fi
else
CS_CHECK_LVG=$(diskutil cs list | awk '/– Logical Volume Group / {print $NF}')
for CSLVG in $CS_CHECK_LVG; do
echo $(date "+%a %b %d %H:%M:%S") " [OK] – CoreStorage LVG found with ID: $CSLVG"
done
CHECK_FOR_FV
fi
}
NO_DRIVE_AVAIL(){
NO_INT_PHYS=($($CD msgbox –title "Error!" –icon stop –text "Unable to detect hard drive" –no-newline –informative-text "Unable to locate an internal physical hard drive.
If you feel this is in error, please contact IT." –button1 "Shutdown" –string-output))
if [ "$NO_INT_PHYS" = "Shutdown" ]; then
echo $(date "+%a %b %d %H:%M:%S") "[ERROR] – User alerted and they selected shutdown now"
shutdown -h now
fi
}
FORMAT_REGULAR_DRIVE(){
# Get a list of disks. Loop the through the disks and stop when we find an internal, physical and _non-removable_ disk.
echo $(date "+%a %b %d %H:%M:%S") ""
if [[ "$MAC_MODEL" == *VMware* ]]; then # Check to see if we are VMWare Fusion – we have different disk types grrr
echo $(date "+%a %b %d %H:%M:%S") " [DEBUG] – Yo! Running VMWare Fusion! We will use /external, physical/ for finding a hard disk!"
# Start by getting a list of disk dev id's
DEV_DISK_LIST=$(diskutil list | grep "external" | grep -v "virtual" | awk '/dev/ {print $1}')
for DISK in $DEV_DISK_LIST; do
echo $(date "+%a %b %d %H:%M:%S") " – Checking $DISK …"
#DISK_INFO=$(diskutil info $DISK)
SSD_STATUS=$(diskutil info -plist $DISK | plutil -convert json -o – – | python -c 'import sys, json; print json.load(sys.stdin)["SolidState"]')
REMOVABLE_STATUS=$(diskutil info -plist $DISK | plutil -convert json -o – – | python -c 'import sys, json; print json.load(sys.stdin)["Removable"]')
#if [[ $(echo "$DISK_INFO" | awk '/Removable Media:/ {print $3}') = "No" ]]; then
if [[ "$REMOVABLE_STATUS" = "False" ]]; then
echo $(date "+%a %b %d %H:%M:%S") " [OK] Disk Type: Non Removable"
HDD_DISK_DEV=$DISK
break # Stop when we find an external, physical disk that is non-removable
#elif [[ $(echo "$DISK_INFO" | awk '/Removable Media:/ {print $3}') = "Yes" ]]; then
elif [[ "$REMOVABLE_STATUS" = "True" ]]; then
echo $(date "+%a %b %d %H:%M:%S") " [ERROR] Disk Type: Removable"
continue
fi
done
if [ ! -z $HDD_DISK_DEV ]; then
echo $(date "+%a %b %d %H:%M:%S") " [OK] Found Eligible Disk: $HDD_DISK_DEV"
else
echo $(date "+%a %b %d %H:%M:%S") " [ERROR] – Unable to locate external, non removable disk!"
NO_DRIVE_AVAIL
fi
else
# Ok now search for disks on a normal non VMWare Fusion Machine
DEV_DISK_LIST=$(diskutil list | grep "internal" | grep -v "virtual" | awk '/dev/ {print $1}')
for DISK in $DEV_DISK_LIST; do
echo $(date "+%a %b %d %H:%M:%S") " – Checking $DISK …"
#DISK_INFO=$(diskutil info $DISK)
SSD_STATUS=$(diskutil info -plist $DISK | plutil -convert json -o – – | python -c 'import sys, json; print json.load(sys.stdin)["SolidState"]')
REMOVABLE_STATUS=$(diskutil info -plist $DISK | plutil -convert json -o – – | python -c 'import sys, json; print json.load(sys.stdin)["Removable"]')
#if [[ $(echo "$DISK_INFO" | awk '/Removable Media:/ {print $3}') = "No" ]]; then
if [[ "$REMOVABLE_STATUS" = "False" ]]; then
echo $(date "+%a %b %d %H:%M:%S") " [OK] Disk Type: Non Removable"
HDD_DISK_DEV=$DISK
break # Stop when we find an internal, physical disk that is non-removable
#elif [[ $(echo "$DISK_INFO" | awk '/Removable Media:/ {print $3}') = "Yes" ]]; then
elif [[ "$REMOVABLE_STATUS" = "True" ]]; then
echo $(date "+%a %b %d %H:%M:%S") " [ERROR] Disk Type: Removable"
continue
fi
done
if [ ! -z $HDD_DISK_DEV ]; then
echo $(date "+%a %b %d %H:%M:%S") " [OK] Found Eligible Disk: $HDD_DISK_DEV"
else
echo $(date "+%a %b %d %H:%M:%S") " [ERROR] – Unable to locate internal, physical, non removable disk!"
NO_DRIVE_AVAIL
fi
fi
echo $(date "+%a %b %d %H:%M:%S") ""
echo $(date "+%a %b %d %H:%M:%S") " – Beginning regular drive format."
echo $(date "+%a %b %d %H:%M:%S") " – Formating drive $HDD_DISK_DEV as Macintosh HD …"
echo $(date "+%a %b %d %H:%M:%S") ""
/usr/sbin/diskutil partitionDisk $HDD_DISK_DEV 1 GPT jhfs+ "Macintosh HD" R
echo $(date "+%a %b %d %H:%M:%S") ""
}
#————————————————————————————————————#
# Start the script
echo $(date "+%a %b %d %H:%M:%S") "========================================================================"
echo $(date "+%a %b %d %H:%M:%S") " – Running Script: $SCRIPT_NAME v. $VERS"
echo $(date "+%a %b %d %H:%M:%S") " "
if [[ "$MAC_MODEL" == *iMac* ]] || [[ "$MAC_MODEL" == *Macmini* ]]; then
echo $(date "+%a %b %d %H:%M:%S") " – Mac Model: $MAC_MODEL _may_ have a FUSION DRIVE!"
echo $(date "+%a %b %d %H:%M:%S") ""
CHECK_FOR_FUSION
CHECK_FOR_CS
else
echo $(date "+%a %b %d %H:%M:%S") " – Mac Model: $MAC_MODEL is NOT an eligible model for a Fusion Drive."
echo $(date "+%a %b %d %H:%M:%S") " "
CHECK_FOR_CS
fi
echo $(date "+%a %b %d %H:%M:%S") "==================== Fusion Drive Format Complete! ======================"
exit 0

view raw

format_hd.sh

hosted with ❤ by GitHub

 

 

 

 

8 comments

  1. Hi Calum,

    Thanks for the excellent script , i am using your script and modifying it to use it in a netboot environment and in casper imaging. i noticed that it work wonderfully on 10.11 but now it is not working on 10.12 sierra .

    i narrow down to the function CHECK_FOR_SSD , i am not sure if you are having the same issue ?

    Like

    1. Hi,

      Nope no issues for me on 10.12. I am using imagr though, not casper. Casper Imaging has always been a problem for me, even when I was using Casper at a lot of sites I never bothered using Casper Imaging it never suited my needs and created too many problems.

      perhaps you could upload the log file from the script somewhere and i can take a look

      Like

      1. HI Calum,

        I see that you upgraded the script. Thanks alot it help.

        I updated your script and i found issue with it. Still the same function CHECK_FOR_SSD is causing the problem

        i saw your changes , and did a comparison and make the modification , the core changes are the SSD_STATUS variable and the if [[ “$SSD_STATUS” = “False” ]] && [[ “$REMOVABLE_STATUS” = “False” ]]; statement

        I tested on a fusion drive mac mini , the script ran and when it goes to CHECK_FOR_SSD function,

        It didn’t detect the hard drive properly

        Wed Feb 01 13:31:27 NetBoot758 jamf – Mac Model: Macmini7,1 _may_ have a FUSION DRIVE!
        Wed Feb 01 13:31:27 NetBoot758 jamf
        Wed Feb 01 13:31:27 NetBoot758 jamf – Checking for an Multiple Internal Disks…
        Wed Feb 01 13:31:27 NetBoot758 jamf [OK] Detected 2 internal disks, checking for SSD and HDD..
        Wed Feb 01 13:31:27 NetBoot758 jamf
        Wed Feb 01 13:31:27 NetBoot758 jamf – Checking 2 disks looking for SSD or HDD and Non Removable Media …
        Wed Feb 01 13:31:27 NetBoot758 jamf
        Wed Feb 01 13:31:27 NetBoot758 jamf – Checking /dev/disk0 …
        Wed Feb 01 13:31:27 NetBoot758 jamf [ERROR] Disk /dev/disk0 does not meet our requirements of SSD or HDD and Non Removable Media. Moving on.
        Wed Feb 01 13:31:27 NetBoot758 jamf
        Wed Feb 01 13:31:28 NetBoot758 jamf – Checking /dev/disk1 …
        Wed Feb 01 13:31:29 NetBoot758 jamf [ERROR] Disk /dev/disk1 does not meet our requirements of SSD or HDD and Non Removable Media. Moving on.
        Wed Feb 01 13:31:29 NetBoot758 jamf [ERROR] Did not find a SSD AND HDD!
        Wed Feb 01 13:31:29 NetBoot758 jamf – Checking for CoreStorage Volumes…
        Wed Feb 01 13:31:29 NetBoot758 jamf [OK] – CoreStorage LVG found with ID: 2C0AD3AA-4C19-4E85-95A8-1DA45D8C5C09
        Wed Feb 01 13:31:29 NetBoot758 jamf – Checking for FileVault Encryption ….
        Wed Feb 01 13:31:29 NetBoot758 jamf [OK] FileVault not enabled
        Wed Feb 01 13:31:29 NetBoot758 jamf – Locating CoreStorage LVG …
        Wed Feb 01 13:31:29 NetBoot758 jamf [OK] – Located existing CoreStorage LVG with ID: 2C0AD3AA-4C19-4E85-95A8-1DA45D8C5C09
        Wed Feb 01 13:31:29 NetBoot758 jamf – Removing CoreStorage LVG ID: 2C0AD3AA-4C19-4E85-95A8-1DA45D8C5C09 …

        Background , i am using netboot and the netboot is 10.12.3 Sierra and i am using Casper Imaging

        diskutil list log

        Disk0 is the SSD
        Disk1 is the 1TB HDD

        NetBoot743:~ root# diskutil list
        /dev/disk0 (internal, physical):
        #: TYPE NAME SIZE IDENTIFIER
        0: GUID_partition_scheme *121.3 GB disk0
        1: EFI EFI 209.7 MB disk0s1
        2: Apple_CoreStorage Mac HD 121.0 GB disk0s2
        3: Apple_Boot Boot OS X 134.2 MB disk0s3
        /dev/disk1 (internal, physical):
        #: TYPE NAME SIZE IDENTIFIER
        0: GUID_partition_scheme *1.0 TB disk1
        1: EFI EFI 209.7 MB disk1s1
        2: Apple_CoreStorage Mac HD 999.9 GB disk1s2
        3: Apple_Boot Boot OS X 134.2 MB disk1s3
        /dev/disk2 (external, physical):
        #: TYPE NAME SIZE IDENTIFIER
        0: GUID_partition_scheme *127.8 MB disk2
        1: Apple_HFS Untitled 127.8 MB disk2s1
        /dev/disk3 (disk image):
        #: TYPE NAME SIZE IDENTIFIER
        0: GUID_partition_scheme +9.0 GB disk3
        1: EFI EFI 209.7 MB disk3s1
        2: Apple_HFS NB Apple LS 8.7 GB disk3s2
        /dev/disk4 (disk image):
        #: TYPE NAME SIZE IDENTIFIER
        0: untitled +268.4 MB disk4
        /dev/disk5 (internal, virtual):
        #: TYPE NAME SIZE IDENTIFIER
        0: Apple_HFS Mac HD +1.1 TB disk5
        Logical Volume on disk0s2, disk1s2
        E5860F2F-7E09-49EF-B2C4-363CBEB46CD5
        Unencrypted Fusion Drive

        Like

      2. Get the new script from my github. I’m using this script successfully at 2,000+ sites with 3500+ machines.

        Also ditch casper imaging, its horrible!

        Like

  2. Hi Calum,

    where do you get your /Volumes/imagr_repo mount from? This is already mounted when you execute the script?

    Thanks in advance!

    Like

Leave a comment