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 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 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
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.
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 🤦♂️
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.
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.
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.
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.