Search code examples
linuxserveransiblecompareansible-template

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


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.


Solution

  • 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