Aged out Macs

Goober22
New Contributor III

Hey guys,

I am looking to get a smart group set up to notify me when Macs age out after 4 years for upgrades. I am doing this right now using the model and specifying the year they were made, but that just means every year I need to add a new field. Is there an easier way to do this?

1 ACCEPTED SOLUTION

Rocky
New Contributor III

I put together an extension attribute script that gives approximate manufacture date. It is based on that part of Michael Lynn's script. GSX integration is better, of course, but has some issues for programmatically getting it for many machines as I understand (captcha and limit to number per day, unless you are a self repair shop).

#!/bin/sh
#
# Parse machine serial number for date (week and year) of manufacture
# based on pyMacWarranty by Michael Lynn (https://github.com/pudquick/pyMacWarranty)
#
# Rocky Waters - Humboldt State University - Oct 2016
#

function mnfdate()
{
 local WEEK=$1 YEAR=$2
 local JAN1_WEEK JAN1_DAY
 local FIRST_MON
 local DATE_FMT="%d %b %Y"
 local DATEYEAR DAYS DAYSTMP

 DATEYEAR="01-Jan-$YEAR"
 JAN1_WEEK=$(date -jf "%d-%b-%Y" "$DATEYEAR" +%W)
 JAN1_DAY=$(date -jf "%d-%b-%Y" "$DATEYEAR" +%u)

 # If Jan 1 is Monday then use Jan 1, else calculate what date is first Monday of year
 if ((JAN1_WEEK))
 then
   FIRST_MON=$YEAR-Jan-01
 else
   FIRST_MON=$YEAR-Jan-$((01 + (7 - JAN1_DAY + 1) ))
 fi
 (( DAYSTMP = $WEEK * 7 ))
 DAYS="-v+"$DAYSTMP"d"
 DATEYEAR=$(date -j -f "%Y-%b-%d" $DAYS "$FIRST_MON" +"%Y-%m-%d")
 echo "$DATEYEAR"
}

SERIAL=`ioreg -c IOPlatformExpertDevice -d 2 | awk -F" '/IOPlatformSerialNumber/{print $(NF-1)}'`

if [ "${#SERIAL}" = '11' ]
  then
  # Old Serial number format (11 Chars) - Changed in 2010
  # Year is third character in serial number
  YEAR=${SERIAL:2:1}
  # Key index for years
  KEY_YEAR='   3456789012'
  # Strip characters from KEY_YEAR after the YEAR
  IDX_YEAR="${KEY_YEAR%%$YEAR*}"
  # Base year is 2000, Manufactured year is base year + position of the YEAR in the KEY_YEAR
  MNF_YEAR=$((2000 + ${#IDX_YEAR}))
  # Week manufactured is characters 4-5 in serial number
  WEEK=${SERIAL:3:2}
  # Get an approximate date manufactured given the week number and year
  MNFDATEAPPROX=$(mnfdate $WEEK $MNFYEAR)

elif [ "${#SERIAL}" = '12' ]
  then
  # New Serial number format (12 chars)
  # Key index for years: c is first half of 2010, d is second half 2010...
  # goes through 2019
  KEY_YEAR='cdfghjklmnpqrstvwxyz'
  # Year is fourth character in serial number
  YEAR=${SERIAL:3:1}
  # Set YEAR to lowercase
  YEAR=$(echo $YEAR| tr '[:upper:]' '[:lower:]')
  # Strip characters from KEY_YEAR after the YEAR
  IDX_YEAR="${KEY_YEAR%%$YEAR*}"
  # Base year is 2010, Manufactured year is base year + position of the YEAR in the KEY_YEAR
  # divided by 2 (each letter is half year)
  MNF_YEAR=$(( 2010 + ${#IDX_YEAR} / 2))
  # determine first or second half of year
  HALF_YEAR=$(( ${#IDX_YEAR} % 2))
  # Key index for weeks - only gives half year need to know forst or second half to calculate
  KEY_WEEK="123456789cdfghjklmnpqrtvwxy"
  # Week manufactured is character 5 in serial number
  ALPHA_WEEK=${SERIAL:4:1}
  # Set ALPHA_WEEK to lowercase
  ALPHA_WEEK=$(echo $ALPHA_WEEK| tr '[:upper:]' '[:lower:]')
  # Strip characters from KEY_WEEK after ALPHA_WEEK
  IDX_WEEK="${KEY_WEEK%%$ALPHA_WEEK*}"
  # Position of ALPHA_WEEK in KEY_WEEK
  IDX_WEEK=${#IDX_WEEK}
  # WEEK is the IDX_WEEK plus 26 weeks if it is the second half of the year
  WEEK=$(( IDX_WEEK + ( HALF_YEAR * 26 ) ))
  # Get an approximate date manufactured given the week number and year
  MNFDATEAPPROX=$(mnfdate $WEEK $MNF_YEAR)

fi

echo "<result>$MNFDATEAPPROX</result>"

View solution in original post

28 REPLIES 28

sdagley
Esteemed Contributor II

If you're only concerned about the actual age of the Mac, you could create an EA that decodes the machine's serial number (Google will find the formula for you) to report the age.

Goober22
New Contributor III

I am having issues finding the formula, all I can find is websites that will decode it for you.

Nix4Life
Valued Contributor

Munkireport has this information, I use it in conjuction with casper. It's php so perhaps you could look under the hood to get the formula:
munkireport

sdagley
Esteemed Contributor II

If you look at munkireport, the function you're interested in is estimate_manufactured_date() in file munkireport-php/app/helpers/warranty_helper.php

They refer to the same MacRumors article I was thinking of you'd find with Google: Apple Tweaks Serial Number Format With New MacBook Pro

Chris_Hafner
Valued Contributor II

I've always used the Warranty expiration, monitored by our GSX integration. Just another thought.

Rocky
New Contributor III

I put together an extension attribute script that gives approximate manufacture date. It is based on that part of Michael Lynn's script. GSX integration is better, of course, but has some issues for programmatically getting it for many machines as I understand (captcha and limit to number per day, unless you are a self repair shop).

#!/bin/sh
#
# Parse machine serial number for date (week and year) of manufacture
# based on pyMacWarranty by Michael Lynn (https://github.com/pudquick/pyMacWarranty)
#
# Rocky Waters - Humboldt State University - Oct 2016
#

function mnfdate()
{
 local WEEK=$1 YEAR=$2
 local JAN1_WEEK JAN1_DAY
 local FIRST_MON
 local DATE_FMT="%d %b %Y"
 local DATEYEAR DAYS DAYSTMP

 DATEYEAR="01-Jan-$YEAR"
 JAN1_WEEK=$(date -jf "%d-%b-%Y" "$DATEYEAR" +%W)
 JAN1_DAY=$(date -jf "%d-%b-%Y" "$DATEYEAR" +%u)

 # If Jan 1 is Monday then use Jan 1, else calculate what date is first Monday of year
 if ((JAN1_WEEK))
 then
   FIRST_MON=$YEAR-Jan-01
 else
   FIRST_MON=$YEAR-Jan-$((01 + (7 - JAN1_DAY + 1) ))
 fi
 (( DAYSTMP = $WEEK * 7 ))
 DAYS="-v+"$DAYSTMP"d"
 DATEYEAR=$(date -j -f "%Y-%b-%d" $DAYS "$FIRST_MON" +"%Y-%m-%d")
 echo "$DATEYEAR"
}

SERIAL=`ioreg -c IOPlatformExpertDevice -d 2 | awk -F" '/IOPlatformSerialNumber/{print $(NF-1)}'`

if [ "${#SERIAL}" = '11' ]
  then
  # Old Serial number format (11 Chars) - Changed in 2010
  # Year is third character in serial number
  YEAR=${SERIAL:2:1}
  # Key index for years
  KEY_YEAR='   3456789012'
  # Strip characters from KEY_YEAR after the YEAR
  IDX_YEAR="${KEY_YEAR%%$YEAR*}"
  # Base year is 2000, Manufactured year is base year + position of the YEAR in the KEY_YEAR
  MNF_YEAR=$((2000 + ${#IDX_YEAR}))
  # Week manufactured is characters 4-5 in serial number
  WEEK=${SERIAL:3:2}
  # Get an approximate date manufactured given the week number and year
  MNFDATEAPPROX=$(mnfdate $WEEK $MNFYEAR)

elif [ "${#SERIAL}" = '12' ]
  then
  # New Serial number format (12 chars)
  # Key index for years: c is first half of 2010, d is second half 2010...
  # goes through 2019
  KEY_YEAR='cdfghjklmnpqrstvwxyz'
  # Year is fourth character in serial number
  YEAR=${SERIAL:3:1}
  # Set YEAR to lowercase
  YEAR=$(echo $YEAR| tr '[:upper:]' '[:lower:]')
  # Strip characters from KEY_YEAR after the YEAR
  IDX_YEAR="${KEY_YEAR%%$YEAR*}"
  # Base year is 2010, Manufactured year is base year + position of the YEAR in the KEY_YEAR
  # divided by 2 (each letter is half year)
  MNF_YEAR=$(( 2010 + ${#IDX_YEAR} / 2))
  # determine first or second half of year
  HALF_YEAR=$(( ${#IDX_YEAR} % 2))
  # Key index for weeks - only gives half year need to know forst or second half to calculate
  KEY_WEEK="123456789cdfghjklmnpqrtvwxy"
  # Week manufactured is character 5 in serial number
  ALPHA_WEEK=${SERIAL:4:1}
  # Set ALPHA_WEEK to lowercase
  ALPHA_WEEK=$(echo $ALPHA_WEEK| tr '[:upper:]' '[:lower:]')
  # Strip characters from KEY_WEEK after ALPHA_WEEK
  IDX_WEEK="${KEY_WEEK%%$ALPHA_WEEK*}"
  # Position of ALPHA_WEEK in KEY_WEEK
  IDX_WEEK=${#IDX_WEEK}
  # WEEK is the IDX_WEEK plus 26 weeks if it is the second half of the year
  WEEK=$(( IDX_WEEK + ( HALF_YEAR * 26 ) ))
  # Get an approximate date manufactured given the week number and year
  MNFDATEAPPROX=$(mnfdate $WEEK $MNF_YEAR)

fi

echo "<result>$MNFDATEAPPROX</result>"

jamflund
New Contributor III

Can anyone confirm that this script works with BigSur. Thanks!

Goober22
New Contributor III

Thanks Rocky, I will see if that works for what I need

Chris_Hafner
Valued Contributor II

Yea, we're SSA. That said, this is a super cool EA. Nice work!

mjerome
New Contributor II

I'm new to this. How do I get this attribute (which I created the extension), how do I get it into the data I want.

Rocky
New Contributor III

If you have the extension attribute set up, then just set up an advanced search on it - since it is a date that is returned, you can either specify a date (before or after) or more/less than a specific number of days.

entrata
New Contributor II

I am also new to scripts and extension attributes.

I set this up as an extension attribute but when I put it into a smart group I get no results no matter how I put in the criteria, before YYYY-MM-DD or x days before. Is there some part of your script that I need to adjust for our own network?

deehar
New Contributor

@entrata Looks like there was a small typo in Rocky's script that would return an empty result for older machines with an 11-digit serial. The last line of the 11-digit serial conditional should be changed from:

MNFDATEAPPROX=$(mnfdate $WEEK $MNFYEAR)

to:

MNFDATEAPPROX=$(mnfdate $WEEK $MNF_YEAR)

($MNF_YEAR was missing an underscore). Otherwise, this script seems solid for me. Kudos, Rocky!

stevenjklein
Contributor II

@Rocky 's script (with corrections from @deehar) works for me. I'm a little unclear on how weeks are calculated. Are you using the ISO 8601 standard for numbering weeks?

It seems a reasonable presumption that Apple would follow the ISO standard.

But is it a presumption? Or has Apple documented this somewhere?

(Not that it really matters if some of my dates are a week off. I'm just curious.)

Rocky
New Contributor III

I'm not sure how Apple calculated the week. ISO standard would be a reasonable guess. In the Serial Number Apple uses a single character to indicate week:
KEY_WEEK="123456789cdfghjklmnpqrtvwxy"

The calculation came from Michael Lynn (https://github.com/pudquick/pyMacWarranty). I would have to do the calculations, but I believe the date that ends up being output for the first week is calculated as the first day in January to fall on a Monday.

garybidwell
Contributor III

@Rocky Its seems like Apple has just started re-using the same "year identifier" character again for devices made in this decade on the 12 digit serial numbers,

So the above script now incorrectly identifies a new MacBookPro16,1 purchased last month as being made in January 2010 and not January 2020
Example of part of a real MacBookPro16,1 serial number is: C02C25
The Mac's forth character "C" can now both mean the first half of 2010 or the first half of 2020.
There doesn't seem to be any other clear identifier in the new serial number to say its a new Mac made this decade - have you see this yourself yet?

orlandinim
New Contributor II

Thanks @Rocky, great job!

Only one simple question:
The original pyhton script pyMacWarranty by Michael Lynn calculate the week number by the line:

est_week = alpha_week.index(week) + (est_half * 26) - 1

that you in your script translate with:

WEEK=$(( IDX_WEEK + ( HALF_YEAR * 26 ) ))

so... why the -1 is disappeared?

And yes, the two scripts return slightly different results...
The final question is: what's the most correct calculation?

Thanks.

fernandez_payen
New Contributor III

Has anyone been able to get an updated EA for retrieving the manufacture date? Just instilled a policy to refresh client computers every 4 years so my Jamf smart group is not providing me with an accurate report.

stevenjklein
Contributor II

@fernandez.payen: So far as I'm aware, we don't need an updated EA.

@Rocky's script above works, if you make the single-line correction noted by @deehar .

Link to script: https://www.jamf.com/jamf-nation/discussions/21504/aged-out-macs#responseChild130157

Link to correction: https://www.jamf.com/jamf-nation/discussions/21504/aged-out-macs#responseChild176391

But also note that sometime in 2021, Apple will be switching to random serial numbers, so we will no longer be able to calculate manufacturer dates after that.

j_allenbrand
Contributor

I tried using this above but seem to be running into some issues.

Executing Policy Serial number (computer Age)
Running script Serial Number Age...
Script exit code: 0
Script result: <result>2019-04-08</result> but Per JAMF Model:
13-inch Retina MacBook Pro (Mid 2017)

So there seems to be a miss match.

fernandez_payen
New Contributor III

@stevenjklein When this EA is ran on the new 2020 Mac models, it is coming back with a 2010 manufacturer date.

stevenjklein
Contributor II

@fernandez.payen : The whole issue will soon be academic, as Apple has announced plans to start using random serial numbers.

I don't know why I didn't think of this earlier, but what we really need is a script to check the model identifier. (For example, MacBookPro5,5)

I'll look into creating such a script.

Anything come out of this? My org is finally wanting to track our inventory and keep things more current. 

jamflund
New Contributor III

Can anyone confirm that this script works with BigSur? 

bradtchapman
Valued Contributor II

The script works; however, the serial number format has already changed for new computers to something totally random. 

For existing computers, Apple started recycling the fourth character that identifies the year of manufacture.  So a 2020 MacBook Pro with a SN that starts with C02D would be identified as 2010 by the script.  There's no way to compensate for this.

kwoodard
Contributor III

I have been trying to get this to work all morning and have failed. I have the script setup as an EA. I create a new search with the EA checked, along with some other identifiers...results are always zero. I am not sure where I am messing up.

swapple
Contributor III

For me the dates returned stop in 2019.  Any script like this for the new serial numbers?  We don't have GSX to pull from.