1

I have a playbook that sets the variable "certData" which is an array of dictionaries. This is what "certData" looks like:

ok: [localhost] => {
    "msg": [
        {
            "cn": "node1.corp.com",
            "expires": "2020-11-05T15:20:18+00:00",
            "id": 705,
            "serial": "111"
        },
        {
            "cn": "node2.corp.com",
            "expires": "2020-11-05T15:20:28+00:00",
            "id": 728,
            "serial": "222"
        },
        {
            "cn": "node3.corp.com",
            "expires": "2020-10-28T19:37:29+00:00",
            "id": 670,
            "serial": "333"
        },
        {
            "cn": "node4.corp.com",
            "expires": "2021-04-04T09:55:08+00:00",
            "id": 1163,
            "serial": "444"
        },
        {
            "cn": "node5.corp.com",
            "expires": "2020-11-05T15:20:22+00:00",
            "id": 715,
            "serial": "555"
        }
    ]
}

I have a play that is taking the data from "certData" and saving it a file called cert_expiring.json like this:

- name: Testing jinja
      lineinfile:
        dest: "./cert_expiring.json"
        line: |-
          {
          "devices": [{
              "items": {
          {%- for i in certData -%}
          {{ "" if loop.first else "," }}
                "{{ i.id }}": "{{ i.cn }}"
          {%- endfor %}
            
              }
          }]
          }
        state: present
        create: yes

The playbook runs successfully but gives this warning:

[WARNING]: The value {'devices': [{'items': {'705': 'node1.corp.com', '715': 'node5.corp.com', '670': 'node3.corp.com', '728': 'node2.corp.com', '1163':
'node4.corp.com'}}]} (type dict) in a string field was converted to u"{'devices': [{'items': {'705': 'node1.corp.com', '715': 'node5.corp.com', '670':
'node3.corp.com', '728': 'node2.corp.com', '1163': 'node4.corp.com'}}]}" (type string). If this does not look like what you expect, quote the entire value to ensure it does not
change.

The data from the warning is exactly how it gets printed to the "cert_expiring.json" file with single quotes.

certs_expiring.json:

{'devices': [{'items': {'705': 'node1.corp.com', '715': 'node5.corp.com', '670': 'node3.corp.com', '728': 'node2.corp.com', '1163': 'node4.corp.com'}}]}

But, when I load the contents of the "cert_expiring.json" back into a variable (expired_certs2) and print it, it prints properly:

 - name: Store file contents into variable
   include_vars:
     file: cert_expiring.json
     name: expired_certs2
    
 - name: Print expired_certs2 variable.
   debug:
     msg: "{{ expired_certs2 | json_query('devices[*].items') }}"
TASK [Print expired_certs2 variable.] **********************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        {
            "1163": "DUKEISEC12.corp.cox.com",
            "670": "sage.stg.cox.com",
            "705": "btnrssmoloch01.corp.cox.com",
            "715": "elmwssmoloch01.corp.cox.com",
            "728": "rnkessmoloch01.corp.cox.com"
        }
    ]
}

I tried single quoting the Jinja code like this but it doesn't help:

- name: Testing jinja
      lineinfile:
        dest: "./cert_expiring.json"
        line: |-
          '{
          "devices": [{
              "items": {
          {%- for i in certData -%}
          {{ "" if loop.first else "," }}
                "{{ i.id }}": "{{ i.cn }}"
          {%- endfor %}
            
              }
          }]
          }'
        state: present
        create: yes

Also, tried using double quotes with no luck.

How can I get the data to print to the "cert_expiring.json" file in proper json format?

stminion001
  • 333
  • 2
  • 15

1 Answers1

2

Q: "How can I get the data to print to the "cert_expiring.json" file in proper JSON format?"

A: Use filter to_json, e.g.

    - include_vars:
        file: cert_expiring.json
        name: expired_certs2
    - debug:
        msg: "{{ expired_certs2 | to_json }}"

gives

  msg: '{"devices": [{"item": {"705": "node1.corp.com", "728": "node2.corp.com", "670": "node3.corp.com", "1163": "node4.corp.com", "715": "node5.corp.com"}}]}'

The query

    - debug:
        msg: "{{ expired_certs2.devices | json_query('[].item') | to_json }}"

gives

  msg: '[{"705": "node1.corp.com", "728": "node2.corp.com", "670": "node3.corp.com", "1163": "node4.corp.com", "715": "node5.corp.com"}]'

It's possible to simplify the content, e.g.

    - set_fact:
        devices: "{{ devices | default([]) +
                     [{'item': certData | items2dict(key_name='id', value_name='cn')}] }}"
    - copy:
        dest: cert_expiring.json
        content: >
          {"devices": {{ devices | to_json }} }

gives

shell> cat cert_expiring.json
{"devices": [{"item": {"705": "node1.corp.com", "728": "node2.corp.com", "670": "node3.corp.com", "1163": "node4.corp.com", "715": "node5.corp.com"}}] }
Vladimir Botka
  • 58,131
  • 4
  • 32
  • 63
  • when i apply the "to_json" filter and print expired_certs2 my output prints like yours but all the double quotes are excaped ("msg": "{\"devices\": [{\"items\": {\"705\":...). when i try to print using `json_query('[].items') | to_json`, this prints the same with escaped double quotes. when taking your simplified approach it works and creates the cert_expiring.json file with the proper syntax....btw **devices** should be in double quotes correct? – stminion001 May 25 '21 at 15:24
  • This depends on the callback plugin. See ``ansible-doc -t callback -l``. I use ``yaml`` callback plugin. The escaped quotation might be a result of the ``json`` callback plugin. The callback plugin is responsible for printing the data. The Jinja2 templating is not influenced by callback plugins. – Vladimir Botka May 25 '21 at 15:36
  • You're right. JSON standard requires double quotes. I've fixed the answer. Thank you. – Vladimir Botka May 25 '21 at 15:40