Script to Un-enrol

Euwanh
New Contributor III

Is there a script that can be pushed to a computer that will un-enroll it fully. I know that from the portal you can select 'remove from mdm' however I usually follow that up with going on the device and using the 'jamf removeFramework'. This will not be possible if the user is remote however?

Thanks

21 REPLIES 21

Hugonaut
Valued Contributor II

You can certainly push the sudo jamf -removeFramework to a machine and it will un-enroll it. How you deploy & run it is up to you but it is possible to do. The way I would do it is push the script itself to the end users computer so it lives locally on the machine and then have jamf call on the script locally to run. I've never done this but it should work.

________________
Looking for a Jamf Managed Service Provider? Look no further than Rocketman
________________


Virtual MacAdmins Monthly Meetup - First Friday, Every Month

sharriston
Contributor III

So a while back I found a script that creates a launch agent that will perform one last Recon, then unenroll the machine and use the api to unmanage the machine in your jss.

#!/bin/bash
# This script runs one last recon, updates the JSS API to show the machine is unmanaged, and then creates a launchd to remove the jamf framework.

# Define variables
apiUsername="apiuseraccount"
apiPassword="apipassword"
jssServer="https://your.jss.com:8443"
xmlString="<?xml version="1.0" encoding="UTF-8"?><computer><general><remote_management><managed>false</managed><management_username/></remote_management></general></computer>"

# Identify the location of the jamf binary for the jamf_binary variable.
CheckBinary (){
    # Identify location of jamf binary.
    jamf_binary=`/usr/bin/which jamf`

    if [[ "$jamf_binary" == "" ]] && [[ -e "/usr/sbin/jamf" ]] && [[ ! -e "/usr/local/bin/jamf" ]]; then
        jamf_binary="/usr/sbin/jamf"
    elif [[ "$jamf_binary" == "" ]] && [[ ! -e "/usr/sbin/jamf" ]] && [[ -e "/usr/local/bin/jamf" ]]; then
        jamf_binary="/usr/local/bin/jamf"
    elif [[ "$jamf_binary" == "" ]] && [[ -e "/usr/sbin/jamf" ]] && [[ -e "/usr/local/bin/jamf" ]]; then
        jamf_binary="/usr/local/bin/jamf"
    fi
}

# Update the computer inventory
RunRecon (){
    $jamf_binary recon
}

# Use the API to update the management status of the computer
# Takes a parameter for the computer name
UpdateAPI (){
    curl -sS -k -i -u ${apiUsername}:${apiPassword} -X PUT -H "Content-Type: text/xml" -d "${xmlString}" "${jssServer}/JSSResource/computers/name/$1"
}

# Create a temp launchd job in /private/tmp/ that uses a RunAtLoad key of false and StartInterval of 60 seconds
# This is done so that the script can return a result to the JSS before the framework is removed
CreateLaunchd (){
    echo "<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>Disabled</key>
        <false/>
        <key>Label</key>
        <string>tmp_removeframework</string>
        <key>ProgramArguments</key>
        <array>
            <string>$jamf_binary</string>
            <string>removeFramework</string>
        </array>
        <key>RunAtLoad</key>
        <false/>
        <key>StartInterval</key>
        <integer>60</integer>
    </dict>
    </plist>" > /private/tmp/removetools.plist

    chown root:wheel /private/tmp/removetools.plist
    chmod 644 /private/tmp/removetools.plist 

    /bin/launchctl load /private/tmp/removetools.plist
}

CheckBinary
RunRecon
UpdateAPI $2
CreateLaunchd
##ps -Ajc | grep loginwindow | awk '{print $2}' | xargs kill -9

This is not my script and I totally forgot the author of it but this is what we use to unenroll machines.

Chris_Hafner
Valued Contributor II

OK, we do this to all of our student computers prior to the end of the year. Long story short we have a policy that runs some un-installers directly and indirectly and loads a script in /tmp, of which I've included the relevant bits to your question. Said script does a LOT more in our environment, like stopping Cylance before running its uninstaller and cleaning up Adobe things, etc. But that's not what you've asked about so... While our offboarding policy calls the script after everything else there is finished. The lines you're interested in perform the following:

  • Remove all profiles
  • Delete the computer record from the JSS
  • Quit Self-Service
  • Remove the JAMF Framework
  • Remove our extra admin account
  • Reboots the machine (Thus deleting anything we've left in /tmp)
#!/bin/bash

# Removes any remaining profiles
profiles remove -forced -all

#This will remove the computers record in the JSS itself. 
#Please insert a JAMF user password where I've indicated. 
#I recommend creating a JAMF Pro account that can ONLY perform this lookup and delete function.
CAM=`networksetup -getmacaddress en1 | cut -c19-35 |sed 's/:/./g'`

curl -k -v -u Delete:JAMF-PASSWORD https://jss.URL.com:8443/JSSResource/computers/macaddress/$CAM -X DELETE

JAM=`networksetup -getmacaddress en0 | cut -c19-35 |sed 's/:/./g'`

curl -k -v -u Delete:JAMF-PASSWORD https://jss.URL.net:8443/JSSResource/computers/macaddress/$JAM -X DELETE

# This should quit Self-Service.
killall "Self Service"

#This will uninstall the JAMF binary        
/usr/local/jamf/bin/jamf -removeFramework

#This will delete the left over admin account
/usr/bin/dscl . -delete /Users/admin

# Reboot the machine.
shutdown -r now

exit 0

I'm sure there are better and more efficient ways to run this, but it works flawlessly in our environment. we do this for ~360 computers a year. The commands above have not been tested with Mojave but I should be getting to that very shortly.

sharriston
Contributor III

@Chris_Hafner The script worked 1000x better than mine but am I missing a variable somewhere to delete the record? I changed the Delete:JAMF-Password to my delete-account:deletepassword and the record remains but the machine is unenrolled.

Chris_Hafner
Valued Contributor II

What happens if you try executing this manually from the command line?

sharriston
Contributor III

Same thing Its alright I can play around with the api settings I was just wondering if there was a variable or something I was missing. Thanks for the help.

Chris_Hafner
Valued Contributor II

PERFECT! Well, as you suspect there IS a variable, but I would look at the permissions that you've given the "delete" user. The general syntax still works for me, so I doubt you're missing something in the script.

niacobelli
New Contributor III

@Chris_Hafner @sharriston I'm having an issue with the proper format for the API username and password in the script. Is the proper formatting Delete-username:password or is it something else that I'm missing? I get an error when I run the script. When I run the script manually it works, but it doesn't remove the record from the server.

tlarkin
Honored Contributor

So, I wrote a DEP Notify Decom solution for funsies after discussing how bad BYOD is with a buddy over a beer. Note, that this was for fun and is a POC and I tested it on some VMs, it is not something I use, nor do I plan on using it in prod anytime. It uses spotlight tagging to tag apps you install so you can later remove them in a decom type workflow. Of course, this makes sense for BYOD only, as for Org owned devices you just wipe and factory reset them.

Hopefully, someone finds this repo useful

Chris_Hafner
Valued Contributor II

@niacobelli Yes, this works for Mojave and Catalina. Your underline helps. Here's mine with a little more clarity, using your naming example.

#This will remove the computers record in the JSS itself
CAM=`networksetup -getmacaddress en1 | cut -c19-35 |sed 's/:/./g'`

curl -k -v -u username:password https://yourJAMF.URL.com:8443/JSSResource/computers/macaddress/$CAM -X DELETE

JAM=`networksetup -getmacaddress en0 | cut -c19-35 |sed 's/:/./g'`

curl -k -v -u username:password https://yourJAMF.URL.com:8443/JSSResource/computers/macaddress/$JAM -X DELETE

You really also have to make sure that the API user has the proper permissions on the JAMF Pro instance.

Chris_Hafner
Valued Contributor II

This year's full script below. I didn't change much

#!/bin/bash

# Sets the logged in user to $consoleuser
consoleuser=$(ls -l /dev/console | awk '{ print $3 }')

echo "logged in user is" $consoleuser

# Unloads PC Client launchagent and then quits PCClient

if [ -f /Library/LaunchAgents/com.papercut.client.agent.plist ]; then
    sudo -u "$consoleuser" /bin/launchctl unload $3/Library/LaunchAgents/com.papercut.client.agent.plist
fi

ProcessName="JavaAppLauncher"
ProcessCheck="$(ps axc | grep "${ProcessName}$" | awk '{print $5}')"

if [ "$ProcessCheck" = "$ProcessName" ]; then
    /usr/bin/killall JavaAppLauncher
fi

# This will remove some left over directories after the applications themselves are uninstalled
rm -rf /Applications/Logger Pro 3
rm -rf /Applications/Graphical Analysis 3
rm -rf /Applications/Acapela TTS for Mac
rm -rf /Applications/PCClient.app
rm -rf /Applications/Enterprise Connect.app
rm -rf /Applications/Vernier Graphical Analysis.app
rm -rf /Applications/Utilities/DEPNotify.app

# Removes any remaining profiles
profiles remove -forced -all

#Resets the "ignored" software update list.
sudo softwareupdate --reset-ignored

# Stop Cylance Service
launchctl unload /Library/launchdaemons/com.cylance.agent_service.plist

# Uninstall Cylance PROTECT
/Applications/Cylance/Uninstall CylancePROTECT.app/Contents/MacOS/Uninstall CylancePROTECT --noui 

#This will remove the computers record in the JSS itself
CAM=`networksetup -getmacaddress en1 | cut -c19-35 |sed 's/:/./g'`

curl -k -v -u username:password https://yourJAMF.URL.com:8443/JSSResource/computers/macaddress/$CAM -X DELETE

JAM=`networksetup -getmacaddress en0 | cut -c19-35 |sed 's/:/./g'`

curl -k -v -u username:password https://yourJAMF.URL.com:8443/JSSResource/computers/macaddress/$JAM -X DELETE

echo
ioreg -c "IOPlatformExpertDevice" | awk -F '"' '/IOPlatformSerialNumber/ {print $4}'
echo

# This should quit Self-Service and the remaining Sophos Processes.
killall "Self Service"

#Set login window to show username.
/usr/bin/defaults write /Library/Preferences/com.apple.loginwindow SHOWFULLNAME 0

#This will uninstall the JAMF binary        
/usr/local/jamf/bin/jamf -removeFramework

#This will delete any potential left over Admin account
/usr/bin/dscl . -delete /Users/admin
/usr/bin/dscl . -delete /Users/itd

# Log the user out of the machine.
shutdown -r now

exit 0

scottb
Honored Contributor

@Chris_Hafner - apologies for dopey question here, but would the script not stop at the point where the line below runs? Or is it called by a daemon?

/usr/local/jamf/bin/jamf -removeFramework

Chris_Hafner
Valued Contributor II

@scottb If this was a script being run within a policy then yea, that would be an issue. However, I load this script into /tmp and call it at the end of a policy. Since the binary is only telling the computer to run the script it doesn't matter that the script removes the jamfFramework.

The policy I use for this does a number of additional things, like loading this script and most of the application uninstallations as well as a separate script that elevates the logged-in user to admin. This is why that script has no issue removing the remaining admin accounts. Leaving it in tmp and forcing a restart clears the script. That's probably not good enough security for some though.

scottb
Honored Contributor

@Chris_Hafner - that's what I figured, but wanted to be sure...probably be doing something similar to this that we did before using a daemon, but yours has some interesting other useful bits as well - thanks for sharing that.
Our setup actually does this then enrolls the Macs into a new JSS (using a QuickAdd.pkg placed as well), so until Jamf come up with a better way, this be it!

Cheers

jweiss
New Contributor II

@Chris_Hafner picking up on this thread to see if you can answer something for me. 

 

We have the same need as this thread but we have a device that was setup via pre-stage enrollment without the user having the ability to remove Jamf profiles. For devices not setup this way our remove framework script works with no issues but when we push the same script to a device setup via prestage we are unable to get it working. 

Does the above script work for these type of devices? Unfortunately simply erasing this device is not an option. 

Thanks!

Chris_Hafner
Valued Contributor II

You should be able to scope out the profiles ahead of the framework removal. The end-user or local script does not need to do that. before I write up something more detailed, is there a reason why that's not currently part of the consideration? I'll admit, I left that part in my script... as a lazy catch-all. We do programmatically remove the profiles before the script is run by changing the devices department to our "offboarding" department during the process. Because the profiles are scoped out of that department they should go away on their own. This assumes that the connection to the JAMF pro service works. If the MDM connection is broken, then yea. I've seen some stuff that's been done in the startup utilities, not currently sure what works or not. You used to be able to disable SIP form there and manually remove the profiles. Not sure if that's possible in Monteray. Mind sharing more details?

jweiss
New Contributor II

Hey @Chris_Hafner thanks for the reply! Excuse my long winded reply but will do my best to explain the details. 

We have a subsection of our devices that were enrolled in Jamf via Pre-Stage enrollment and we had the box checked which prohibited the end user from removing the profiles. We now have a handful of these devices we need to remove from Jamf remotely without wiping the devices. On a device setup outside of a pre-stage enrollment with the option to remove the MDM profile from within the user a simple command of "sudo jamf remove Framework" gives us everything that we need meaning it removes the profiles as well as self-service, etc. However for devices setup via Pre-Stage the configuration profiles do not remove only self-service. 

I just did some testing this morning and when you manually disabled SIP via recovery and push out a command to delete the /var/db/configuration/store folder this is successful once SIP is off and following a reboot the Jamf profiles are gone. While this works it is not an easy process to take users through and of course is not able to be fully scripted. 

Are you aware of a way to remove the configuration profiles and framework on a device configured via a pre-stage enrollment with the profile section locked?  

Chris_Hafner
Valued Contributor II

One and only one thing. The "Remove MDM Profile" command can be issued from the unit "Management Comnmnands" tab in the JAMF Pro service. Have you tried that?

 

jweiss
New Contributor II

Yes :/ 

The logs of the command show as successfully completed but the profiles remain on the device. I am not sure if this is SIP stopping the command from actually executing on the device or something else not working as intended. 

Chris_Hafner
Valued Contributor II

That's interesting... OK, this might be a good time for a support ticket. Something seems off because this is a supported and expected workflow. I'll do some digging, but it's definitely support time. 

jweiss
New Contributor II

Thanks for the help Chris. With all of the information out there I still was not sure what was expected and supported versus not. I will submit a case and see if I can get support to share more information.