best way to modify json in ansible

10,342

Solution 1

I don't know about the best way, but one option is to write a simple library module to handle the mechanics of the update for you. You could use the jsonpointer module as a way of locating the data you wish to modify, and then return the modified object to ansible. A starting point might look like:

#!/usr/bin/python

from ansible.module_utils.basic import AnsibleModule

import json

try:
    import jsonpointer
except ImportError:
    jsonpointer = None


def main():
    module = AnsibleModule(
        argument_spec=dict(
            data=dict(required=True, type='dict'),
            pointer=dict(required=True),
            action=dict(required=True,
                        choices=['append', 'extend', 'update']),
            update=dict(type='dict'),
            extend=dict(type='list'),
            append=dict(),
        ),
        supports_check_mode=True,
    )

    if jsonpointer is None:
        module.fail_json(msg='jsonpointer module is not available')

    action = module.params['action']
    data = module.params['data']
    pointer = module.params['pointer']

    if isinstance(data, str):
        data = json.loads(str)

    try:
        res = jsonpointer.resolve_pointer(data, pointer)
    except jsonpointer.JsonPointerException as err:
        module.fail_json(msg=str(err))

    if action == 'append':
        res.append(module.params['append'])
    if action == 'extend':
        res.extend(module.params['extend'])
    elif action == 'update':
        res.update(module.params['update'])

    module.exit_json(changed=True,
                     result=data)


if __name__ == '__main__':
    main()

If you drop this into, e.g., library/json_modify.py, you can use it in a playbook like this:

- hosts: localhost
  gather_facts: false
  vars:
    myvar: {
        "PolicyVersion": {
          "CreateDate": "2017-08-07T02:48:05Z",
          "Document": {
            "Statement": [
              {
                "Action": "sts:AssumeRole",
                "Effect": "Allow",
                "Resource": [
                  "arn:aws:iam::123456789123:role/Root_Update_svcacct",
                  "arn:aws:iam::123456789123:role/Root_Delete_svcacct",
                  "arn:aws:iam::123456789123:role/Root_Create_svcacct",
                  "arn:aws:iam::123456789123:role/Root_Full_svcacct",
                  "arn:aws:iam::987654321987:role/Member1_Create_svcacct",
                  "arn:aws:iam::987654321987:role/Member1_Update_svcacct",
                  "arn:aws:iam::987654321987:role/Member1_Delete_svcacct",
                  "arn:aws:iam::987654321987:role/Member1_Full_svcacct"
                ]
              }
            ],
            "Version": "2012-10-17"
          },
          "IsDefaultVersion": true,
          "VersionId": "v2"
        }
      }
  tasks:
    - json_modify:
        data: "{{ myvar }}"
        pointer: "/PolicyVersion/Document/Statement/0/Resource"
        action: extend
        extend:
          - "arn:aws:iam::001122334455:role/Member1_Create_svcacct"
          - "arn:aws:iam::001122334455:role/Member1_Update_svcacct"
          - "arn:aws:iam::001122334455:role/Member1_Delete_svcacct"
          - "arn:aws:iam::001122334455:role/Member1_Full_svcacct"
      register: result

    - debug:
        var: result.result

The result of running this playbook and the proposed module is:

TASK [debug] *******************************************************************
ok: [localhost] => {
    "result.result": {
        "PolicyVersion": {
            "CreateDate": "2017-08-07T02:48:05Z", 
            "Document": {
                "Statement": [
                    {
                        "Action": "sts:AssumeRole", 
                        "Effect": "Allow", 
                        "Resource": [
                            "arn:aws:iam::123456789123:role/Root_Update_svcacct", 
                            "arn:aws:iam::123456789123:role/Root_Delete_svcacct", 
                            "arn:aws:iam::123456789123:role/Root_Create_svcacct", 
                            "arn:aws:iam::123456789123:role/Root_Full_svcacct", 
                            "arn:aws:iam::987654321987:role/Member1_Create_svcacct", 
                            "arn:aws:iam::987654321987:role/Member1_Update_svcacct", 
                            "arn:aws:iam::987654321987:role/Member1_Delete_svcacct", 
                            "arn:aws:iam::987654321987:role/Member1_Full_svcacct", 
                            "arn:aws:iam::001122334455:role/Member1_Create_svcacct", 
                            "arn:aws:iam::001122334455:role/Member1_Update_svcacct", 
                            "arn:aws:iam::001122334455:role/Member1_Delete_svcacct", 
                            "arn:aws:iam::001122334455:role/Member1_Full_svcacct"
                        ]
                    }
                ], 
                "Version": "2012-10-17"
            }, 
            "IsDefaultVersion": true, 
            "VersionId": "v2"
        }
    }
}

Solution 2

Actually Ansible is natively able to read JSON files.

see this question:

add-a-new-key-value-to-a-json-file-using-ansible

Share:
10,342

Related videos on Youtube

Eyestrain
Author by

Eyestrain

Updated on September 16, 2022

Comments

  • Eyestrain
    Eyestrain about 1 year

    I have a variable (via set_fact) containing a json string:

    {
      "PolicyVersion": {
        "CreateDate": "2017-08-07T02:48:05Z",
        "Document": {
          "Statement": [
            {
              "Action": "sts:AssumeRole",
              "Effect": "Allow",
              "Resource": [
                "arn:aws:iam::123456789123:role/Root_Update_svcacct",
                "arn:aws:iam::123456789123:role/Root_Delete_svcacct",
                "arn:aws:iam::123456789123:role/Root_Create_svcacct",
                "arn:aws:iam::123456789123:role/Root_Full_svcacct",
                "arn:aws:iam::987654321987:role/Member1_Create_svcacct",
                "arn:aws:iam::987654321987:role/Member1_Update_svcacct",
                "arn:aws:iam::987654321987:role/Member1_Delete_svcacct",
                "arn:aws:iam::987654321987:role/Member1_Full_svcacct"
              ]
            }
          ],
          "Version": "2012-10-17"
        },
        "IsDefaultVersion": true,
        "VersionId": "v2"
      }
    }
    

    What is the best way to insert more elements in the "Resource" array?

    "arn:aws:iam::001122334455:role/Member1_Create_svcacct",
    "arn:aws:iam::001122334455:role/Member1_Update_svcacct",
    "arn:aws:iam::001122334455:role/Member1_Delete_svcacct",
    "arn:aws:iam::001122334455:role/Member1_Full_svcacct"
    

    I am exploring dumping the variable to a file and inserting the block I want with external shell tools, which does not seem to be elegant.

  • Sion
    Sion over 5 years
    Ansible will look in the library directory relative to the playbook. In this example, put the custom module in playbook/library/json_modify.py.
  • Juan Jimenez
    Juan Jimenez about 5 years
    @larsks I realize this thread is old, but the code for json_modify appears to change the resulting json to add an extra closing bracket at the end. The result fails.
  • larsks
    larsks about 5 years
    @JuanJimenez I don't think it does: the code doesn't do anything that could add an unbalanced bracket like that (it only manipulates the data using the json and jsonpointer modules, both of which are pretty stable). If you have a link to a failing example, let me know and I would be happy to update the answer.
  • fabiog
    fabiog almost 4 years
    Thanks, it works great. Sad to see there is no Ansible way to do that instead. I played around with set_fact and combine, but no luck. Better to do it in pure python via a module... but sad!!