syntax error in crontab

Good morning. I am updating a cron job by replacing a line in the crontab file. My error comes when I use the "" (see below) to escape the “(”. Using the escape character works on the command line but Ansible keeps giving me a syntax error. Here is my code:

  • name: Check for cron job to remove wazuh logs
    lineinfile:
    path: “{{ cron_job_path[ansible_distribution] }}”
    regexp: “find /var/ossec/logs”
    line: “{{ 59|random }} {{ 23|random }} * * * find /var/ossec/logs/ ( -name ‘.gz’ -o -name '.json’ -o -name ‘*.log’ ) -type f -mtime +2 -delete; find /var/ossec/logs -type d -empty -delete”
    state: present

With this code, Ansible states “this one looks like it might be an issue with missing quotes”.

I need to escape the “(” for it to work in crontab/bash command line. When I delete the "" characters the Ansible error goes away, but of course it does not work in crontab/bash.

I’ve tried using " around each of the commands but that doesn’t work either.

Does anyone see where I have gone wrong?

To get the literal backslashes in the crontab file, you need to escape the backslashes (“\”) in your double-quoted YAML string.

Alternatively, you could change your double-quoted string to a single-quoted string, because backslashes aren’t “special” in single-quoted strings. If you do that, you’ll also need to double the single quotes to get literal single quotes — i.e. ‘.gz’ becomes ‘’.gz’’ etc. (Double-quoted strings and single-quoted strings have completely different rules.)

Another alternative is to change your string to a “block scalar” (https://yaml-multiline.info/) where all quotes and backslashes are not “special”:

  line: |
    {{ 23|random }} * * * find /var/ossec/logs/ \( -name '*.gz' -o -name '*.json' -o -name '*.log' \) -type f -mtime +2 -delete; find /var/ossec/logs -type d -empty -delete

That’s great - thanks. Not sure i understood “block scalars” before.

What Todd said. Worth mentioning that using ansible.builtin.cron might be slightly cleaner, although you do still have the same quoting/escaping concerns with the job specification.

Also worth mentioning that using {{ 59|random }} & {{ 23|random }} will result in a CHANGED every time you run the play or role. You can still get a pseudo-random variation in cronjob execution timing but consistency across runs using something like:

{{ range(0,59) | random(seed=inventory_hostname) }}