Is there a way to deploy a LaunchAgent to several machines through JSS or Casper Remote?

haalx790
New Contributor

Hello,

I am having difficulty figuring out if there is a way to push out a LaunchAgent to multiple machines at the same time. I have a .plist file that I would like to have put into '/Library/LaunchAgents' for all of our machines in our environment.

Does the JSS or Casper Remote provide a way to place a .plist file into '/Library/LaunchAgents' for multiple machines? It would be great if I can avoid placing the .plist file into this location manually (one-by-one).

Thank you,
-Alex H.

1 ACCEPTED SOLUTION

Aziz
Valued Contributor

Hey @haalx790

Package the Launch Agent using Composer and deploy it via the JSS, Casper Remote or ARD.

Make sure to include this as post flight script to set some permissions and load the agent. Right click on "Scripts", click on "Shell Script" and "post flight".

#!/bin/bash
chown root "/Library/LaunchAgents/NAMEOF.plist"
chmod 644 "/Library/LaunchAgents/NAMEOF.plist"
launchctl load -w "/Library/LaunchAgents/NAMEOF.plist"

Example of where to put the preflight script:

optional image ALT text

optional image ALT text

Good luck!

View solution in original post

20 REPLIES 20

Aziz
Valued Contributor

Hey @haalx790

Package the Launch Agent using Composer and deploy it via the JSS, Casper Remote or ARD.

Make sure to include this as post flight script to set some permissions and load the agent. Right click on "Scripts", click on "Shell Script" and "post flight".

#!/bin/bash
chown root "/Library/LaunchAgents/NAMEOF.plist"
chmod 644 "/Library/LaunchAgents/NAMEOF.plist"
launchctl load -w "/Library/LaunchAgents/NAMEOF.plist"

Example of where to put the preflight script:

optional image ALT text

optional image ALT text

Good luck!

mm2270
Legendary Contributor III

Yes, just package it up in Composer the same way you would do with any item you're looking to deploy. I recommend a package installer (.pkg), but you could in theory use a .dmg format as well.

The only other consideration would be if you want to load the LaunchAgent right away after its installed, or wait for it to load naturally at the next login. Getting LaunchDaemons to load via postinstall scripts in a pkg is easy. LaunchAgents take a little more work since they have to load as the user, not as root.

alexjdale
Valued Contributor III

I find it's easier to simply create a script on your JSS that echoes that plist out to the /Library/LaunchAgents folder, then that same script can load it if you'd like. No need to mess with packaging it.

donmontalvo
Esteemed Contributor III

+1

--
https://donmontalvo.com

mpermann
Valued Contributor II

@alexjdale would you mind sharing an example of the solution you recommended above? It's not a method I am familiar with. I'm familiar with the way @mm2270 suggested. It's nice to have options though.

bpavlov
Honored Contributor

@mpermann I believe he's talking about this: https://en.wikipedia.org/wiki/Here_document

Two examples from @rtrouton 's blog:
https://derflounder.wordpress.com/2011/09/23/setting-multiple-network-time-servers-from-the-command-...

#!/bin/sh

/bin/cat > /etc/ntp.conf << 'NEW_NTP_CONF'
server ns0.time.server
server ns1.time.server
server time.apple.com
NEW_NTP_CONF

And from the postinstall example here:
https://derflounder.wordpress.com/2015/02/26/deploying-sophos-enterprise-anti-virus-for-mac-9-2-x/

#!/bin/sh

/bin/cat > "/Library/Preferences/com.sophos.sau.plist" << 'SOPHOS_CONFIG'
<?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>LastUpdated</key>
    <string>February 25, 2015 at 9:17:42 AM EST</string>
    <key>OverrideCredentialsForSaas</key>
    <integer>0</integer>
    <key>PrimaryServerProxy</key>
    <integer>0</integer>
    <key>PrimaryServerProxyPort</key>
    <integer>0</integer>
    <key>PrimaryServerProxyURL</key>
    <string></string>
    <key>PrimaryServerType</key>
    <integer>2</integer>
    <key>PrimaryServerURL</key>
    <string>smb://sophos_enterprise_server_name_goes_here/SophosUpdate/CIDs/S000/ESCOSX</string>
    <key>SecondaryServer</key>
    <true/>
    <key>SecondaryServerProxy</key>
    <integer>0</integer>
    <key>SecondaryServerProxyPort</key>
    <integer>0</integer>
    <key>SecondaryServerProxyURL</key>
    <string></string>
    <key>SecondaryServerType</key>
    <integer>0</integer>
    <key>SecondaryServerURL</key>
    <string></string>
    <key>UpdateFrequency</key>
    <integer>1</integer>
    <key>UpdateInterval</key>
    <integer>10</integer>
    <key>UpdateLogIntoFile</key>
    <true/>
    <key>UpdateLogIntoSyslog</key>
    <false/>
    <key>UpdateOnConnection</key>
    <false/>
    <key>UpdateUnits</key>
    <integer>1</integer>
</dict>
</plist>
SOPHOS_CONFIG

mm2270
Legendary Contributor III

Echoing out a launchd from a script is actually a good method. I use that often in my scripts since it allows you to actually create dynamic LaunchDaemons and LaunchAgents that can have custom information based on the Mac the script is running it on if needed.
However, I wouldn't recommend that method to start for someone who is new to using launchd jobs, just because there are several gotchas to watch out for that, if, unaware of them can cause a massive amount of frustration. And sometimes the syntax needing to be used in the plist files isn't exactly intuitive. For someone new to all this, I'd recommend first starting out making them in an application like LaunchControl or Lingon, to gain an understanding of how they are created. Once comfortable with them, then yes, its good to explore making them on your own via scripts.
Also, simply echoing out the contents to a file isn't enough. You need to make sure the owner:group and permissions on the file are correct after its created or the OS won't load it.

chown root:wheel /Library/LaunchAgents/com.org.someexample.plist
chmod 644 /Library/LaunchAgents/com.org.someexample.plist

mpermann
Valued Contributor II

Thanks @bpavlov and @mm2270 for the examples and clarification. It is helpful.

alexjdale
Valued Contributor III

I actually just use echo. When running from the JSS, I haven't had to change ownership or permissions in my experience, it just works.

https://jamfnation.jamfsoftware.com/discussion.html?id=14940

nessts
Valued Contributor II

which probably makes sense since the JSS is doing things as root so it gets set to root:wheel by default, you have just been lucky that the default umask did not make the file group writable. That would cause problems.

May
Contributor III

I use this for loading LaunchAgents as the user,
with much appreciated help from @mm2270 + @yr_joelbruner

#!/bin/sh

loggedInUser=$( stat -f%Su /dev/console )
loggedInPID=$( ps -axj | awk "/^$loggedInUser/ && /Dock.app/ {print $2;exit}" )

(/bin/launchctl bsexec "${loggedInPID}" sudo -iu "${loggedInUser}" "launchctl load /Library/LaunchAgents/your.launchagent.job.plist")

nessts
Valued Contributor II

It is somewhat unfortunate that you just figured this out. seems to not work very well in public beta versions of some other OS that will be coming soon...

haalx790
New Contributor

Thank you all for your help! I was able to add my .plist file to the 'Sources' area of Composer, add the post flight shell script and then build it as a package. I then deployed the package out through Casper Remote and it worked! The third-party app that I wanted to launch on user login is launching like I wanted (it seems to be launching for all users who log in also which is awesome). Thank you again everyone!

jmb03012
New Contributor III

@May I have a similar situation and was able to use asuser to run my 10.10 and 10.11 LaunchAgents but I'm trying to use bsexec to handle by 10.9 and older and I'm running into an error.

I have the syntax correct as far as I can tell but when I run the command I get:
sudo: unable to execute /bin/bash: Bad address
launchctl bsexec failed: No such file or directory

Have you encountered this before?

UPDATE:
So I do have the syntax correct, I've noticed that if I run the command several times it eventually works, usually on the second or third run so I think sytax is good, but I cant explain why it doesnt consistently work (when it does fail it gets the same error as above).

rcastorani
New Contributor II

@jmb03012 From some other advice, I stopped using bsexec and now use sudo -u, like this:

# Get logged in user account, use lpadmin to add copier as user
loggedInUser=$( stat -f%Su /dev/console )
sudo -u $loggedInUser /usr/sbin/lpadmin -p PrinterName -L PrinterLocation -E -o printer-is-shared=false -v smb://server/PrinterName -P /Library/Printers/PPDs/Contents/Resources/PrinterDriver.gz -o auth-info-required=negotiate
sudo -u $loggedInUser lpoptions -d PrinterName

jmb03012
New Contributor III

@rcastorani For 10.10 and 10.11 using sudo -u will usually cause issues, but I had thought also that you could still use it 10.9 and older but when I try I am running into a different error:

sudo -u geid_mac_dev launchctl unload /Library/LaunchAgents/com.bloomberg.BNotifier.plist 
Could not open job overrides database at: /private/var/db/launchd.db/com.apple.launchd.peruser.0/overrides.plist: 13: Permission denied
launchctl: CFURLCreateDataAndPropertiesFromResource(/Library/LaunchAgents/com.bloomberg.BNotifier.plist) failed: -10
launchctl: no plist was returned for: /Library/LaunchAgents/com.bloomberg.BNotifier.plist
launchctl: no plist was returned for: /Library/LaunchAgents/com.bloomberg.BNotifier.plist
nothing found to unload

stedders
New Contributor

A little late to the game, I had the same requirement as @jmb03012, @rcastorani and @May - load the LaunchAgent as the user. The bsexec command with PID fails on SIP enabled devices, but launchctl 2.0 offers the following syntax to load the LaunchAgent as the user.

#!/bin/sh
#Get Username and UID
user=`ls -la /dev/console | cut -d " " -f 4`
uid=`id -u $user`

echo "User: $user"
echo "UID: $uid"

PLIST_NAME="com.acme.coyote"
PLIST_FILE=/Users/$user/Library/LaunchAgents/$PLIST_NAME.plist

echo "Plist Name: $PLIST_NAME"
echo "Plist File: $PLIST_FILE"

# Create your plist file here...

#cat <<EOF > $PLIST_FILE
#<?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>Label</key>
#<string>$PLIST_NAME</string>
#...
#</dict>
#</plist>
#EOF

echo "Plist file created"

#Unload
launchctl print gui/$uid/$PLIST_NAME &>/dev/null && launchctl bootout gui/$uid $PLIST_FILE
echo "Unloaded plist"

#Load
launchctl bootstrap gui/$uid $PLIST_FILE
echo "Loaded plist"

More info on launchctl 2.0 at https://babodee.wordpress.com/2016/04/09/launchctl-2-0-syntax/

Hope this helps a random person in the future.

slang
New Contributor II

Is that the correct syntax for a launchdaemon? Because for me it is not running, when I deploy it. The launchdaemon is loaded, but the script won't be executed.

#!/bin/bash

#### Create a script ####

cat << EOF > /Library/Folder/myScript.sh
#!/bin/bash

echo "Here is my script"
EOF

#### Create a plist file ####

cat << EOF > /Library/LaunchDaemons/com.jamf.launchdaemon.plist
<?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>KeepAlive</key>
    <true/>
    <key>Label</key>
    <string>com.jamf.launchdaemon</string>
    <key>ProgramArguments</key>
    <array>
        <string>sh</string>
        <string>/Library/Folder/myScript.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>
EOF

#### Set permissions for both files ####

chmod 644 /Library/LaunchDaemons/com.jamf.launchdaemon.plist
chown root:wheel /Library/LaunchDaemons/com.jamf.launchdaemon.plist

chmod 755 /Library/Folder/myScript.sh
chown root:wheel /Library/Folder/myScript.sh

#### Load launchdaemon ####

launchctl load /Library/LaunchDaemons/com.jamf.launchdaemon.plist

mm2270
Legendary Contributor III

@slang I don't know if it's at all related, but since you're already making the script executable, you don't need to have the <string>sh</string> line in the LaunchDaemon, since just calling the script should execute it fine. This is coming from my own experience in working with LaunchDaemons and LaunchAgents. I would try removing that from the ProgramArguments array and try it again to see if it works.
Also, was the script you're creating in what you posted above just an example or is that the script you're testing with? I ask because I'm wondering how you confirm if it's working or not working since all it's doing is echoing a line to stdout. To really test it, I would have the script output to a local file maybe.

slang
New Contributor II

No, this is not the original script. I just wanted to understand, if the syntax is fine.
In general i have a package with a script, a plist file and a postinstall script. All together works fine, when I deploy it via a package. But with this approach if using a simple script, it does not work so far. And I just want to understand, what the difference is :)