Setting up a CRON job manually is a child’s play so why am I writing about it? The reason I’m writing about it because of two main reasons:
- My experience of setting it up with Ansible
- Common mistakes I made which others can avoid
CRON
CRON daemon is a long-running process that executes commands at specific dates and times. This makes it easy to schedule activities like sending bulk emails. The CRON relies on a crontab file that has a list of scheduled commands to run. So we can manually add/edit/remove scheduled commands directly in the crontab file but this may induce bugs, especially when the list is too long. Ansible helps in deploying such CRON jobs effortlessly.
Ansible
Ansible is an IT automation and orchestration engine. It uses YAML file syntax for us to write such automation called Plays and the file itself is referred to as a Playbook. Playbooks contain Plays. Plays contain Tasks. And Tasks run pre-installed modules sequentially and trigger optional handlers.
Similarly, I have used a CRON module in Ansible to set up a task which configures a CRON job as follows:
- name: Run CRON job every day at 11.05 PM UTC
become: yes
become_method: sudo
cron:
name: "apache_restart"
user: "root"
hour: 23
minute: 5
job: "/bin/sh /usr/sbin/apache_restart.sh"
state: present
Imagine there is a fictional CRON job to run Apache2 at the specified time every day – god knows why? But I made unfair mistakes initially while setting it up. Let us go step by step into each of those mistakes:
become and become_method
These flags are only necessary while running the job
with sudo
or any other privilege escalation method. In this case, I wanted to run /bin/sh /usr/sbin/apache_restart.sh
command with sudo
and I wished not to expect a password prompt that we usually get while running such commands manually. So the become
flag prevents the password prompt.
In the beginning, I had forgotten to add these flags preventing the CRON job from executing the bash apache_restart.sh
file as expected.
cron module
Ansible lets us use the pre-installed CRON module so that it will be far easy to setup CRON jobs. Although, by mistake, I had made CRON module an Ansible task as mentioned below.
- cron:
name: "apache_restart"
user: "root"
hour: 23
minute: 5
job: "/bin/sh /usr/sbin/apache_restart.sh"
state: present
As we learned before, only Tasks can run pre-installed modules. So Ansible instantly threw an error while deploying and I managed to save my face 🤦♂️
cron name
I thought that since I had already named the task, naming the CRON module will not be necessary. But I was embarrassed more than wrong. Because each time you deploy any changes to Ansible, without a CRON name, it will set up a new CRON job leaving the previous one as is. So I was literally restarting Apache2 thrice at a time. Remember, the CRON name works as a unique key to identify if any CRON job is already set up with the same name. If not, it will set up a brand new CRON job in the crontab file. Otherwise, override the existing one with new configurations.
state
The default state of the CRON job is present. Although to disable a particular CRON job, you change the state to absent
and redeploy it via Ansible. I was using the state present
without the CRON name that was creating multiple crontab entries on each deployment.
job
The job key takes the actual command that you want to run at a specific time/date. But make to use absolute command paths for brevity.
Wrap up
I also use tail -f /var/log/syslog
and grep CRON /var/log/syslog
commands to check the logs to make sure that CRON actually runs the bash file I specified.
Hi. Thank you for great article.
How to prevent Ansible from add cronjob on every Ansible run?
run_once: true doesn’t help
Appreciate.
You mean that it’s adding a new cron entry in the Crontab? If yes, I resolved it with `cron name` (read about it above if you missed).
Great. Thank you