Search code examples
cronansible

Idempotence and Random Variables in Ansible


Is there a way to guarantee idempotence for playbooks that use randomly generated variables?

For example, I want to setup my crontabs to trigger emails on multiple servers at different times, so I create random integers using ansible's set_fact module:

  tasks:
  - set_fact:
      first_run_30="{{ 30 | random }}"
    run_once: yes

Then apply those generated variables to my crontab using ansible like so:

   - name: Setup cron30job
    cron: name=cron30job minute={{first_run_30}},{{first_run_30 | int + 30}} job='/bin/bash /cron30job.sh' state=present user=root
    environment:
      MAILTO: '[email protected]'
      MAILFROM: '[email protected]'

This works very well, however, ansible's indempotence principle is, I believe, broken using this strategy because each time a play is made you see a change:

TASK: [Setup cron30job] ***************************************** 
changed: [127.0.0.1]

Further, in the crontab checking under root each time during three separate runs:

[ansible]# cat /var/spool/cron/root 
#Ansible: cron30job
5,35 * * * * /bin/bash /sw/test/cron30job.sh
#Ansible: cron30job
9,39 * * * * /bin/bash /sw/test/cron30job.sh
#Ansible: cron30job
6,36 * * * * /bin/bash /sw/test/cron30job.sh

If there is a workaround, or maybe indempotence just will not be possible in my scenario, I would like to know.


Solution

  • Instead of a random value, you could get something related to the node, like an hash of the hostname or the last byte of the ip address.

    This is an example:

    - name: Get a pseudo-random minute 
      shell: expr $((16#`echo "{{inventory_hostname}}" | md5sum | cut -c 1-4`)) % 30
      register: minute
      changed_when: false