stminion001
stminion001

Reputation: 353

Ansible, line module not saving JSON data to file correctly

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?

Upvotes: 1

Views: 377

Answers (1)

Vladimir Botka
Vladimir Botka

Reputation: 68034

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"}}] }

Upvotes: 2

Related Questions