How to modify json file on remote node?

Hello everyone :slight_smile:

Since Ansible doesn’t currently have a proper way of editing json file, I’ve made some research and found several solutions, like this one.

I would like to modify a json file to (1) add some key-value pair and (2) change existing one by appending them to existing variables (defined in my inventory).
For e.g. : remplace "location": 0.0.0.0 by "location": {{ ip_address }}

JSON file (located in /tmp/config) look like this:

{
    "option1": true,
    "option2": false,
    "option3": true,
    "location": 0.0.0.0, 
}

Here’s the playbook (inside a role):

---
- name: Load file
  ansible.builtin.slurp:
    src: /tmp/conf.json
  register: imported_var

- name: Add some key/value pair
  ansible.builtin.set_fact:
    imported_var: "{{ imported_var.content|b64decode|from_json | default([]) | combine({
    'option4': '60',
    'option5': 'test',
    'option6': '1.2.3.4'
    }) }}"

- name: write var to file
  ansible.builtin.copy:
    content: "{{ imported_var | to_nice_json }}"
    dest: /tmp/conf.json

How can I do it ? Since I’m adding multiple key/pair AND modify others with variables, is this method still appropriate ?

In advance, thanks for your help :heart: :pray:

Personally I’d convert the contents of the JSON file into YAML (for example using yq) and then add that to the role defaults/main.yml file and then template the file.

2 Likes

Thanks for your answer. But ideally, I would prefer to not rely on external tool or module outside the one provided in Ansible by default :nerd_face:

I think that’s a fine way to do it.

Can you expand a bit on what’s making you uncomfortable with that solution?

Indeed, it’s working fine at the moment.

The only issue with this solution is that I can’t nest a variable (defined earlier in my playbooks) inside another one, like this :

- name: Add some key/value pair
  ansible.builtin.set_fact:
    imported_var: "{{ imported_var.content|b64decode|from_json | default([]) | combine({
    'option4': '60',
    'option5': 'test',
    'option6': '1.2.3.4',
    'location': "{{ ip_address }}"
    }) }}"

Would you have a fix for this issue, or maybe am I doing it wrong ? :eyes:

Oh. You said that in your first post, and I totally overlooked it. Repeat after me:
Mustaches don’t nest.

Try this:

- name: Add some key/value pair
  ansible.builtin.set_fact:
    imported_var: "{{ imported_var.content | b64decode | from_json | default([])
                      | combine({'option4': '60',
                                 'option5': 'test',
                                 'option6': '1.2.3.4',
                                 'location': ip_address
                                }
                               )
                   }}"
2 Likes

That’s always a good advice, thanks :nerd_face:

But then, what are my options if I want to insert the {{ ip_address }} variable inside this json file ? :woman_shrugging:

{{ ip_address }} is a variable (ip_address) inside a Jinja context ({{ }}).

In the prior post, you already have a Jinja context from the outer mustaches ({{ … }}) — that’s why things like “from_json” and “default()” work as something other than plain text. That also works for all your variables, like “ip_address”, but without the quotes of course; because with quotes it’s just a string.

Take a closer look at the example I posted. Your ip_address variable is there, doing its “replace me with my value in this expression” thing.

2 Likes

Thanks for explaining how the Jinja context works in this situation, I understand better now :grinning:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.