Search code examples
jsonansiblejinja2raw

Is there a way to insert raw escaped json in json body in ansible


See below first call which works. This is an example of how I create a template on my system. Notice I have to use {% raw %} ... {% endraw %} so ansible doesn't try to interpret the variables in my template.

    - name: Create a template : OK
      uri:
        url: https://{{ ip }}/api/v1/templates
        method: POST
        headers:
          Authorization: "{{ token }}"
          Content-Type: application/json
        body: |
          {
            "name": "{{ template_name }}",
            "type": "v1",
            "description": "{{ template_name }}",
            "content": "template: |  {% raw %}\n    {\n   \"class\": \"ABC\",\n      \"param\": {{param1::integer}}\n  }{% endraw %}"
          }
        body_format: json
        timeout: 60
        status_code: 202
        validate_certs: false
      register: json_response

Output OK:

    ok: [notahost] => {
        "invocation": {
            "module_args": {
                "body": {
                    "content": "template: |  \n    {\n   \"class\": \"ABC\",\n      \"param\": {{param1::integer}}\n  }", 
                    "description": "test1", 
                    "name": "test1", 
                    "type": "v1"
                }, 
                "body_format": "json", 

Now, I am trying to move the content of the template outside in a file called template1.j2.

template1.j2:

    {% raw %} 
    {
      "class": "ABC",
      "param": {{param1::integer}}
    } 
    {% endraw %}

I insert the template template1.j2 into my JSON body (noticed I added the {% raw %} ... {% endraw %} inside the template).

    - name: Create a template NOK
      uri:
        url: https://{{ ip }}/api/v1/templates
        method: POST
        headers:
          Authorization: "{{ token }}"
          Content-Type: application/json
        body: |
          {
            "name": "{{ template_name }}",
            "type": "v1",
            "description": "{{ template_name }}",
            "content": "template: | {{ lookup('file','template1.j2') }}"
          }
        body_format: json
        timeout: 60
        status_code: 202
        validate_certs: false
      register: json_response

Output NOK:

    fatal: [notahost]: FAILED! => {
        "content": "{\"message\":\"request body has an error: failed to decode request body: invalid character '\\\\n' in string literal\"}\n", 
        "invocation": {
            "module_args": {
                "body": "{\n  \"name\": \"test2\",\n  \"type\": \"v1\",\n  \"description\": \"test2\",\n  \"content\": \"template: |  \n{\n    \"class\": \"ABC\",\n    \"param\": {{param1::integer}}\n} \n\"\n}\n", 
                "body_format": "json", 

For some reason, it looks like the way I am doing this doesn't work, ansible still try to interpret the variable in my template at creation.

Any idea on how to get his work with my template outside the ansible task?

PS: I have tried to load the template file using the shell ansible module and that did not help.

Thanks & Regards, Romain


Solution

  • Use lookup 'file' instead of 'template', e.g. the template (that you actually don't want to use as a template in this task)

    shell> cat template1.j2
    param: {{param1.integer}}
    

    and the play

    - hosts: localhost
      vars:
        param1:
          integer: 99
      tasks:
        - debug:
            msg: |
              {{ lookup('template', 'template1.j2') }}
              {{ lookup('file', 'template1.j2') }}
    

    gives

      msg: |-
        param: 99
      
        param: {{param1.integer}}
    

    Given the template

    shell> cat template1.j2
    {
      "class": "ABC",
      "param": {{param1::integer}}
    }
    

    The play below shows how to create a body with and without the template

    - hosts: localhost
    
      vars:
        template_name: test1
    
        body1: |
          {
          "name": "{{ template_name }}",
          "type": "v1",
          "description": "{{ template_name }}",
          "content": "template: |  {% raw %}\n    {\n   \"class\": \"ABC\",\n      \"param\": {{param1::integer}}\n  }{% endraw %}"
          }
    
        body2:
          {
          "name": "{{ template_name }}",
          "type": "v1",
          "description": "{{ template_name }}",
          "content": "template: |  \n{{ lookup('file','template1.j2') }}"
          }
    
      tasks:
        - debug:
            var: body1|type_debug
        - debug:
            var: body1
        - debug:
            var: body2|type_debug
        - debug:
            var: body2
    

    gives

    ok: [localhost] => 
      body1|type_debug: dict
    
    ok: [localhost] => 
      body1:
        content: |-
          template: |
              {
             "class": "ABC",
                "param": {{param1::integer}}
            }
        description: test1
        name: test1
        type: v1
    
    ok: [localhost] => 
      body2|type_debug: dict
    
    ok: [localhost] => 
      body2:
        content: |-
          template: |
          {
            "class": "ABC",
            "param": {{param1::integer}}
          }
        description: test1
        name: test1
        type: v1