Temporary admin using self service.

New Contributor II

I know there has been many discussions around giving people temporary admin. I build two scripts, one will give the user admin access and create a plist. The plist will kick off 30 minutes after it is added by the first script. The plist will run a manual JSS trigger, that will run the second script to remove the admin rights. I have posted both scripts to my github account. The links are below.

Give admin rights:
Remove admin rights:


Legendary Contributor III

That's cool. Nice work on that. The only potential issue I see with your approach is, what if someone runs the Self Service policy, then disconnects from the network, either intentionally or by accident? If you have laptop users this is always a possibility. If you're kicking off the admin remove portion via a JSS policy manual trigger, it would fail and that user will remain an admin until they next connect back to the JSS. I assume the LaunchDaemon will try again repeatedly every 30 minutes until it succeeds and then remove itself.
It might be safer to deploy a local hidden script (your "30minAdminJssRemoved.sh" for example) that would get called into action by the LaunchDaemon, rather than rely on a policy that requires a server connection. The only drawback is since its not a policy, you may not be able to get a log back into the JSS to indicate it successfully removed the user's admin rights. I suppose you could have your script attempt a recon at the end to submit new inventory data back if it can.

My last suggestion would be to name your LaunchDaemon something less obvious than 'adminremove'. If you're giving users temporary admin rights, that will also include the ability to run a 'sudo launchctl list' command which would allow them to see the LaunchDaemon and possibly figure out how to disable it.

I realize I'm making a ton of assumptions here, such as that the users you support would even try to circumvent what you have in place or that they even have the technical know-how to understand LaunchDaemons, etc. so if there is little concern over this, then just ignore my ramblings :)
Just my 2ç. Otherwise, nicely done!

New Contributor

For one, I am surprised that there is not a lot of activity on this thread. I just discovered this from watching one of the JNUC 2013 videos! I hope to be re-attending this year after missing last year. Anyway, this is great especially in a K-12 environment, where we do not give admin rights to students. With this we can give out temp admin rights while at school. I have to say this is awesome! Good job getting this done, and anyone else involved with getting this to work!

Valued Contributor

This was an excellent jump-start for what we want to do; here's my volley in this particular game of tennis:

- Local removal mechanism
- A little "tightening-up" some things (like the (D/d)isabled key in the plist)
- Notification

I'm sure I broke as much as I fixed, so to speak, so use with caution and test test test.
Edit: Noticed that I still had the old version of the removal script; it's been updated to reflect the far more pared-down version that actually behaves properly when called from a daemon.


# TempAdmin.sh
# This script will give a user 30 minutes of Admin level access.
# It is designed to create its own offline self-destruct mechanism.

USERNAME=`who |grep console| awk '{print $1}'`

# create LaunchDaemon to remove admin rights
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"> 
</plist>" > /Library/LaunchDaemons/com.yourcompany.adminremove.plist

# create admin rights removal script
echo '#!/bin/bash
USERNAME=`cat /var/somelogfolder/userToRemove`
/usr/sbin/dseditgroup -o edit -d $USERNAME -t user admin
rm -f /var/somelogfolder/userToRemove
rm -f /Library/LaunchDaemons/com.yourcompany.adminremove.plist
rm -f /Library/Scripts/removeTempAdmin.sh
exit 0'  > /Library/Scripts/removeTempAdmin.sh

# set the permission on the files just made
chown root:wheel /Library/LaunchDaemons/com.yourcompany.adminremove.plist
chmod 644 /Library/LaunchDaemons/com.yourcompany.adminremove.plist
chown root:wheel /Library/Scripts/removeTempAdmin.sh
chmod 755 /Library/Scripts/removeTempAdmin.sh

# enable and load the LaunchDaemon
defaults write /Library/LaunchDaemons/com.yourcompany.adminremove.plist Disabled -bool false
launchctl load -w /Library/LaunchDaemons/com.yourcompany.adminremove.plist

# build log files in /var/somelogfolder
mkdir /var/somelogfolder
TIME=`date "+Date:%m-%d-%Y TIME:%H:%M:%S"`
echo $TIME " by " $USERNAME >> /var/somelogfolder/30minAdmin.txt

# note the user
echo $USERNAME >> /var/somelogfolder/userToRemove

# give current logged user admin rights
/usr/sbin/dseditgroup -o edit -a $USERNAME -t user admin

# notify
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -icon /Applications/Utilities/Keychain Access.app/Contents/Resources/Keychain_Unlocked.png -heading 'Temporary Admin Rights Granted' -description "
Please use responsibly. 
All administrative activity is logged. 
Access expires in 30 minutes." -button1 'OK' > /dev/null 2>&1 &

exit 0

Valued Contributor II

So we've been using this since I attended JNUC 2013. Our security staff recently decided to re-review this. So how are people controlling standard users from elevating themselves and then doing nefarious things? Turn on root? Create other accounts? Etc? Are people just tracking for non-standard accounts with JAMF smart groups? Same with Root enabled?

Honored Contributor II
Honored Contributor II

We like to track for specific security compliance breaches, FV turned off, AV disabled etc, and then switch them back on via a policy, normally emailing the user at the same time.

Other concerns could be around malicious software being installed so that would need to be monitored.

If users are the types to intentionally do bad things with their elevated permissions that is another issue.

Valued Contributor II

I'm asking more about HOW, not WHAT, is being tracked? To clarify, @davidacland it's all through JAMF inventory updating smart groups or EAs?

Honored Contributor II
Honored Contributor II

We are using EAs where necessary (for things like Sophos status) and standard smart group options (such as FV status) to track whats going on. We don't track if a user is an admin specifically. Not sure if that helps!

Valued Contributor

In broad strokes, if you care about a certain security control, you do two things:

1. Monitor
2. Act

The specific way in which you do those two things is going to vary considerably, based on the control. An example you stated was the root account being enabled. Turns out that you can Monitor for that by inspecting the output of the following command:

dscl . -read /Users/root | grep AuthenticationAuthority

You should expect no output if root is disabled, or some text starting "AuthenticationAuthority:" if it is enabled. You could pretty easily write an extension attribute to capture the output of this command and return a nice result. Now, to Act, you could create a Smart Group where the membership is a computer this EA has whatever value you set for currently/previously enabled root account, and then maybe email yourself or some security functionary on membership changes. You might also decide to disable the root user, disable the system entirely, pop up some warning text, etc. - all depending on your policies.

Valued Contributor II

@JPDyson I already have that running as an EA but we're not doing anything with it. I think my question around has this been a concern for people? Any experiences/pratfalls that I may have overlooked?

Valued Contributor

That's a good conversation to have; it really puts magnification on what settings and controls you're managing and ensuring in an ongoing fashion. I don't have a lot to add right now, since I've not really seen any serious issues with users that have admin rights. It's typically isolated incidents - one stubborn user.

Valued Contributor II

We have a group of developers who've asked for access to the functionality, and one of the questions that they asked was "what's to stop me from running the policy once every 30 minutes?" They are the group that I could see creating an admin user for local self support when we're not paying attention.


@easyedc There's a couple of options for you. You could use a Configuration Profile to restrict access to the "Users and Groups" System Preferences menu. The other option would be to make sure you audit the usage of that policy from time to time. If you see that they are abusing it then maybe it's time to have a chat with your CIO regarding the abuse if they aren't willing to play fair. Really the Config Profile route with the auditing would work well together in tandem.

Valued Contributor II

Ultimately with their elevated rights they can enable permanent root access via command line and dsenableroot and or dscl . create user. Creating monitoring is probably the way that we will go.

Legendary Contributor III
We have a group of developers who've asked for access to the functionality, and one of the questions that they asked was "what's to stop me from running the policy once every 30 minutes?"

You could either make it a once per day, once per week or even once per computer frequency to stop that. It will be a little more "work" for you if you need to go in and flush policy logs to re-enable it on a case by case basis, but might be worth it in the long run. I would definitely consider not making it use an Ongoing frequency, because they are right, what impetus would they have in not just re-running it as frequently as needed? You might as well just make them permanent admins at that point.

As a talking point, all of our users, from CEOs to sales people and everyone in between, have local admin rights - PC or Mac. Like @JPDyson stated, we get some users who try to buck things and will do everything they can to circumvent controls, but its not nearly as many as you might think. Just a small % of the user base that are "bad apples" The rest follow the rules and try to be good company stewards.
The part for us in IT that makes it all very tricky and frustrating is that there is basically zero consequences for actively circumventing things. Maybe a finger wag and a tsk tsk, but not much more. I personally have always felt there needs to be more action taken by management if a user is found trying to do something to actively compromise security and controls, including up to termination in the severe cases. But alas, not much is ever done.
So I guess what I'm saying is company AUP/EULA and HR policy is every bit as important as any technical controls; maybe more important in the end.

New Contributor III

@JPDyson @brockma9 I'm new to the whole JSS thing so I come on to these dicussions to learn and test policies that will be effective at my job. I came across this discussion which is FANTASTIC.

My questions here is, how do I implement this? is there a step my step guide somewhere?

Any help would be greatly appreciated, my users are currently all Admins and we one to make them Standard user so something like this will help a great deal.

New Contributor

Late to this party but.... I've just attempted to implement this today and realized that the first run works flawlessly but the second time the user is granted the TempAdmin rights it does not remove after the time expires.

Has anyone else seen the same behavior?

PS @NGKF if you have not already implemented this, drop a comment here and I'll provide my step by step.

New Contributor II

I think this is fantastic!
However....I have a machine that was setup as a stand alone machine then later added to the jss using self enrollment. When I ran the script from self service it said I was good to go for 30 mins but the user really doesn't have admin privileges. It was still asking for my admin creds. Do you guys have anything that might help me here?
Thanks guys!

Contributor III

Hi @JPDyson ,

I've just tried using your script, and it worked great. I do have one question...

Suppose a user runs the script (via self service), then within the 30 minutes, they rebooted their Macbook, would it mean the 30 minutes countdown would never happen, and therefore the admin rights not removed?

New Contributor II

I am confused about JPDyson 's script. Where it says yourcompany. Am I supposed to be changing that value to something else or just plugging it in as is?

Valued Contributor II

@THQIT Generally yes. Most people will chose to customize that to who they work for. Apple, for example, starts everything com.apple.xxxx

New Contributor II

OK but it has no affect on the script per say its just to put your company info in there? So in theory I could put anything at all in there and it would be the same.

New Contributor III

I am implementing this and can get it to work half way. It will grant the user Admin access, but does not remove it. If I run the policy the first time, it adds admin access, creates the log file and remove script as it should. I see the removeTempAdmin.sh file in Scripts. Then after the time delay (1 minute for testing) that file is removed. However Admin access remains. I have to log in as my other local admin, remove admin access from the test user account, then I can try again.

If I run the policy a second time it writes to the log file and creates the removeTempAdmin.sh script, but this time does not remove it.

This is on 10.11 if that matters.

Edit: Apparently 10.11 does matter. I used the same machine to test our 10.12 update process and now it works. However we do have users on 10.11 so curious to know why it didn't work on 10.11.

New Contributor III

Another odd finding. Even on 10.12 it is hit and miss. I notice in the logs the user running the script is inconsistent. Sometimes it is the local user for the account. Other times it is the AD username.

When enrolling we have it linked to LDAP so in Self Service they are logged in as their AD account first.last. However we have not linked AD to the local user accounts. We just create those the default way using First Last, which makes the username firstlast.

For Example:
User Bob Smith has the local user account "Bob Smith". His AD account name is bob.smith. He enrolled in Jamf using bob.smith on his user account bobsmith. When this policy/script runs, sometimes it runs under bob.smith and other times under bobsmith.

This happens even if done one right after another, no login/logout of the computer or Self Service.

I'm not sure that is why it fails. Even if it runs as bob.smith the user logged is the actual local user bobsmith.

Attached is a screen shot that does show what happens when I run it multiple times. The first time it works: adds and removes admin access. The second time it adds the Admin access, but does not remove it. I see the deamon is already loaded, so perhaps thats what is happening? The deamon is loaded, so something doesn't work quire right and the removeTempAdmin.sh does not run?


New Contributor II

@ddasilva Hi there, I'm attempting this and not having much luck. Would be great to see your step by step approach.

It's falling over on the:

give current logged user admin rights

/usr/sbin/dseditgroup -o edit -a $U -t user administrator
exit 0

I'm getting the Group not found error. Tried on 10.11 and 10.12. I'm guessing this is due to SIP?

New Contributor II

Bump for this. Looking to roll this out in our environment.

New Contributor II

I tried using this script but it never removed the admin rights. Any update on this?

Contributor II

What happens if the user adds themselves to the local sudoers when they have elevated themselves?

New Contributor III

Changes I made/had to make.

  • writing out a .plist via shell redirection didn't seem to work on Mojave. Used PlistBuddy to write the .plist
  • switch to supported load/unload/enable/disable of LaunchDaemons
  • lowered time limit to 5 minutes
  • changed jamf binary path
  • substitute echo for printf
  • To Do: use sysadminctl instead of dseditgroup EDIT: I thought you could use sysadminctl as a drop-in replacement of dseditgroup. Guess you can't...

Note: chanded stdout and stderr variables to sout and serr, respectively. My bad.

U="$(who |grep console| awk '{ print $1 }')"
TIME=$(date "+Date:%m-%d-%Y TIME:%H:%M:%S")

mkadminremoved() {
    local label="$PLIST"
    local string0="/usr/local/bin/jamf"
    local string1="policy"
    local string2="-event"
    local string3="adminremove"
    local sout="$LOGDIR/${PLIST%.plist}.out"
    local serr="$LOGDIR/${PLIST%.plist}.err"

    plistbuddy() { /usr/libexec/PlistBuddy "$@"; }    

    plistbuddy -c "add :Label string $label" 
                -c "add :ProgramArguments array" 
                -c "add :ProgramArguments:0 string $string0" 
                -c "add :ProgramArguments:1 string $string1" 
                -c "add :ProgramArguments:2 string $string2" 
                -c "add :ProgramArguments:3 string $string3" 
                -c "add :StandardOutPath string $sout" 
                -c "add :StandardErrorPath string $serr" 
                -c "add :StartInterval integer 300" 

# Message to user they have admin rights for 5 min. 
/usr/bin/osascript <<-EOF
    tell application "System Events"
        display dialog "You now have admin rights to this machine for 5 minutes" buttons {"Let Me at it."} default button 1
    end tell

# Create launchD service to call JSS policy to remove admin rights.

#set the permission on the file just made.
chown root:wheel "$PPATH"/"$PLIST"
chmod 644 "$PPATH"/"$PLIST"

# load the removal plist timer.
launchctl enable system/"$PLIST" "$PPATH"/"$PLIST"
launchctl bootstrap system "$PPATH"/"$PLIST"

# build log files in var/uits.
[[ ! -d "$LOGDIR" ]] && mkdir "$LOGDIR"
printf "%s
" "$TIME by $U" >> "$LOGDIR"/5minAdmin.txt
printf "%s
" "$U" >> "$LOGDIR"/userToRemove

# give current logged user admin rights
/usr/sbin/dseditgroup -o edit -a "$U" -t user admin


I had to deal with temporary admin rights for a long time. It was everytime a nightmare.

For me, this ended with a new app from SAP: https://github.com/SAP/macOS-enterprise-privileges

New Contributor III

And the other side...

The ugly loop was used during testing. I decided to keep it for now.


if [[ -f /var/uits/userToRemove ]]; then

    U="$(cat /var/uits/userToRemove)"

   for USER in $U; do
        printf "%s
" "removing $USER from admin group"
        /usr/sbin/dseditgroup -o edit -d "$USER" -t user admin
        printf "%s
" "$USER has been removed from admin group"

    rm -f /var/uits/userToRemove


    printf "%s
" "going to unload"
    launchctl disable system/"$PLIST"
    launchctl bootout system/"$PLIST"
    printf "%s
" "Completed"
    rm -f /Library/LaunchDaemons/"$PLIST"


New Contributor III

@gda Nice one. Thanks.


@JPDyson super new to scripting and Jamf but is there any way you can change the time to 12 hours? By the way your script works awesome! Thank You All.

New Contributor III


You need to change:


Which is 1800 seconds (30 minutes). To:


Which is 43200 seconds (12 hours).

StartInterval tells this LaunchDaemon to start every N units of time. It, in-turn, executes what it is told to execute.


instead of the timer counting down, can this script do an LDAP Group lookup and give the users in that specific LDAP group admin right and remove it when the user is remove from the LDAP group?

Also is there really a way to catcher the logs from the time Admin right is activated and it end?

Sorry and Thank You for all the help.

NVM it won't work the way we had it setup here. Thanks though.