ansibleyamljinja2

Why does a single space in Ansible lineinfile module completely change the result


I solved my initial question how to enter a specific line into a file with Ansible. Now I still struggle to see why.

The minimal example is the following:

---
- name: Test line in file
  hosts: localhost
  tasks:
    - name: Set var
      set_fact:
        test_var: test

    - name: write to file
      lineinfile:
        path: test.txt
        create: True
        line: ' {"socket": "{{test_var}}"},'

This, as expected, creates a file with the line {"socket": "test"}, However, using

    - name: write to file
      lineinfile:
        path: test.txt
        create: True
        line: '{"socket": "{{test_var}}"},'

(Note the missing space ) results in the line [{'socket': 'test'}] . Can someone please explain what is happening? Bonus points if someone knows a solution inserting the expected line without a beginning space.


Solution

  • It is a side effect of how templating works.

    Ansible does some magic to try and convert strings that look like Python data structures to Python data structures.

    A: Writing what you want in a single Ansible expression is impossible.

    {"socket": "test"}
    

    and

    {socket: "test"}
    

    are valid dictionaries. See YAML Syntax. The first expression is also valid JSON. Note that JSON is a YAML subset.

    '{"socket": "test"},'
    
    '{socket: "test"},'
    
    '{socket: "{{ test_var }}"},'
    

    , but if the 'key' is quoted

    '{"socket": "{{ test_var }}"},'
    

    it evaluates to a list [{'socket': 'test'}]


    Example of a complete playbook for testing
    - hosts: localhost
    
      vars:
    
        test_var: test
      
      tasks:
    
        - debug:
            msg: |
              x: {{ item }}
              type: {{ item | type_debug }}
          loop:
            - {"socket": "test"}
            - {socket: "test"}
            - '{"socket": "test"}'
            - '{socket: "test"}'
            - '{"socket": "test"},'
            - '{socket: "test"},'
            - '{socket: "{{ test_var }}"},'
            - '{"socket": "{{ test_var }}"},'
    

    Details:

    • Both expressions below evaluate to a dictionary
    {"socket": "{{ test_var }}"}
    {socket: "{{ test_var }}"}
    
    • If you quote them the first expression evaluates to a dictionary. The second one evaluates to a string
    '{"socket": "{{ test_var }}"}'
    '{socket: "{{ test_var }}"}'