Checking if a Mac is Automated Device Enrollment Eligible – Jamf API Script

A dilemma I have come across recently is if a Mac is purchased and not in Apple Business Manager (ABM) then it won’t be an Automated Device Enrollment (ADE) eligible device so can’t be deployed as a ZeroTouch device but needs to either be registered which is now only just possible if its a Monterey device but has overheads (iOS15, Configurator 2, Managed Apple ID) or built using a more manual process.

But how can you easily check without booting the Mac?

You could log into ABM and check or log in to Jamf but what if you don’t want to add 10’s or even 100’s of accounts to your ABM or Jamf for your local engineers to access. At present, there is no API to ABM and the minimum account level of Staff still allows access to VPP settings so letting in users is not always ideal and adding and maintaining Jamf Pro access could be a chore based on your setup.

Well, there is an easier way and that’s using the Jamf Pro API (not classic) and device-enrollments/[ID]/devices request and creating a simple command-line tool.

When a device is in ABM and you have set up and pointed ABM to Jamf and in Jamf you have set up an ADE instance you will see your Macs in ABM in Jamf and you can assign these to your Pre-Stage for your Zero-touch deployment.

With a little bit of bash and a Jamf account with specific read-only access, it’s possible to search this list. When I say search you can’t do a direct search, there is no API reference to do that but what we can do is download a list of ADE devices, search it and then delete the download.

First, create an user account in Jamf and set it to only have the following access, that’s all it needs just Read Only for Automated Device Enrollment ensure you just have this in place and not write access.

Once done copy the script below and edit the URL, username adding your API username and password plus the ID number of your ADE instance in Jamf, if you just have one the ID is usually 1.

Save and run the script from the terminal and follow the instructions, enter the full serial number when requested.

The screenshot below shows using the serial 123456 and of course, it’s not found.

The screenshot below shows using a serial starting VQ and it’s found.

The full script is below, like a lot of scripting, there are multiple ways to achieve the same results.
Credit to William Smith aka talkingmoose as the script here really helped kick this off.

If I quickly go through it, after setting the URL and API details the file downloaded is set as my_ade_devices.txt, the next line is the ID of the ADE instance in Jamf. We then set some colours to make the script a bit easier to read.

The next part is the text on the screen when the script is run and a Y/N question. If we choose no we end if we continue we print some more text on screen then make the connection to jamf and do a curl to get the info and save it as a .txt file.

After that, we do our search for the user-entered text and then show results based on if the entered item is found in the text file and finally we clean up afterwards and delete the file.

To distribute the script I make it a .command file, chmod +x so its a simple double click to run. Then package using Jamf Composer and add to Self Service. Self Sevice has an IT Engineers section only and the Engneers must login to Self Service to access and install. I also tend to add some instructions or a video on how to use and link this in the Self Service description.

And thats is, simple command line tool to check your if a Mac is in ADE in jamf.

Feel free to take a copy change, improve as required.
As always using this is at your risk.

#!/bin/bash

# Gets the device in ADE in Jamf

do_file_action(){

# server connection information
URL="https://YOURJamfURL"
username="JAMFAPIUSERNAME"
password="JAMFAPIPASSWORD"

# Local file of the ADE devices
downloadfile="my_ade_devices.txt"

# provide the Jamf Pro ID of the Automated Device Enrollment instance; look in the URL when viewing the Automated Device Enrollment instance name
ADEID="1"

# Set the colour variable to make the script a bit more fun
bold='\033[1;30m'
green='\033[0;32m'
greenbold='\033[1;32m'
red='\033[0;31m'
redbold='\033[1;31m'
# Clear the colour variable
clear='\033[0m'

printf "\n"
printf "${bold}Mac ADE Check${clear}"
sleep 2
printf "\n"
echo "This app will check if the serial number entered is a Mac in ADE in your Jamf environment."
sleep 1
printf "\n"

# Continue question
read -r -p "Do you want to continue [y/n]" input
case $input in
[yY][eE][sS]|[yY])
echo "Continuing..."
;;
[nN][oO]|[nN])
echo "Thanks for this tool. Goodbye."
printf "Please close this app by click the ${redbold}red${clear} dot at the top left."
exit
;;
*)
echo "Answer was not Y or n so exiting"
exit
;;
esac

echo "Getting a list of Mac's in that are registered in Apple Device Enrolment."
printf "${bold}Please wait, this may take 20-30 seconds...${clear}"


# created base64-encoded credentials
encodedCredentials=$( printf "$username:$password" | /usr/bin/iconv -t ISO-8859-1 | /usr/bin/base64 -i - )

# generate an auth token
authToken=$( /usr/bin/curl "$URL/uapi/auth/tokens" \
--silent \
--request POST \
--header "Authorization: Basic $encodedCredentials" )

# parse authToken for token, omit expiration
token=$( /usr/bin/awk -F \" '{ print $4 }' <<< "$authToken" | /usr/bin/xargs )

# submit scope for ADE ID
/usr/bin/curl "$URL/uapi/v1/device-enrollments/$ADEID/devices" -o "$downloadfile" \
--silent \
--request GET \
--header "Authorization: Bearer $token" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \

# expire the auth token
/usr/bin/curl "$URL/uapi/auth/invalidateToken" \
--silent \
--request POST \
--header "Authorization: Bearer $token"

# hide the downloaded file
chflags hidden "$downloadfile"

printf "\n"
printf "List of ADE Mac's downloaded.\n \n"
printf "Enter the Mac serial number of the Mac you need to check.\n"
printf "You can find the Mac serial number on the box or on the base of the Mac.\n"
printf "${bold}Enter serial number:${clear}"
read varserial
printf "The Mac serial number entered is: ${bold}"$varserial"${clear} \n\n"
echo "Searching...."
sleep 2
if grep -w -q "$varserial" $downloadfile;
then
printf "${greenbold}"$varserial"${clear} - ${bold}The Mac is registered.${clear} \n"
printf "Thanks for using this tool. Goodbye.\n \n"
rm $downloadfile
else
printf "${redbold}Computer Says No! - Mac Not Found ${clear}\n \n"

printf "${bold}This Mac is Not on ADE${clear} \n \n"


printf "${bold}Would you like to search again [y/n]${clear}"
read answer
if [ $answer = y ]
then do_file_action
exit 0
elif [ $answer = n ]
then echo "Thanks for this tool. Goodbye."
printf "Please close this app by click the ${redbold}red${clear} dot at the top left."
rm $downloadfile
exit
fi
fi
}

do_file_action

Jamf Connect – Configuration file does not specify default realm error message

A few months ago we added Jamf Connect and all was good but recently and we started getting this error when a user was changing their password.

Jamf Connect Error

Jamf state this could happen If the Mac was previously bound to AD but we also saw this on new Mac’s that had never been bound, so the jury is out on the cause, but the good news is there is a simple fix.

The fix is to add a krb5.conf file to the user’s Mac (/etc/krb5.conf) and specify in that file the Kerberos realm.

For example the krb5.conf file would be:

[libdefaults]
default_realm=UK.DOMAIN.COM

Now we could of created this file, packaged in Composer, added to Jamf, created a policy, scoped and distributed but we have 5 realms (examples below)….so it seemed a lot of work to create 5 pkgs.

DOMAIN.COM
UK.DOMAIN.COM
US.DOMAIN.COM
ASIAPAC.DOMAIN.COM
RD.DOMAIN.COM

However, a quick search on https://community.jamf.com and this wonderful script by bigmikeey caught my eye. A quick test and we were in business. The Jamf community really is a fantastic resource.

All you need to do is change the MCSLTD.INTERNAL in the script below to your domain. Add the script to Jamf, create a policy and deploy. The krb5.conf is created on the users Mac and Jamf connect is killed and relaunched.

#!/bin/bash

#Find the Current users ID.
currentUser=$( /usr/bin/stat -f %Su "/dev/console" )
userID=$( /usr/bin/id -u "$currentUser" )

#Unload the Jamf Connect Menu bar app
/bin/launchctl bootout gui/"$userID" /Library/LaunchAgents/com.jamf.connect.plist

#Create the Kerberos file
touch /etc/krb5.conf

#Write the content into the file
cat << 'EOT' >/etc/krb5.conf

[libdefaults]
default_realm=MCSLTD.INTERNAL

EOT

#wait 2 seconds
sleep 2

#Kill any running instance with the name Jamf Connect
pkill "Jamf Connect"

#Re-launch Jamf Connect Menu bar app (by launching the LaunchAgent)
/bin/launchctl bootstrap gui/"$userID" /Library/LaunchAgents/com.jamf.connect.plist

exit 0

For our needs, we would have 5 scripts and change each one to the specific domain…..which I did! But a far more efficient way is to make the default_realm= value a variable, then have that variable set a script parameter in Jamf. So a single script, much more efficient.

So below is my version. You can see the domain variable is $4 and the default_realm=$domain

#!/bin/bash

#Jamf Script Parameter
domain=$4

#Find the Current users ID.
currentUser=$( /usr/bin/stat -f %Su "/dev/console" )
userID=$( /usr/bin/id -u "$currentUser" )

#Unload the Jamf Connect Menu bar app
/bin/launchctl bootout gui/"$userID" /Library/LaunchAgents/com.jamf.connect.plist

#Create the Kerberos file
touch /etc/krb5.conf

#Write the content into the file
cat << 'EOT' >/etc/krb5.conf

[libdefaults]
default_realm=$domain

EOT

#wait 2 seconds
sleep 2

#Kill any running instance with the name Jamf Connect
pkill "Jamf Connect"

#Re-launch Jamf Connect Menu bar app (by launching the LaunchAgent)
/bin/launchctl bootstrap gui/"$userID" /Library/LaunchAgents/com.jamf.connect.plist

exit 0

In the script Options section in Jamf you can set a Parameter Label as below:

Jamf Script Parameter Label

Then when you create your policy and add the script you can enter your domain in the script Parameter Value and you will see your label set in script Options:
Jamf Script Variable

Now when you scope and trigger the script the krb5.conf will be created with the value entered so in this case UK.DOMAIN.COM

Once again thanks to the Jamf community for this fix 😊

“Apple is not Enterprise” – Why this is not true.

I often hear from managers and IT leaders that “Apple is not Enterprise focused because…” and then a number of variations. There seems to be something in common with the people saying this and that is what they think is Enterprise. Their Enterprise is usually based 10,20 or 40 years experience of a Windows centric environments.

Apple is certainly Enterprise focused. I work with some amazing Apple Enterprise Engineers, Developers and Account Managers all from their Enterprise Division. Plus, support from Apple Enterprise Support and AppleCare for Enterprise. All this along with Apple Seed programme allows me extra insight and direct named contacts to aid deploying devices in the Enterprise and keeping them running with superb help and support. If Apple were not Enterprise why would these areas exist.

If you want control, simple use Jamf Pro. Jamf and Apple have a long-standing relationship, Jamf have a history of being Day 0 ready for major macOS releases. The same is true of Cisco, there is a deep partnership. These 3rd parties and products are for the Enterprise, you don’t need to run Jamf at home. Why would Apple create and maintain these relationships if they were not Enterprise focused.

As said this opinion of Apple bot being Enterprise seems to come from the individuals understanding of what Enterprise is and that is mainly a Microsoft Windows centric view with devices using Active Directory having layers of additional security and user controls who are seemingly unwilling to depart from these thought processes.

Enterprise is in fact what you make it and it can be much better with Mac’s and Jamf Pro. The best Enterprise environments are where the user has choice, that way the user feels more confident and will be more comfortable with what they know and therefore more productive.

Mac’s can fit into your infrastructure with ease you just need to respect and work with its differences and not against them and not try and force them to work the way your Windows devices work, after all it’s a different platform, it requires different thinking, different support and management.

In the late 90’s and early 2000’s Apple had a Think Different campaign for their consumer products, that needs to come across to Enterprise IT leaders.

macOS Big Sur 11.4 and Proxy via Profile – Finally Fixed

macOS 11.4 was released this week and one issue we have been dealing with and raised with Apple in December 2020 is finally fixed.

The Big Sur 11.4 release notes for Enterprise are as below the one we are interested in for this article is number 5.

  1. Using MDM to install software updates works more reliably on Mac computers with Apple silicon.
  2. Resolves an issue where first login with a mobile account does not create a login keychain.
  3. Setup Assistant no longer skips Location Services when account creation is skipped by MDM.
  4. App installations from MDM are no longer canceled during setup when the country is changed.
  5. Resolves an issue where DNS lookups may fail when using an authenticated proxy or a proxy configured with a PAC file.
  6. Resolves an issue where the Kerberos SSO extension may fail to renew credentials.
  7. Resolves an issue where iCloud Keychain sync could not be restricted by MDM.
  8. Resolves an issue where StorNext volumes from fsforeignservers file would not mount automatically.

This item refers to when a Profile is used to set a Proxy using a authenticate PAC URL. This worked fine in Catalina but in Big Sur when the Profile was used this then caused a failure to contact our internal domain name due to the DNS lookup issue it caused. This was easily demonstrated by pinging the domain and then adding and removing the Profile. With the Profile ping failed, without the Profile ping was successful. The same domain is also used for our SSO extension so we could not use a Profile until now to set the proxy.

The workaround was a bash script which was set to run via a Jamf policy at check-in or Network Change. The script ran through every network connection available and entered the PAC URL for the Automatic Proxy Configuration URL box.

As this item was still user editable (we don’t lock Network prefs to users) it could be removed by the user so hence why it was run on Check-in or Network Change and therfore added back in in case the user had taken it out. The reason they take out is the Proxy service we use can be dreadfully slow but it’s there to protect the device so is a Security requirement.

The issue here is the policy can run 100’s of times a day on a single device and each time it runs its recoded to the Jamf database, so it’s not efficient.

Now this is fixed we have gone back to a simple Profile configured in Jamf with a Proxy payload. This is silently deployed to Macs as they upgrade to 11.4 and the current Policy is excluded, this is all done using scoping in jamf to smart groups plus we have a clean-up script that removes the entry from the Network prefs as Profile settings are not shown in GUI. The change is totally seamless and users don’t even realise it’s happening.

It’s great to have this working again but its issues like this and the time to resolve especially when its a regression from a previous OS that slow down the roll out of each macOS release in Enterprise environments. With this fix plus the SSO fixes Big Sur now feels Enterprise ready but 7 months after release and two weeks before WWDC when we will no doubt hear about macOS 12.

macOS Kerberos SSO authentication in Google Chrome

SSO on macOS for Safari is out of the box, no config required. As long as the Mac is getting a valid Kerberos ticket which can be checked in the terminal or via the Ticket Viewer app you are good to go.

Google Chrome however takes a little bit of config which can be set via a Profile for SSO to work.

The settings required are:
AuthNegotiateDelegateAllowlist
AuthServerAllowlist

Notice both these end Allowlist and not Whitelist. From Chrome 86 Allowlist was introduced and Whitelist was deprecated.

Deprecated
https://chromeenterprise.google/policies/#AuthNegotiateDelegateWhitelist
https://chromeenterprise.google/policies/#AuthServerWhitelist

Current
https://chromeenterprise.google/policies/#AuthNegotiateDelegateAllowlist
https://chromeenterprise.google/policies/#AuthServerAllowlist

You can check if these are in place by typing chrome://policy into the URL bar, this will show the policies set
Chrome Policy Settings

If not set these SSO will fail in Chrome.

To set in Jamf you could create a mobileconfig or plist locally, then create a new Profile with a Application & Custom Settings payload and use the Upload section but a better option is to use the in-built External Applications section of the Application & Custom Settings payload and select the Jamf Repository and choose a JSON schema.
Jamf Profile

Once saved this will show all the Google Chrome options as a GUI where you can select the items you wish to configure and enter the settings required.

For SSO pick the two items described and enter your domain or domains.
Chrome Settings Jamf Profile

These can be wildcarded and multiple domains separated with a comma

*mydomain1.net, *.mydomain2.com

Once in place, simply scope in the normal way and test out.

JSON Schemas in jamf

From around jamf 10.19 a new feature was added that is slowly gain more traction and that’s the ability to use JSON (JavaScript Object Notation) schemas in the Applications and Custom Settings payload. Over time jamf have updated and improved its functionality and ease of use. This article assumes you are on at least jamf 10.26.

So what does all that mean?
It means an app vendor, developer or anyone can create a schema that in turn can be used to create the Profile configuration directly in jamf rather than having to write XML or use other tools like ProfileCreator. This simplifies Profile creation and is a huge time saver.

So let’s give it a go….

Create a new Profile.
Name it and select a category just like creating any other Profile:

Once done scroll down and select the Applications and Custom Settings payload.

It will open up and show a few options:
Jamf Applications
External Applications
Upload

If you use other jamf applications such as jamf connect suite (Login, Sync Verify) you can set up the Profiles from here directly in jamf. For this article though we are going to use the External Applications option.

Select External Applications then click the +Add button on the right. The page will show as below with a Source dropdown:

Click the dropdown and you will see Repository and Custom Schema options. If you select Repository you will see an Application Domain dropdown and within this schemas for Chrome, MAU, Office, Outlook.

For this article lets choose the Custom Schema from the Source dropdown:

You will see a Preference Domain box and the Custom Schema box.

If we want to set up a Profile for NoMAD we would first enter the plist file name. The plist contains the configuration and will be created on the devices we scope the Profile to.

In the Preference Domain box enter com.trusourcelabs.NoMAD.json

Note the name is case sensitive so enter exactly as shown.

 In the Custom Schema box we can now enter the JSON schema, lucky for us one has been created and you can find it here:
https://github.com/Jamf-Custom-Profile-Schemas/mscottblake-schemas/blob/master/com.trusourcelabs.NoMAD.json

Carefully copy and paste the XML from github into the Custom Schema field. I usually select the Raw view on github, then select all and copy.

It should now look like this:

Now scroll down past the Custom Schema field and you will see the JSON XML transformed into items configurable items right inside jamf.

Here you can see the Key’s you would set in XML as configurable items:

You may fine all the configuration items are shown but you can customise the list from the +Add/Remove Keys. Simply check or uncheck the items you require and click OK:

Go ahead and create your Profile by selecting the options you want and adding in the configuration required. The schema creator has helpfully added links to the NoMAD pages for each item so you can quickly see what the config item refers to. Remember to click Save regularly.

When you are ready to test the Profile scope to you test Mac and save again to push it to that device.

And that’s it. No more creating XML by hand, uploading, testing, editing and repeat. It can all be done in jamf.
This is just one example of a schema there are many other JSON schemas on github and Jamf Nation, here are a few I’ve found:

Talkingmoose’s substantial list:
https://github.com/talkingmoose/jamf-manifests

AgileBits 1Password
Apple Safari
Google Chrome and Keystone – although this is already built it as described above
Jamf Products

Microsoft Defender, Edge, OneDrive, Outlook
SAP
https://github.com/Jamf-Custom-Profile-Schemas/JSON-Schema-for-Jamf-Pro-Applications-and-Settings-MDM-Payload

Safari
NoMAD
Zoom
macOS Notifications
https://github.com/bpstuder/jamf_custom_schema

BBedit
Transmit
https://github.com/chrisgrande/jamf-profile-schemas

Managed Installs
https://github.com/joshua-d-miller/JAMF-JSON-Schema

MS Edge
https://blogs.windows.com/msedgedev/2020/02/20/edge-profiles-jamf-applications-custom-settings/

Cant find the schema you want?
I’ve not tried this but there is an app to help create the JSON schema, give it a go and if it works out publish it for other on github.
Schema Builder
https://github.com/BIG-RAT/Managed-App-Schema-Builder

Unbind from AD – The Silent Way

In my last post I detailed how NoMAD Login 1.4 can be used to silently convert a Mobile account to a Local account using the demobilize function. If the Mac is bound to AD the bind will still in place so after the demobilize is completed you may want to remove the bind as well.

Again, this can be done silently and is very simple using an MDM like jamf.

Create a new policy and name something like AD Unbind
Set the Trigger to be Check-in
Set the Execution Frequency to be Once per Computer
Add a Files & Processes payload
File and Processes

In the Execute command section add the following:
Execute Command

dsconfigad -force -remove -u test -p test

This will remove the bind, the -u (username) and -p (password) can be anything you want.
Scope the policy to your devices and save.

On next check-in the Bind will be gone. To test grab a Mac that is bound to AD, open the terminal and force a check-in with
sudo jamf policy

You will see the policy run and then if check System Preferences > Users and Groups and click the Login Options and you will where the Bind domain use to show with a green dot will now just show as below.

Bind Gone

That’s it.

Mobile to Local – The Silent Way

Apple is encouraging Enterprise Mac Admins to shift away from binding to Active Directory and Mobile accounts. If you have Mac’s with Mobile accounts they can be converted to Local and there are some great scripts to do this. The one from Rich Trouton is very good and there is also a Swift app by Leslie Helou. However, these require user interaction if you want a silent way to switch there is a very simple alternative option.

NoMAD Login 1.4+ allows silent Mobile to Local conversion using the demobilize function and it’s very simple to deploy via jamf MDM.

Head over to the NoMAD Login page on gitlab and have a quick read about NoMAD Login:
https://gitlab.com/orchardandgrove-oss/NoMADLogin-AD/-/wikis/home

Download NoMAD Login here:
https://files.nomad.menu/NoMAD-Login-AD.pkg

Once you have download the pkg add it to jamf in the usual way create a new Policy and add NoMAD-Login-AD.pkg to the Packages payload:
Jamf Policy

Add to the Policy a Files & Processes payload:
File and Processes

In the Execute Command section add the following:
Execute Command

authchanger -reset -demobilize;defaults write /Library/Preferences/menu.nomad.login.ad.plist DemobilizeUsers -bool true;sudo jamf recon

This command will instruct NoMAD Login to demobilize which is NoMAD Login speak for converting from Mobile to Local and for good measure run a jamf recon.

The policy can be scoped to your devices as you require and run with the options you require for example at Check-in. The policy only needs to run once. You could make it a Self Service item and instruct your users to run from Self Service when they require.

Once run the user will require to log our or reboot and at next login the account will switch from Mobile to Local. You could request this in your policy or force a reboot but as we want this to be silent simply wait for the user to perform the action. The first time they do login after the policy has run this the login may be a little slower.

You may also want to collect account information about which type of account is on the Mac. This can be done via a simple Extension Attribute script.

Create an EA script using the below:

#!/bin/sh
NETACCLIST=`dscl . list /Users OriginalNodeName | awk '{print $1}' 2>/dev/null`
if [ "$NETACCLIST" == "" ]; then
echo "<result>Local</result>"
else
echo "<result>Mobile</result>"
fi
exit 0

When a Mac next does a full jamf inventory update the account type will be collected and show:
Mobile or Local

If you have a mixed environment of Mobile and Local accounts set up the EA and let it run for a few days or weeks collecting device account type. Then create a Smart Group to show devices that have Mobile accounts and scope your Mobile to Local Policy to the smart group.

If the Mac is Bound to AD the demobilize will leave the bind in place. This can be removed when required by another simple policy which I’ve detailed in this post

Happy demobilizing.