Skip to content

Instantly share code, notes, and snippets.

@shurkin18
Last active October 3, 2023 21:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shurkin18/b8de3de44b184f1f8802b860639a9b4d to your computer and use it in GitHub Desktop.
Save shurkin18/b8de3de44b184f1f8802b860639a9b4d to your computer and use it in GitHub Desktop.
JAMF UAPI LAPS script for continuous local administrator password change, with password securely stored in JSS
#!/bin/bash
#################################################################################################################
# This script can be used together with "JAMF UAPI LAPS initialization script which will set LAPS Computer Extension Attribute to a desired password"
# URL of the other script: https://gist.github.com/shurkin18/50ae6e9da329a9332592df19e10744bb
#################################################################################################################
# Please note: this script requires jq JSON parser to be installed on the mac, otherwise the script won't work
# You can install jq JSON parser using brew by running this script, which will install brew and jq automatically (non-interactive):
# https://gist.github.com/shurkin18/62ec34967794a32f9d63615db881ab5c
#
# There is also an alternative way of running jq JSON parser, without installing the whole brew suite
# You can download the jq binary here: https://techstoreon.com/files/jq-osx-amd64
# Pre-load it to each mac via the policy and store it somewhere (in /var for example) and just point your script to it
# every time jq needs to be used > jq="/usr/local/jq/jq-osx-amd64" and simply use jq as $jq
#
# NOTE: Can add a check if jq is present to the top of the script and then download/install it with a policy to ensure the script will work
# if [ ! -f "$jq" ]; then
# <<<DOWNLOAD/INSTALL WITH THE POLICY jq BINARY>>
#################################################################################################################
# This script will reset local administrator password on regular basis (whatever you set the policy frequency to)
# and store it in JSS extension attribute per "lapsextattrib" variable
#
# Setup:
# 1) Update the following variables as needed: "apiUser", "apiPass", "apiURL", "resetUser", "lapsextattrib"
# 2) Create a string/textfield Computer Extension Attribute on JSS, which should match: "lapsextattrib" variable
# 3) "resetUser" should be set to local computer's administrator account name
# 4) The policy with this script should be set to Ongoing with desired frequency to constantly reset the local
# administrator account "resetUser" and save the most current password in JSS LAPS Computer Extension Attribute "lapsextattrib"
# 5) For full automation, combine this script with the LAPS initialization script:
# https://gist.github.com/shurkin18/b8de3de44b184f1f8802b860639a9b4d
#
# There are several checks of the password change procedure and if any of them fails, LAPS ("lapsextattrib")
# will be set with PASSNOTWORKING, for which a Smart Group can be created to track these down, furthermore
# you can add a policy which will delete and re-create the local administrator account for affected macs,
# make sure you include the LAPS inialize script with that policy (https://gist.github.com/shurkin18/50ae6e9da329a9332592df19e10744bb),
# as with the local administrator account re-creation, you need to make sure LAPS Computer Extension Attribute matches
# the password the account will receive
#################################################################################################################
# server connection information
apiUser="API USERNAME"
apiPass="API PASSWORD"
apiURL="API URL"
#variables which need to be updated depending on what your company uses:
resetUser="local administrator account name"
lapsextattrib="LAPS"
################################################################
# DO NOT TOUCH BELOW UNLESS YOU KNOW WHAT YOU ARE DOING! #######
################################################################
# created base64-encoded credentials
encodedCredentials=$( printf "$apiUser:$apiPass" | /usr/bin/iconv -t ISO-8859-1 | /usr/bin/base64 -i - )
# generate an auth token
authToken=$( /usr/bin/curl "$apiURL/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 )
#Generate new password and store it in newPass variable
newPass=$(openssl rand -base64 10 | tr -d OoIi1lLS | head -c14;echo)
####################################################################
#
# ┌─── openssl is used to create
# │ a random Base64 string
# │ ┌── remove ambiguous characters
# │ │
# ┌──────────┴──────────┐ ┌───┴────────┐
# openssl rand -base64 10 | tr -d OoIi1lLS | head -c14;echo
# └──────┬─────┘
# │
# prints the first 14 characters ──────┘
# of the randomly generated string
#
####################################################################################################
# Check if js JSON parser is present on the mac and if not - exit out of the script immediately
if [ -z $(which jq) ]; then
echo "jq JSON parser is not installed on the machine, unable to run the script, please install it using brew or any other method."
exit 1
fi
udid=$(/usr/sbin/system_profiler SPHardwareDataType | /usr/bin/awk '/Hardware UUID:/ { print $3 }')
#Determine current LAPS extension attribute definitionId
lapsdefid=`curl --request GET \
--url "$apiURL/uapi/v1/computers-inventory?section=GENERAL&page=0&page-size=100&sort=id%3Aasc&filter=udid%3D%3D$udid" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" | jq | grep '"name": "'$lapsextattrib'"' -B1 | awk -F '"' '/definitionId/ { print $4 }'`
echo "lapsdefid is: $lapsdefid"
#Obtain current LAPS password from JSS computer's LAPS extension attribute via UAPI
oldPass=`curl --request GET \
--url "$apiURL/uapi/v1/computers-inventory?section=GENERAL&page=0&page-size=100&sort=id%3Aasc&filter=udid%3D%3D$udid" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" | jq -r '.results[].general' | sed -n -e '/'$lapsextattrib'/,/STRING/ p' | grep -A 1 "values" | sed 's/\[//g;s/\]//g' | sed 's/ //g' | sed 's/values//g' | sed 's/,//g' | sed 's/"//g' | sed 's/://g' | tail -n +2`
echo "oldPass is (current LAPS password pulled from JSS): $oldPass"
echo "resetUser is (should be local admin account) is: $resetUser"
# Set the location of the jamf binary for the jamf_binary variable.
jamf_binary="/usr/local/jamf/bin/jamf"
echo "jamf_binary location is set to: $jamf_binary"
# Verify the current administrator Password exists in JSS LAPS extention attribute
if [ -z "$oldPass" ] || [ "$oldPass" == " " ] || [ "$oldPass" == "" ] ;then
echo "No Password is stored in LAPS. Current oldPass variable value is: $oldPass"
#Update LAPS with "PASSNOTWORKING" if you wish to use Smart Group to catch macs where LAPS is not working
#Determine JSS ID of the machine using new UAPI via correct syntax
idraw=`curl --request GET \
--url "$apiURL/uapi/v1/computers-inventory?section=GENERAL&page=0&page-size=100&sort=id%3Aasc&filter=udid%3D%3D$udid" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" | sed -n '4 p'`
#echo "idraw is $idraw"
#Extract the computer ID from raw data from JSS UAPI
#echo "idraw is: $idraw"
id="$(echo -e "$idraw" | cut -d '"' -f4)"
echo "id is: $id"
currentlocaladminpass="PASSNOTWORKING"
#Curl - adding extension attribute data with the correct UAPI syntax - is working
curl --request PATCH \
--url "$apiURL/uapi/v1/computers-inventory-detail/$id" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" \
--header "Content-Type: application/json" \
--data '
{
"general":
{
"extensionAttributes":
[
{
"definitionId": "'$lapsdefid'",
"values": ["'$currentlocaladminpass'"]
}
]
}
}
'
else
echo "A Password was found in LAPS."
fi
#Testing current local administrator password, if the command returns nothing > it's correct
passwdA=`dscl /Local/Default -authonly $resetUser $oldPass`
if [ "$passwdA" == "" ];then
echo "Local mac password for $resetUser is correct"
else
echo "Local mac password for $resetUser. is incorrect"
oldPass=""
#Update LAPS with "PASSNOTWORKING" if you wish to use Smart Group to catch macs where LAPS is not working
#Determine JSS ID of the machine using new UAPI via correct syntax
idraw=`curl --request GET \
--url "$apiURL/uapi/v1/computers-inventory?section=GENERAL&page=0&page-size=100&sort=id%3Aasc&filter=udid%3D%3D$udid" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" | sed -n '4 p'`
#echo "idraw is $idraw"
#Extract the computer ID from raw data from JSS UAPI
#echo "idraw is: $idraw"
id="$(echo -e "$idraw" | cut -d '"' -f4)"
echo "id is: $id"
currentlocaladminpass="PASSNOTWORKING"
##Curl - adding extension attribute data with the correct UAPI syntax - is working
curl --request PATCH \
--url "$apiURL/uapi/v1/computers-inventory-detail/$id" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" \
--header "Content-Type: application/json" \
--data '
{
"general":
{
"extensionAttributes":
[
{
"definitionId": "'$lapsdefid'",
"values": ["'$currentlocaladminpass'"]
}
]
}
}
'
fi
# Test if current password from LAPS extention attribute is empty and IF NOT - Update the local administrator password
echo "Running LAPS..."
if [ "$oldPass" == "" ];then
#Update LAPS with "PASSNOTWORKING" if you wish to use Smart Group to catch macs where LAPS is not working
#Determine JSS ID of the machine using new UAPI via correct syntax
idraw=`curl --request GET \
--url "$apiURL/uapi/v1/computers-inventory?section=GENERAL&page=0&page-size=100&sort=id%3Aasc&filter=udid%3D%3D$udid" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" | sed -n '4 p'`
#echo "idraw is $idraw"
#Extract the computer ID from raw data from JSS UAPI
#echo "idraw is: $idraw"
id="$(echo -e "$idraw" | cut -d '"' -f4)"
echo "id is: $id"
currentlocaladminpass="PASSNOTWORKING"
##Curl - adding extension attribute data with the correct UAPI syntax - is working
curl --request PATCH \
--url "$apiURL/uapi/v1/computers-inventory-detail/$id" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" \
--header "Content-Type: application/json" \
--data '
{
"general":
{
"extensionAttributes":
[
{
"definitionId": "'$lapsdefid'",
"values": ["'$currentlocaladminpass'"]
}
]
}
}
'
else
#Update local adminsitrator password to the new one using current account's credentials
echo "Updating password for $resetUser."
$jamf_binary resetPassword -updateLoginKeychain -username $resetUser -oldPassword $oldPass -password $newPass
# Update the LAPS Extention Attribute with the new password
echo "Recording new password for $resetUser into LAPS."
#Determine JSS ID of the machine using new UAPI via correct syntax
idraw=`curl --request GET \
--url "$apiURL/uapi/v1/computers-inventory?section=GENERAL&page=0&page-size=100&sort=id%3Aasc&filter=udid%3D%3D$udid" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" | sed -n '4 p'`
#echo "idraw is $idraw"
#Extract the computer ID from raw data from JSS UAPI
#echo "idraw is: $idraw"
id="$(echo -e "$idraw" | cut -d '"' -f4)"
echo "id is: $id"
##Curl - adding extension attribute data with the correct UAPI syntax - is working
curl --request PATCH \
--url "$apiURL/uapi/v1/computers-inventory-detail/$id" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" \
--header "Content-Type: application/json" \
--data '
{
"general":
{
"extensionAttributes":
[
{
"definitionId": "'$lapsdefid'",
"values": ["'$newPass'"]
}
]
}
}
'
fi
# Verify the new User Password
echo "Verifying new password for $resetUser."
passwdB=`dscl /Local/Default -authonly $resetUser $newPass`
if [ "$passwdB" == "" ];then
echo "New password for $resetUser is verified."
else
echo "Error: Password reset for $resetUser was not successful!"
#Update LAPS with "PASSNOTWORKING" if you wish to use Smart Group to catch macs where LAPS is not working
#Determine JSS ID of the machine using new UAPI via correct syntax
idraw=`curl --request GET \
--url "$apiURL/uapi/v1/computers-inventory?section=GENERAL&page=0&page-size=100&sort=id%3Aasc&filter=udid%3D%3D$udid" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" | sed -n '4 p'`
#echo "idraw is $idraw"
#Extract the computer ID from raw data from JSS UAPI
#echo "idraw is: $idraw"
id="$(echo -e "$idraw" | cut -d '"' -f4)"
echo "id is: $id"
currentlocaladminpass="PASSNOTWORKING"
##Curl - adding extension attribute data with the correct UAPI syntax - is working
curl --request PATCH \
--url "$apiURL/uapi/v1/computers-inventory-detail/$id" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" \
--header "Content-Type: application/json" \
--data '
{
"general":
{
"extensionAttributes":
[
{
"definitionId": "'$lapsdefid'",
"values": ["'$currentlocaladminpass'"]
}
]
}
}
'
fi
sleep 1
echo "LAPS Update Done."
#End of the UAPI scripts#
#\\\\\\\\\\\\\\\\\\\\\
# expire the auth token
/usr/bin/curl "$apiURL/uapi/auth/invalidateToken" \
--silent \
--request POST \
--header "Authorization: Bearer $token"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment