Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

best way to modify json in ansible

Tags:

json

ansible

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.

like image 407
Eyestrain Avatar asked Sep 03 '17 16:09

Eyestrain


People also ask

Can Ansible read JSON?

Ansible reads a JSON file into a variable from_json filter - converts the variable to JSON.

Can we write Ansible playbook in JSON?

In general you cannot represent YAML as JSON, because JSON is subset of YAML. E.g. YAML tags and anchors cannot be expressed in JSON, and the restrictions on keys in JSON object are extremely severe, whereas YAML essentially can have any node as key in a mapping.

What is JSON Ansible?

The json_query filter lets you query a complex JSON structure and iterate over it using a loop structure. Note. You must manually install the jmespath dependency on the Ansible controller before using this filter. This filter is built upon jmespath, and you can use the same syntax. For examples, see jmespath examples.


2 Answers

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"
        }
    }
}
like image 79
larsks Avatar answered Oct 22 '22 03:10

larsks


Actually Ansible is natively able to read JSON files.

see this question:

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

like image 24
Emmanuel Avatar answered Oct 22 '22 05:10

Emmanuel