Search code examples
ansiblejinja2

How to prevent Jinja2 substitution in Ansible


I goal is to replace all the {{ and }} with {% raw %}{{ and }}{% endraw %} in all my markdown notes so that my hexo plugin won't attempt to template when rendering.

My code is:

- name: Get file list of post files
  find:
    paths: "{{ hexo_data_post_file_dir }}"
  register: hexo_data_post_file_list

- name: Replace special char
  replace:
    path: "{{ item[0]['path'] }}"
    regexp: "{{ item[1]['pattern'] }}"
    replace: "{{ item[1]['string'] }}"
  with_nested:
    - "{{ hexo_data_post_file_list['files'] }}"
    - - pattern: "{{  '{{' }}"
        string: "{{ '{% raw %}{{' }}"
      - pattern: "{{ '}}' }}"
        string: "{{ '}}{% endraw %}' }}"

Despite I used "{{ '{{' }}" to ask jinja not to render {{, I still get error:

TASK [setup_docker_hexo : Replace special char] ********************************************************************************************************************************************
Tuesday 27 December 2022  11:58:56 +0800 (0:00:00.491)       0:00:13.572 ******
fatal: [uranus-debian]: FAILED! => {"msg": "template error while templating string: unexpected 'end of template'. String: {{. unexpected 'end of template'"}


Solution

  • Given the file

    shell> cat test.txt.j2
    {{ test_var }}
    

    Declare the strings unsafe to block templating. For example,

      my_files:
        - "{{ playbook_dir }}/test.txt.j2"
    
      my_regex:
        - pattern: !unsafe '{{ '
          string: !unsafe '{% raw %}{{ {% endraw %}'
        - pattern: !unsafe ' }}'
          string: !unsafe '{% raw %} }}{% endraw %}'
    

    Test the expansion

        - debug:
            msg: "{{ item }}"
          with_nested:
            - "{{ my_files }}"
            - "{{ my_regex }}"
    

    gives

    TASK [debug] *********************************************************************************
    ok: [localhost] => (item=['/export/scratch/tmp7/test-117/test.txt.j2', {'pattern': '{{ ', 'string': '{% raw %}{{ {% endraw %}'}]) => 
      msg:
      - /export/scratch/tmp7/test-117/test.txt.j2
      - pattern: '{{ '
        string: '{% raw %}{{ {% endraw %}'
    ok: [localhost] => (item=['/export/scratch/tmp7/test-117/test.txt.j2', {'pattern': ' }}', 'string': '{% raw %} }}{% endraw %}'}]) => 
      msg:
      - /export/scratch/tmp7/test-117/test.txt.j2
      - pattern: ' }}'
        string: '{% raw %} }}{% endraw %}'
    

    Replace the patterns

        - replace:
            path: "{{ item.0 }}"
            regexp: "{{ item.1.pattern }}"
            replace: "{{ item.1.string }}"
          with_nested:
            - "{{ my_files }}"
            - "{{ my_regex }}"
        - debug:
            msg: "{{ lookup('file', my_files.0) }}"
        - debug:
            msg: "{{ lookup('template', my_files.0) }}"
    

    gives

    TASK [debug] *********************************************************************************
    ok: [localhost] => 
      msg: '{% raw %}{{ {% endraw %}test_var{% raw %} }}{% endraw %}'
    
    TASK [debug] *********************************************************************************
    ok: [localhost] => 
      msg: |-
        {{ test_var }}
    

    Declare the variable

      my_file: "{{ playbook_dir }}/test.txt"
      test_var: foo bar
    

    and use the template

        - template:
            src: "{{ my_files.0 }}"
            dest: "{{ my_file }}"
        - debug:
            msg: "{{ lookup('file', my_file) }}"
        - debug:
            msg: "{{ lookup('template', my_file) }}"
    

    gives

    TASK [debug] *********************************************************************************
    ok: [localhost] => 
      msg: '{{ test_var }}'
    
    TASK [debug] *********************************************************************************
    ok: [localhost] => 
      msg: |-
        foo bar
    

    Notes

    • Example of a complete playbook for testing
    - hosts: localhost
    
      vars:
    
        my_files:
          - "{{ playbook_dir }}/test.txt.j2"
    
        my_regex:
          - pattern: !unsafe '{{'
            string: !unsafe '{% raw %}{{{% endraw %}'
          - pattern: !unsafe '}}'
            string: !unsafe '{% raw %}}}{% endraw %}'
    
        my_file: "{{ playbook_dir }}/test.txt"
        test_var: foo bar
    
      tasks:
    
        - debug:
            msg: "{{ item }}"
          with_nested:
            - "{{ my_files }}"
            - "{{ my_regex }}"
    
        - replace:
            path: "{{ item.0 }}"
            regexp: "{{ item.1.pattern }}"
            replace: "{{ item.1.string }}"
          with_nested:
            - "{{ my_files }}"
            - "{{ my_regex }}"
        - debug:
            msg: "{{ lookup('file', my_files.0) }}"
        - debug:
            msg: "{{ lookup('template', my_files.0) }}"
    
        - template:
            src: "{{ my_files.0 }}"
            dest: "{{ my_file }}"
        - debug:
            msg: "{{ lookup('file', my_file) }}"
        - debug:
            msg: "{{ lookup('template', my_file) }}"
    
    • The playbook is not idempotent!