Posted on 09-01-2017 06:59 AM
As a school district we have some schools that allow the students to get Apps from the Mac App Store. Because we are a school district, we obviously cannot allow admin access to those machine. Therein lies the rub.
I would love to see if any of you have a graceful solution for non-admins to remove apps from the Applications folder without being an admin.
This is what I have worked out so far:
I KNOW I can use a script to move all apps from the applications folder to the user's applications folder.
From there they should be able to delete them. (To my knowledge, please correct me if I'm wrong.)
I KNOW that I should be able to exclude items from that action, Utilities folder, built-in Mac Apps, etc.
I need some help getting a script together so that we can do this. If anyone has a more graceful method, I am all ears.
I will include what I have so far but am limited by the string length and can only include a few apps.
#!/bin/sh
# current user is $3
shopt -s extglob
cd /Users/$3/desktop/test
mv !('dont move'|Computers.csv|'Asset Tags.csv') /Users/$3/Applications
Solved! Go to Solution.
Posted on 09-02-2017 08:15 AM
@agetz Give the below a try. I haven't been able to do much testing with it, but it may work better than before. It's a bit weird but I've used this technique before, which essentially pipes a script out to tmp and then runs the script itself as the logged in user. The result of the script is captured and passed to the remainder of the script and determines what application to delete. In function, this works the same as before when testing the script locally, but runs as root, then executes the "selection" script as the user using launchctl asuser
which I find tends to be more reliable.
Once I have a moment, I'll test this under Sierra 10.12.6 to see if there are still any issues to be addressed. But give it a try.
#!/bin/bash
## Edited to correct an error with variables ##
## We capture the logged in user and the UID to use later when running the local script as the user
loggedInUser=$(stat -f%Su /dev/console)
loggedInUID=$(id -u "$loggedInUser")
## Create a script in /tmp to run as the logged in user
cat << EOS > /private/tmp/del_appstore_user.sh
#!/bin/bash
## Get a list of all App Store apps installed into the main Applications folder
AppStoreAppsRaw=$(mdfind -onlyin /Applications kMDItemAppStoreHasReceipt == 1 | awk -F'/' '{print $NF}' | sort -f)
## Run an Applescript to convert the list into a list for selection and present the list to the user
AppToDelete=$(/usr/bin/osascript << EOD
set theAppsList to paragraphs of (do shell script "echo "$AppStoreAppsRaw"")
tell app "System Events"
activate
choose from list theAppsList with prompt "Choose an application to delete"
end tell
EOD)
## Echo back the selection result
echo "$AppToDelete"
EOS
## Script creation done
## Now make the new script executable
chmod +x /private/tmp/del_appstore_user.sh
## Run the script as the user, capturing the output into a new variable
AppToDeleteResponse=$(/bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" "/private/tmp/del_appstore_user.sh")
## Now check to see if the response from the selection was something other than 'false',
## which would indicate the user canceled from a selection
if [ "$AppToDeleteResponse" != "false" ]; then
ConfirmDelete=$(/usr/bin/osascript << EOF
set theAppName to do shell script "echo "$AppToDeleteResponse""
tell app "System Events"
activate
set response to button returned of (display dialog "Are you sure you want to delete the application "" & theAppName & "?" buttons {"Yes","No"} default button 2)
end tell
EOF)
else
echo "User may have canceled. Exiting."
## Clean up the script in /tmp
rm -f /private/tmp/del_appstore_user.sh
exit 0
fi
## If the button returned from the confirmation was Yes, then delete the application using rm
if [ "$ConfirmDelete" == "Yes" ]; then
echo "User confirmed. Deleting $AppToDeleteResponse"
rm -rf "/Applications/$AppToDeleteResponse"
## Finally, clean up the script in /tmp
rm -f /private/tmp/del_appstore_user.sh
exit 0
else
## If not, cancel since the user chose No
echo "User may have canceled. Exiting."
## Clean up the script in /tmp
rm -f /private/tmp/del_appstore_user.sh
exit 0
fi
Posted on 09-01-2017 07:51 AM
I'm not entirely sure what you're looking to do, but it sounds like you want Mac App Store apps to get moved into ~/Applications/
instead of the default /Applications/
I'm not sure that's entirely supported, but I do know that the App Store.app will still recognize the applications as being from the App Store even if they are moved into another path. I've seen that myself with something I moved that was installed from there. The App Store application was still showing an available update for the program even though it wasn't in the main Applications folder.
If this is what you're after, I would recommend using mdfind
to locate all App Store apps in a nice neat list in your script, then simply loop over that list, copying them or moving them into the new location.
The following will grab a list of MAS applications living in /Applications/
mdfind -onlyin /Applications/ -name kMDItemAppStoreHasReceipt == 1
Try using that in your script to determine what needs to be copied. It will naturally exclude things like the built in apps that come with the OS, since those aren't App Store apps.
Note that If you don't get the full results you expect, do an mdls
on a known Mac App Store app to see some of the other App Store distinct items in its record. For example, you could also use kMDItemAppStoreIsAppleSigned == 1
Hope that helps.
Posted on 09-01-2017 07:55 AM
Another thought I just had, which may make this much easier, though initially more work to develop. Think about putting together a script that can be run from a Self Service policy that could list out the apps from the App Store in Applications, just like I show above, put that into a list in an Applescript 'choose from list' style dialog, and let them select the app they want to delete, then do the rm with root privileges.
That way, they can still remove them by running something via Self Service and the apps can remain where they get installed by default. No mess needed with moving apps around. This would also allow anyone who logs into the Mac to delete these rather than just a single account on the Mac.
Posted on 09-01-2017 08:19 AM
@mm2270 Yes, that would be the best solution if we could get there. The issue is really that eventually many students fill up their drives with app store apps and then our techs have to go and remove them so they can continue working. I was working on just moving the apps to a directory they had rights to but actually giving them a type of uninstaller for only App Store Apps would be a beautiful thing.
I am not a master scripter but I could eventually get something together with a bit of testing. Can you point me in the right direction? Even with getting your first recommendation together into a script.
The mdfind option will work nicely, just have to figure out how to setup the mv option worked out in the script to mv the applications it finds.
Thanks.
Posted on 09-01-2017 08:45 AM
Here's a working script that could be run to display an AS dialog with only App Store installed apps that they can select and it will delete the application.
The one potential problem with this is that generally, to get interactive dialogs to show up properly, they usually need to be run as the user, not as the root account. Not the deletion part, but the part where it displays the dialogs. I haven't thrown this into Self Service myself to see if it just works 'as is' You can try, and it may be fine without further modification. I don't know for sure though.
Also, I'm not an expert in Applescript. I have a feeling this could be simplified a bit, but I don't know enough to do that right now. In any case, I tested this locally (not via Self Service) and it works. I downloaded a free App Store app I didn't really want, and used this to delete it from Applications.
#!/bin/bash
AppStoreAppsRaw=$(mdfind -onlyin /Applications kMDItemAppStoreHasReceipt == 1 | awk -F'/' '{print $NF}' | sort -f)
AppToDelete=$(/usr/bin/osascript << EOD
set theAppsList to paragraphs of (do shell script "echo "$AppStoreAppsRaw"")
tell app "System Events"
activate
choose from list theAppsList with prompt "Choose an application to delete"
end tell
EOD)
if [ "$AppToDelete" != "false" ]; then
ConfirmDelete=$(/usr/bin/osascript << EOF
set theAppName to do shell script "echo "$AppToDelete""
tell app "System Events"
activate
set response to button returned of (display dialog "Are you sure you want to delete the application "" & theAppName & "?" buttons {"Yes","No"} default button 2)
end tell
EOF)
else
echo "User may have canceled. Exiting."
exit 0
fi
if [ "$ConfirmDelete" == "Yes" ]; then
echo "User confirmed. Deleting $AppToDelete"
rm -rf "/Applications/$AppToDelete"
exit 0
else
echo "User may have canceled. Exiting."
exit 0
fi
Posted on 09-01-2017 08:48 AM
@mm2270 Thanks for the help, I will do some testing on my end and see how it works. I will post back with the results.
Posted on 09-01-2017 09:02 AM
@mm2270 I am seeing errors when running as root in self-service.
Script exit code: 0
Script result: 227:235: execution error: An error of type -10810 has occurred. (-10810)
71:79: execution error: System Events got an error: Application isn’t running. (-600)
User may have canceled. Exiting.
Running locally works until it gets to the rm command, you must be an admin to do it.
Posted on 09-01-2017 09:09 AM
OK, that is what I was concerned about. The "error of type -10810" basically means, you can't display this interactive dialog to the logged in user when it's run as root.
In this case, let me see what we can do to get it to work. As I mentioned, we need to run the Applescript call as the logged in user. There are a few ways to do this, but some work better/more reliably than others.
Posted on 09-01-2017 09:26 AM
Ok, fairly simple modification. This works, at least for me. I tested running it from Self Service to delete an App Store app. Displays the selection dialog and takes that selection and deletes it.
See if it works for you now.
#!/bin/bash
loggedInUser=$(stat -f%Su /dev/console)
AppStoreAppsRaw=$(mdfind -onlyin /Applications kMDItemAppStoreHasReceipt == 1 | awk -F'/' '{print $NF}' | sort -f)
AppToDelete=$(su "$loggedInUser" -c /usr/bin/osascript << EOD
set theAppsList to paragraphs of (do shell script "echo "$AppStoreAppsRaw"")
tell app "System Events"
activate
choose from list theAppsList with prompt "Choose an application to delete"
end tell
EOD)
if [ "$AppToDelete" != "false" ]; then
ConfirmDelete=$(su "$loggedInUser" -c /usr/bin/osascript << EOF
set theAppName to do shell script "echo "$AppToDelete""
tell app "System Events"
activate
set response to button returned of (display dialog "Are you sure you want to delete the application "" & theAppName & "?" buttons {"Yes","No"} default button 2)
end tell
EOF)
else
echo "User may have canceled. Exiting."
exit 0
fi
if [ "$ConfirmDelete" == "Yes" ]; then
echo "User confirmed. Deleting $AppToDelete"
rm -rf "/Applications/$AppToDelete"
exit 0
else
echo "User may have canceled. Exiting."
exit 0
fi
Posted on 09-01-2017 09:41 AM
@mm2270 Well, a little different this time.
Script result: 227:235: execution error: System Events got an error: Application isn’t running. (-600)
71:79: execution error: System Events got an error: Application isn’t running. (-600)
User may have canceled. Exiting.
Running in self service is hanging but running from casper remote produced this error.
Posted on 09-01-2017 10:07 AM
Hmm. Odd. I'm not sure what that error means. Haven't seen that before. Let me do a little more testing if I can. Can I ask what OS you're running it on? Sierra or something else? That might be part of it actually.
Posted on 09-01-2017 10:17 AM
@mm2270 Sierra. 10.12.6
Running it locally it wont display the application choices. Just
su: Sorry
su: Sorry
User may have canceled. Exiting.
Casper remote:
Script exit code: 0
Script result: 227:235: execution error: An error of type -10810 has occurred. (-10810)
71:79: execution error: An error of type -10810 has occurred. (-10810)
User may have canceled. Exiting.
Self service doesnt even know whats happening and doesnt produce a log, just gathers information and ends. no logs.
Posted on 09-02-2017 08:15 AM
@agetz Give the below a try. I haven't been able to do much testing with it, but it may work better than before. It's a bit weird but I've used this technique before, which essentially pipes a script out to tmp and then runs the script itself as the logged in user. The result of the script is captured and passed to the remainder of the script and determines what application to delete. In function, this works the same as before when testing the script locally, but runs as root, then executes the "selection" script as the user using launchctl asuser
which I find tends to be more reliable.
Once I have a moment, I'll test this under Sierra 10.12.6 to see if there are still any issues to be addressed. But give it a try.
#!/bin/bash
## Edited to correct an error with variables ##
## We capture the logged in user and the UID to use later when running the local script as the user
loggedInUser=$(stat -f%Su /dev/console)
loggedInUID=$(id -u "$loggedInUser")
## Create a script in /tmp to run as the logged in user
cat << EOS > /private/tmp/del_appstore_user.sh
#!/bin/bash
## Get a list of all App Store apps installed into the main Applications folder
AppStoreAppsRaw=$(mdfind -onlyin /Applications kMDItemAppStoreHasReceipt == 1 | awk -F'/' '{print $NF}' | sort -f)
## Run an Applescript to convert the list into a list for selection and present the list to the user
AppToDelete=$(/usr/bin/osascript << EOD
set theAppsList to paragraphs of (do shell script "echo "$AppStoreAppsRaw"")
tell app "System Events"
activate
choose from list theAppsList with prompt "Choose an application to delete"
end tell
EOD)
## Echo back the selection result
echo "$AppToDelete"
EOS
## Script creation done
## Now make the new script executable
chmod +x /private/tmp/del_appstore_user.sh
## Run the script as the user, capturing the output into a new variable
AppToDeleteResponse=$(/bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" "/private/tmp/del_appstore_user.sh")
## Now check to see if the response from the selection was something other than 'false',
## which would indicate the user canceled from a selection
if [ "$AppToDeleteResponse" != "false" ]; then
ConfirmDelete=$(/usr/bin/osascript << EOF
set theAppName to do shell script "echo "$AppToDeleteResponse""
tell app "System Events"
activate
set response to button returned of (display dialog "Are you sure you want to delete the application "" & theAppName & "?" buttons {"Yes","No"} default button 2)
end tell
EOF)
else
echo "User may have canceled. Exiting."
## Clean up the script in /tmp
rm -f /private/tmp/del_appstore_user.sh
exit 0
fi
## If the button returned from the confirmation was Yes, then delete the application using rm
if [ "$ConfirmDelete" == "Yes" ]; then
echo "User confirmed. Deleting $AppToDeleteResponse"
rm -rf "/Applications/$AppToDeleteResponse"
## Finally, clean up the script in /tmp
rm -f /private/tmp/del_appstore_user.sh
exit 0
else
## If not, cancel since the user chose No
echo "User may have canceled. Exiting."
## Clean up the script in /tmp
rm -f /private/tmp/del_appstore_user.sh
exit 0
fi
Posted on 09-05-2017 06:33 AM
Well, it tried to delete everything in the application folder...
The choices did come up and so did the verification prompt, but doesn't look like it passed the right info back for the removal...
The log file is rather large and the policy in self service did complete so theres that. Let me know if you need that.
Posted on 09-05-2017 07:38 AM
@agetz Ouch! Sorry about that! I see now where I messed up when I was editing the script. I sincerely hope that it didn't mess up your computer or whatever you were testing it on. :(
So my mistake was I left the original variable labeled AppToDelete
in the line where it deletes the application. It needed to change to AppToDeleteResponse
because that gets populated with the user response / selection when it runs the local script. So because AppToDelete
was blank, when it ran this line:
rm -rf "/Applications/$AppToDelete"
It was really rm'ing the entire Applications folder! Again, my bad and sorry for not proofing that more carefully.
Please try the edited script above (I changed it so if anyone comes across it they won't try to use the faulty one) Please try on a test Mac, as I'm sure you're already doing. See if that actually works now.
Posted on 09-05-2017 08:07 AM
@mm2270 No worries, thankfully it was localized to the Application folder and the vast majority were locked by Apple apparently and couldn't be removed. It was time to upgrade a few apps anyway.
Yes, I noticed the same thing in the script. Commented out the RM and echoed out the other variable to test. Thanks for the reply back to confirm.
Will let you know how this goes, thanks.
Posted on 09-05-2017 08:42 AM
@mm2270 It seems to be working. I'm going to have some students test it out and see how it goes.
This process is exactly what I was looking for. Thanks for your time and efforts.
Seems that you have helped me out at least a couple times with script related questions and I owe you. Thanks again.
Posted on 09-05-2017 08:59 AM
I agree.
mm2270 Is very helpful.
Posted on 09-05-2017 12:09 PM
@agetz Hey, glad to hear it worked for you! And happy to assist. Hopefully the work done on this will provide a useful example to anyone else that comes across this thread too.
Posted on 09-06-2017 11:01 AM
I've gone a step further and used Platypus to create a simple app that will reference the policy in Self Service. End user can then run the app instead of going to self service. My student test group seems to like it this way better. Guess its one less click, I dunno.
#!/bin/bash
#opens self service in hidden mode, then opens the policy URL.
open -j -a /Applications/Self Service.app "selfservice://entity=policy&url=&product=651&recon=false&enableUserLevelMdm=false&installed=false”
Posted on 09-06-2017 11:13 AM
@agetz Nice! I was just about to post, why not embed the whole script within a self contained platypus app, but then of course you'd run into the admin privileges issue. So that's a creative way of getting around that.
Posted on 05-17-2019 03:44 AM
@mm2270, what would be the modification in the script to not limit this script to App Store applications, but to all (maybe excluding macOS default apps)?
I guess it is in here:
AppStoreAppsRaw=$(mdfind -onlyin /Applications kMDItemAppStoreHasReceipt == 1 | awk -F'/' '{print $NF}' | sort -f)
But I do not have a clue...
Thanks for your help!
Best regards
Chris
Posted on 05-20-2019 09:26 PM
@cbednarzwd haha I literally was doing this in the #bash channel in the Mac Admin Slack last week. Spotlight is an amazing tool. So, almost everything in macOS will have meta data tags to it, and Spotlight can search for those tags. So, we do know that every App has what is called a Bundle ID
and it is unique. We also know that all Apple Apps start with com.apple
in the Bundle ID. The meta data tag for this is kMDItemCFBundleIdentifier
. We can use this to wild card inclusion or exclusion of data we are searching for.
mdfind -onlyin /Applications "kMDItemCFBundleIdentifier == com.apple.*"
We can wildcard com.apple.*
to return a list of all Apps by Apple. I would suggest looking at mdfind
, and mdls
to shell script using Spotlight. There are a lot of powerful things you can do with them.
Posted on 05-21-2019 12:24 AM
Well, I don't get this search query to work, and I cannot figure out the problem. According to all the references I found in WWW; this should basically work:
mdfind -onlyin /Applications "kMDItemCFBundleIdentifier != com.apple.*" && "kMDItemKind = Application" | awk -F'/' '{print $NF}' | sort -f
So, this string should
- just search within Applications folder
- just give out raw Applications (not the other stuff that might reside there)
- limit this just to Applications just to third party apps, NOT from Apple
- format the result accordingly to the script above from mm2270
But it gives this error(s):
-bash: kMDItemKind = Application: command not found
awk: syntax error at source line 1
context is
{print >>> <<< $NF}
I played around with the syntax so much that I am completely confused now. Totally lost in this... :-(
Best regards,
Christian
Posted on 05-21-2019 10:44 AM
@cbednarzwd you have a weird escape in your awk
command, but this is how you fix it, and you are quoting the Spotlight meta data arguments twice, so the binary sees that as two arguments (it is a bashism) so try this:
mdfind -onlyin /Applications "kMDItemCFBundleIdentifier != com.apple.* && kMDItemKind = Application" | awk -F'/' '{print $NF}' | sort -f