Battery recall for 15" mid-2015 MBP

dcgagne
Contributor
Apple today announced a voluntary recall of a limited number of older generation 15-inch MacBook Pro units which contain a battery that may overheat and pose a safety risk. The units were sold primarily between September 2015 and February 2017 and can be identified by their product serial number. The recall does not affect any other 15-inch MacBook Pro units or other Mac notebooks.

https://appleinsider.com/articles/19/06/20/apple-issues-battery-recall-on-15-inch-macbook-pro-from-2...

https://support.apple.com/15-inch-macbook-pro-battery-recall

Creating a saved search with the criteria "Model" - "MacBook Pro (Retina, 15-inch, Mid 2015)" "15-inch Retina MacBook Pro (Mid 2015)" should show any devices that may be part of the recall. A quick glance at my fleet showed a couple that come back positive in the eligibility tool, but I'm not seeing any commonality between their serial numbers.

edit apologies, bad copy and paste job

31 REPLIES 31

robmorton
Contributor

At least for me, I had to use Model is "15-inch Retina MacBook Pro (Mid 2015)". Even then spot checking the serial numbers I found 1 out of about 10 tries that say it has the issue. If anyone finds a better query, please post it.

egjerde
New Contributor III

yes, seriously... the thought of manually pasting all our serials into their lookup tool is a painful one!

robmorton
Contributor

I just asked Apple if there was a upload feature as I don't feel like scripting this. We have almost 2000 that might qualify, so...

rvelasco212
New Contributor II

Also asked as well, just checked a bunch by hand.

hdsreid
Contributor III

glad I only had around 30 to check. 2 of them appear to be covered by the recall, and nothing similar with the serials either

robmorton
Contributor

My Apple rep said there is an internal tool they can use, so I sent our list of serial numbers to him.

patgmac
Contributor III

If you have an Enterprise AppleCare OS Support agreement, you can send a csv of serials to your AppleCare Account Manager and they can have them checked. Not sure about non-Enterprise support customers.

nicktong
New Contributor III

Looks like the serial checker thing on Apple's recall page posts the serial and a random guid to https://qualityprograms.apple.com/snlookup/062019, so just based our EA on that:

Updated on July 24: Agree with @elliotjordan so have added a Run Once option:

#!/usr/bin/env bash

# Run Once Option
# Update (24-JUL-2019): Added runOnce option
# Agree with [~elliotjordan] that we should avoid excessively querying Apple

# Set runOnce bool to true(1) to limit querying qualityprograms.apple.com to the 
# first instance of receiving an expected response (E00, E01, FE01, or FE02)
# that is not a processing error (E99 or FE03).
runOnce="true"

if [[ "$runOnce" == "true" || "$runOnce" == "1" ]]; then
    if [[ -e /usr/local/xattr/.appleRecall062019CheckDone ]]; then
        # Using runOnce cached value:
        echo "<result>$(cat /usr/local/xattr/.appleRecall062019CheckDone | head -n 1)</result>"
        # Delete /usr/local/xattr/.appleRecall062019CheckDone to re-query qualityprograms.apple.com
        exit 0
    fi
fi

createRecallCheckDone () {
    mkdir -p /usr/local/xattr
    echo $1 > /usr/local/xattr/.appleRecall062019CheckDone
}

postURL="https://qualityprograms.apple.com/snlookup/062019"
quotSerial=$(ioreg -l | grep IOPlatformSerialNumber | awk '{print $4}')
quotGUID='"'$(uuidgen | tr "[:upper:]" "[:lower:]")'"'
modelID=$(system_profiler SPHardwareDataType | grep "Model Identifier" | awk '{print $3}')

# Be nice to qualityprograms.apple.com and only query valid models by pre-qualifying:
if [[ "$modelID" == "MacBookPro11,4" || "$modelID" == "MacBookPro11,5" ]]; then
    # 12 charlen for all serials in affected years (14 incl quote)
    # 36 charlen for all guids (38 incl quote)
    if [[ "${#quotSerial}" -eq 14 ]]; then
        if [[ "${#quotGUID}" -eq 38 ]]; then
            postData="{"serial":$quotSerial,"GUID":$quotGUID}"
            resp=$(curl -d "$postData" -H "Content-Type: application/json" -X POST "$postURL")
            if [[ "$resp" == *'"status":"E00"'* ]]; then
                echo "<result>E00-Eligible</result>"
                createRecallCheckDone "E00-Eligible"
            elif [[ "$resp" == *'"status":"E01"'* ]]; then
                echo "<result>E01-Ineligible</result>"
                createRecallCheckDone "E01-Ineligible"
            elif [[ "$resp" == *'"status":"E99"'* ]]; then
                echo "<result>E99-ProcessingError</result>"
                # createRecallCheckDone "E99-ProcessingError"
            elif [[ "$resp" == *'"status":"FE01"'* ]]; then
                echo "<result>FE01-EmptySerial</result>"
                createRecallCheckDone "FE01-EmptySerial"
            elif [[ "$resp" == *'"status":"FE02"'* ]]; then
                echo "<result>FE02-InvalidSerial</result>"
                createRecallCheckDone "FE02-InvalidSerial"
            elif [[ "$resp" == *'"status":"FE03"'* ]]; then
                echo "<result>FE03-ProcessingError</result>"
                # createRecallCheckDone "FE03-ProcessingError"
            else
                echo "<result>Err1-UnexpectedResponse</result>"
            fi
        else
            echo "<result>Err2-NotQueried-InvalidGuidLength</result>"
        fi
    else
        echo "<result>Err3-NotQueried-InvalidSerialLength</result>"
    fi
else
    echo "<result>Msg1-IneligibleModel</result>"
fi

According to the source for Apple's recall page, there are six possible valid responses:

E00-Eligible
E01-Ineligible
E99-ProcessingError
FE01-EmptySerial
FE02-InvalidSerial
FE03-ProcessingError

This attribute adds a message for models that are not pre-qualified for query, an error state for an unexpected response, and error states for not being able to run the query because of invalid serial or GUID lengths:

Msg1-IneligibleModel
The model will not be queried against qualityprograms.apple.com because it does not match MacBookPro11,4 or MacBookPro11,5.

Err1-UnexpectedResponse
The response was something other than one of the six expected codes above.

Err2-NotQueried-InvalidGuidLength
The combined length of the locally generated GUID and two soft-quote characters did not equal 38.

Err3-NotQueried-InvalidSerialLength
The combined length of the serial number and two soft-quote characters did not equal 14.

Anyhow, might be useful just for keeping an eye on things to make sure these don't pop back up once we think we have things cleaned up.

ClassicII
Contributor III

@nicktong

Nice work!!! I hope you don't mind me linking your post to spread the word on this great script!

https://mrmacintosh.com/some-mid-2015-macbook-pros-recalled-over-battery-overheating-issues/

nicktong
New Contributor III

@ClassicII

Thanks! Just so you're aware, an edit had to be made to remove the (incorrect) assumption there would never be metadata returned (there is when an eligible device (E00) is identified).

Changed comparisons from the format of:

'{"metadata":"","status":"E00"}' to *'"status":"E00"'*

As positive responses do contain metadata:

{"metadata":"UmBRSI9lXL4j17Ub8qj3i6cvmx3a1NuJAdM%2FIyAtoS41HX4HaaWKNRmOHlwm8p50o6rRzdoIOWx%2BUBOdVsnXfL7tcuP8v0fULb6DQTw5%2FejMJfgwnaVzn8QwLGgxYC3xXutUfj7IvXzP0ba0U9ovB%2Fs%2BZvuoA%2FV%2B6iFIiXPxAga1DQ%2BKbd8wSvir%2B%2BL0yVyieMxwI%2FmfDjdFNl4K7lr0kIfMfDVbepkJ0jz8%2FW017XG5Ip1i5cFhxpu20DtLvLnYMeQfLu7FuVeVTxgF%2F9qQGjj1ySh%2BBvKTlHY2NVS7UV4ZV455ABhS24GsgYgHf05e8fotQGBedxDoHdaoX8Pgjg%3D%3D","status":"E00"}

neilmartin83
Contributor II

Thanks to Nick's EA I came up with this which you can feed a file containing a list of serials as well (and you can redirect output to a CSV if you want) - in case you don't fancy waiting for a recon:

https://soundmacguy.wordpress.com/2019/06/21/2015-macbook-pro-battery-recall-checker-script/

Hope it helps!

ThijsX
Valued Contributor
Valued Contributor

Great! thanks for putting this together!!

elliotjordan
Contributor III

I'd strongly suggest using @neil.martin83's script instead of implementing an EA for this check. Best not to give Apple another reason to consider putting captchas on lookups like this in the future.

swapple
Contributor III

Following that URL takes us to this???

1c4ad5e179e24a2dadd5faa14b04cb37

neilmartin83
Contributor II

@swhps That URL is an API endpoint - not meant to be accessed by a browser. The way it's used is that you're supposed to send it a specifically crafted HTTP request that contains the serial number you're querying plus a random GUID string, then it'll return a response that you can determine eligibility from.

mikeh
Contributor II

@neil.martin83 Thanks so much for the script. It made identifying our affected MBPs child's play, allowing me to quickly notify our affected users.

donmontalvo
Esteemed Contributor III

@elliotjordan and @neil.martin83 thanks to the internet, I stole from both of you, without remorse, and very thankful to you both. :)

10266ecd413b47c5a87b86a95c94bc7a

Paying it forward...script runs locally (Once Per Computer), curls status from Apple, outputs status to EA...then just need to create a Smart Computer Group listing computers that output "E00-Eligible".

Script:

#!/usr/bin/env bash
# Adapted from Nick Tong's EA:
#    https://www.jamf.com/jamf-nation/discussions/32400/battery-recall-for-15-mid-2015-mbp
# And from Neil Martin's script:
#    https://soundmacguy.wordpress.com/2019/06/21/2015-macbook-pro-battery-recall-checker-script/
#
# This script checks status and outputs to a file (folder path created if not there).
# Run the script in a Once Per Computer policy.
# Create an EA to cat status in file and Smart Computer Group to report.
# 20190621 DM

postURL="https://qualityprograms.apple.com/snlookup/062019"
quotSerial=$(ioreg -l | grep IOPlatformSerialNumber | awk '{print $4}')
quotGUID='"'$(uuidgen | tr "[:upper:]" "[:lower:]")'"'
modelID=$(system_profiler SPHardwareDataType | grep "Model Identifier" | awk '{print $3}')

# Create output directory and define path to output file
OUTPUTDIR="/Library/COMPANY/SearchResults/20190620-15-inch-macbook-pro-battery-recall"
OUTPUTFILE="$OUTPUTDIR/status.txt"
/bin/mkdir -p "$OUTPUTDIR"

# Be nice to qualityprograms.apple.com and only query valid models by pre-qualifying:
if [[ "$modelID" == "MacBookPro11,4" || "$modelID" == "MacBookPro11,5" ]]; then
    # 12 charlen for all serials in affected years (14 incl quote)
    # 36 charlen for all guids (38 incl quote)
    if [[ "${#quotSerial}" -eq 14 ]]; then
        if [[ "${#quotGUID}" -eq 38 ]]; then
            postData="{"serial":$quotSerial,"GUID":$quotGUID}"
            resp=$(curl -d "$postData" -H "Content-Type: application/json" -X POST "$postURL")
            if [[ "$resp" == *'"status":"E00"'* ]]; then
                echo "E00-Eligible" > "$OUTPUTFILE"
            elif [[ "$resp" == *'"status":"E01"'* ]]; then
                echo "E01-Ineligible" > "$OUTPUTFILE"
            elif [[ "$resp" == *'"status":"E99"'* ]]; then
                echo "E99-ProcessingError" > "$OUTPUTFILE"
            elif [[ "$resp" == *'"status":"FE01"'* ]]; then
                echo "FE01-EmptySerial" > "$OUTPUTFILE"
            elif [[ "$resp" == *'"status":"FE02"'* ]]; then
                echo "FE02-InvalidSerial" > "$OUTPUTFILE"
            elif [[ "$resp" == *'"status":"FE03"'* ]]; then
                echo "FE03-ProcessingError" > "$OUTPUTFILE"
            else
                echo "Err1-UnexpectedResponse" > "$OUTPUTFILE"
            fi
        else
            echo "Err2-NotQueried-InvalidGuidLength" > "$OUTPUTFILE"
        fi
    else
        echo "Err3-NotQueried-InvalidSerialLength" > "$OUTPUTFILE"
    fi
else
    echo "Msg1-IneligibleModel" > "$OUTPUTFILE"
fi

exit 0

EA:

#!/bin/sh

OUTPUTDIR="/Library/COMPANY/SearchResults/20190620-15-inch-macbook-pro-battery-recall"
OUTPUTFILE="$OUTPUTDIR/status.txt"

if [ -e "$OUTPUTFILE" ]; then
    STATUS=$( cat $OUTPUTFILE )
    echo "<result>$STATUS</result>"
else
    echo "<result>FileDoesNotExist</result>"
fi
--
https://donmontalvo.com

nzmacgeek
New Contributor III

I also wrote something. As with ANY script - check to see what it does first, and don't be a dick about how you use it. Apple doesn't like DDoS any more than you do. The suggestions you have seen about writing status to disk as a one-off policy and reading that out to the EA seem like the wise way to go.

Here is my script either way. Quick and dirty, written in 5 minutes.

https://gist.github.com/nzmacgeek/ef453c8e96a67ee12d973e4c0594e286

jsherwood
Contributor

A subtle tweak on Neil's script to include a menu of the current Apple Exchange and Repair Extension Programs (at least the ones with a serial number check).

Hope someone finds it useful...

#!/bin/bash

# Check a list of serial numbers for eligibility in the 2015 MacBook Pro battery replacement recall
# Returns only eligible serial numbers
# The text file must be formatted with Unix (LF) line breaks
#
# Usage: mbpserialcheck.sh /path/to/inputfile.txt
#
# To output directly to a CSV file: mbpserialcheck.sh /path/to/inputfile.txt > /path/to/outputfile.csv
#
# Tweaked by John Sherwood to add a menu of current Exchange and Repair Extension Programs
#
#
# Written by Neil Martin https://soundmacguy.wordpress.com
#
#
# With thanks to Nick Tong for his original Jamf EA which gave me the inspiration for this script. https://www.jamf.com/jamf-nation/discussions/32400/battery-recall-for-15-mid-2015-mbp
#
# 2019-06-21

# Edit the variable below to the filename of your text file containing a list of serial numbers to check
file=$1

# Do not edit below this line
# Present list of current service and recall programs
echo ""
echo "Available Exchange and Repair Extension Programs"
echo ""
echo "a) 15-inch MacBook Pro Battery Recall Program"
echo "b) 13-inch MacBook Pro (non Touch Bar) Solid-State Drive Service Program"
echo "c) 13-inch MacBook Pro (non Touch Bar) Battery Replacement Program"
echo "d) iPhone 8 Logic Board Replacement Program" 
echo "e) iPhone 6s Program for Unexpected Shutdown Issues"
echo "f) iSight Camera Replacement Program for iPhone 6 Plus"
echo "" 

read -n1 -p "Selct the Exchange and Repair Extension Program to check " programChoice

case $programChoice in
    a|A) printf "

	You selected 15-inch MacBook Pro Battery Recall Program

" && programChoice="062019";;
    b|B) printf "

	You selected 13-inch MacBook Pro (non Touch Bar) Solid-State Drive Service Program

" && programChoice="112018";;
    c|C) printf "

	You selected 13-inch MacBook Pro (non Touch Bar) Battery Replacement Program

" && programChoice="032018";;
    d|D) printf "

	You selected iPhone 8 Logic Board Replacement Program

" && programChoice="082018";;
    e|E) printf "

	You selected iPhone 6s Program for Unexpected Shutdown Issues

" && programChoice="112016";;
    f|F) printf "

	You selected iSight Camera Replacement Program for iPhone 6 Plus

" && programChoice="082015";;
esac


# Verify the file exists
if [[ ! -e "$file" ]]; then
    echo "Serial number list file not found or specified"
    echo "Ensure there are no spaces and that the path specified is correct"
    echo "Script usage: ./mbpserialcheck.sh /path/to/file.txt"
    exit 1
fi

# Loop through the list and submit data to Apple
while read -r serial; do
    guid=$(uuidgen | tr "[:upper:]" "[:lower:]")
    postData="{"serial":$serial,"GUID":$guid}"
    resp=$(curl -d "$postData" -H "Content-Type: application/json" -X POST "https://qualityprograms.apple.com/snlookup/$programChoice" 2>&1)
    if [[ "$resp" == *'"status":"E00"'* ]]; then
        echo "$serial,eligible"
    elif [[ "$resp" == *'"status":"E01"'* ]]; then
        echo "$serial,ineligible"
    elif [[ "$resp" == *'"status":"E99"'* ]]; then
        echo "$serial,error please recheck"
    elif [[ "$resp" == *'"status":"FE01"'* ]]; then
        echo "$serial,empty serial"
    elif [[ "$resp" == *'"status":"FE02"'* ]]; then
        echo "$serial,invalid serial"
    elif [[ "$resp" == *'"status":"FE03"'* ]]; then
        echo "$serial,error please recheck"
    else
        echo "$serial,unknown result please recheck"
    fi
done < "$file"

jameson
Contributor II

@donmontalvo

have you verified you script is working ?. Just wondering that the one I test with just say filedoesnotexist

donmontalvo
Esteemed Contributor III

@jameson yes, make sure you run the script with admin rights, else the folder/file can't get created and you'll get FileDoesNotExist.

bash-3.2# /tmp/test.sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   101  100    30  100    71     19     45  0:00:01  0:00:01 --:--:--    45
bash-3.2# cat /Library/COMPANY/SearchResults/20190620-15-inch-macbook-pro-battery-recall/status.txt
E01-Ineligible
bash-3.2#

On a computer we know is effected, the output was E00-Eligible. It was already sent out for repair.

--
https://donmontalvo.com

jameson
Contributor II

Jamf is executing the script so admin rights should be there to create the script. I will try and check again

donmontalvo
Esteemed Contributor III

@nzmacgeek yeap, got yelled at in the past for doing that...the script I posted runs the check once per computer and only to affected computers...it is indeed most logical way to scope. #dontPissAppleOff

--
https://donmontalvo.com

donmontalvo
Esteemed Contributor III

@jameson can you check at least that the folder path is being created? Heading in to work now, can peek here later...

--
https://donmontalvo.com

jameson
Contributor II

It is working. Just had a typing error that caused the issue for me

Mauricio
Contributor III

One could use the 'Model Identifier' from the Advanced Criteria to create a smart group as a target for the script, reducing the scope and the workload on the system.

beeboo
Contributor

@donmontalvo i tried this with a script and EA with smart group, however, while i can run the command fine, natively on the machine, it does run right as a script.

Error running script: return code was 126.

i assume its having difficulty making the directory for reason, maybe it needs a $currentuser or something to that effect defined vs just /library/ ?

MatG
Contributor III

Great script, thank you :)

tlarkin
Honored Contributor

@nicktong doing the Lord's work I see! Thanks, gonna steal a bunch of this at some point when I want to automate the battery checking

nicktong
New Contributor III

@tlarkin lol, I can barely keep up with my own work, let alone... :) In any case, I have noticed that once a battery has been replaced, the response still comes back from qualityprograms.apple.com/snlookup/062019 as "Eligible" .. which is definitely annoying

tlarkin
Honored Contributor

I guess quality programs is just a clever name then :-D