Add something to crontab programmatically (over ssh)

my best idea so far

to check first if the content matches what should be in there and only update if it doesn't:

if [[ $(crontab -l | egrep -v "^(#|$)" | grep -q 'some_command'; echo $?) == 1 ]]
then
    set -f
    echo $(crontab -l ; echo '* 1 * * * some_command') | crontab -
    set +f
fi

but this gets complicated enough to build a separate script around that cron task.

other ideas

you could send the string via stdin to crontab (beware, this clears out any previous crontab entries):

echo "* 1 * * * some_command" | crontab -

this should even work right through ssh:

echo "* 1 * * * some_command" | ssh user@host "crontab -"

if you want to append to the file you could use this:

# on the machine itself
echo "$(echo '* 1 * * * some_command' ; crontab -l)" | crontab -
# via ssh
echo "$(echo '* 1 * * * some_command' ; ssh user@host crontab -l)" | ssh user@host "crontab -"

For the record I'm going to suggest using /etc/cron.d/. Only root can write files here but the entries can be configured to run as any user (without need for sudo at run-time),

echo '0 0 * * 0 webadmin /usr/local/bin/tidy_logfiles' >/etc/cron.d/my_webadmin

An important part is that the my_webadmin should be unique to you (not necessarily unique for the run, though) because any installation package can also write files here and you want to avoid a clash. Having this uniqueness constraint, you can update my_webadmin with a simple overwrite, since you know it's "yours" and won't contain entries for anyone/anything else.

Furthermore, with this approach it becomes trivial to remove the cron entry

rm -f /etc/cron.d/my_webadmin

Possibly outside the scope of your question, but if you have remote access to the root account (or via sudo) you can even provision remotely,

echo '0 0 * * 0 webadmin /usr/local/bin/tidy_logfiles' > ~/webadmin.cron
scp -p ~/webadmin.cron root@remote_host:/etc/cron.d/my_webadmin

or,

echo '0 0 * * 0 webadmin /usr/local/bin/tidy_logfiles' |
    ssh -q root@remote_host 'cat >/etc/cron.d/my_webadmin'

and remove the provisioning,

ssh -nq root@remote_host rm -f /etc/cron.d/my_webadmin

(Note that in many cases you cannot provide root's password for the scp/ssh commands because the root account is constrained to prevent password-based logins. Instead you need to have set up public/private key certificates. Also, by implication the local account (whatever it is) will have full root access to the remote server.)


I highly recommend using Ansible* for this rather than rolling your own. Or Puppet or Chef — but Ansible is well-suited for zero-infrastructure deploy scripts like this.

That's because there are already modules meant to solve problems like this, and config management tools have have idempotence as a basic design goal — that's the property of only changing when it needs to even if you accidentally (or intentionally) run it again.

In particular, Ansible's cron module can modify user crontabs. As a bonus, if you want to later adjust to use system crontabs, it'll be a very easy tweak rather than a rewrite.


* disclaimer: I work for Red Hat, and Ansible is a Red Hat sponsored project.

Tags:

Scripting

Cron