Ansible template: Skip if there is no difference to existing file

370 Views Asked by At

actually i'm getting into Ansible and writing my first ever playbook, which does some general setup on my linux servers when i got them.

I am trying to get a Jinja2 template to apply, but only if the file does not exist or is different from the content in the Jinja2 template. If the content of the file is the same, the task should be skipped.

I got this behavior of skipping already for setting the timezone:

  - name: Check current Timezone
    command: timedatectl show --property=Timezone --value
    register: timezone_output
    changed_when: false

  - name: Configure Timezone
    command: timedatectl set-timezone {{ timezone }}
    when: timezone_output.stdout != timezone

But i cant get this for comparing files. I already tried this - but the checksums are not equal although the content of the files are the same:

---
- name: Calculate checksum of Jinja2 template
  set_fact:
    template_content: "{{ lookup('file', 'templates/sshd_config.j2') }}"

- name: Display checksum template
  debug:
    var: template_content | md5

- name: Calculate checksum of remote file
  command: md5sum /etc/ssh/sshd_config.d/initial.conf
  register: md5sum_output
  changed_when: false
  check_mode: no

- set_fact:
    remote_file_checksum: "{{ md5sum_output.stdout.split()[0] }}"
      
- name: Display checksum
  debug:
    var: remote_file_checksum

- name: Update SSH configuration
  template:
    src: sshd_config.j2
    dest: /etc/ssh/sshd_config.d/initial.conf
  notify: Reload SSH Service

Since I will have quite a few roles that will apply templates, it is important for me to skip the tasks if the content of the file is already correct to have a better evaluation later.

1

There are 1 best solutions below

0
Vladimir Botka On BEST ANSWER

The comment by Zeitounator is all you need to use the module template successfully. Let me explain why you see different checksums.

Short answer: Instead of the lookup plugin file use template.

Details: Given the template

shell> cat sshd_config.j2
Port: {{ sshd_Port }}
AddressFamily: {{ sshd_AddressFamily }}
ListenAddress: {{ sshd_ListenAddress4 }}
ListenAddress: {{ sshd_ListenAddress6 }}

and the variables

sshd_Port: 22
sshd_AddressFamily: any
sshd_ListenAddress4: 0.0.0.0
sshd_ListenAddress6: '::'

the below task

    - template:
        src: sshd_config.j2
        dest: /tmp/sshd_config

gives

shell> cat /tmp/sshd_config 
Port: 22
AddressFamily: any
ListenAddress: 0.0.0.0
ListenAddress: ::

You get the same result using the template lookup

    - debug:
        msg: "{{ lookup('template', 'sshd_config.j2') }}"

gives

  msg:
    Port: 22
    AddressFamily: any
    ListenAddress: 0.0.0.0
    ListenAddress: ::

However, when you use the file lookup

    - debug:
        msg: "{{ lookup('file', 'sshd_config.j2') }}"

the variables won't be expanded

  msg:
    Port: {{ sshd_Port }}
    AddressFamily: {{ sshd_AddressFamily }}
    ListenAddress: {{ sshd_ListenAddress4 }}
    ListenAddress: {{ sshd_ListenAddress6 }}

  • Example of a complete playbook for testing
- hosts: localhost

  vars:

    sshd_Port: 22
    sshd_AddressFamily: any
    sshd_ListenAddress4: 0.0.0.0
    sshd_ListenAddress6: '::'

  tasks:

    - debug:
        msg: "{{ lookup('template', 'sshd_config.j2') }}"

    - debug:
        msg: "{{ lookup('file', 'sshd_config.j2') }}"

    - template:
        src: sshd_config.j2
        dest: /tmp/sshd_config

There is one more problem if you want to compare the checksums

    - debug:
        msg: |
          {{ lookup('template', 'sshd_config.j2')|md5 }}
          {{ lookup('file', '/tmp/sshd_config')|md5 }}
          {{ lookup('file', 'sshd_config.j2')|md5 }}

gives

  msg: |-
    2ee29dc15e27ef8169fc557ea6e52263
    bfa2cb3f7cda847038b2591a6e07bbff
    6a91ff2707d18b5f397a03c8e6433b4e

The checksums of the first two items should be equal. Unfortunately, the template lookup adds an empty line to the result but the module template, when creating the file, removes this empty line. If you want to get the same checksums remove the last empty line from the result of the template lookup

    - debug:
        msg: |
          {{ tmpl|md5 }}
          {{ lookup('file', '/tmp/sshd_config')|md5 }}
          {{ lookup('file', 'sshd_config.j2')|md5 }}
      vars:
        tmpl: "{{ lookup('template', 'sshd_config.j2').splitlines()|join('\n') }}"

gives

  msg: |-
    bfa2cb3f7cda847038b2591a6e07bbff
    bfa2cb3f7cda847038b2591a6e07bbff
    6a91ff2707d18b5f397a03c8e6433b4e