在ansible中修改json的最佳方法

时间:2017-09-03 16:45:52

标签: json ansible

我有一个包含json字符串的变量(通过set_fact):

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

在“资源”数组中插入更多元素的最佳方法是什么?

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

我正在探索将变量转储到文件并使用外部shell工具插入我想要的块,这看起来并不优雅。

3 个答案:

答案 0 :(得分:8)

我不知道最佳方式,但一个选项是编写一个简单的库模块来为您处理更新的机制。您可以使用jsonpointer模块来查找要修改的数据,然后将修改后的对象返回到ansible。起点可能如下:

#!/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()

如果你将其放入,例如library/json_modify.py,你可以在这样的剧本中使用它:

- 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

运行此剧本和建议的模块的结果是:

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

答案 1 :(得分:0)

实际上Ansible本身可以读取JSON文件。

看到这个问题:

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

答案 2 :(得分:0)

这有点旧。我知道,但我觉得它可能对很多人有用,所以我会在这里发布我的解决方案,作为我个人可以找到的“修改 json 的最佳方法”。

首先是 JSON。在我的用例中,我有一堆使用错误的 kms 密钥加密的 aws 快照,我不得不使用正确的密钥重新创建 AMI。

所以我不得不:

  • 获取旧快照的数据(如大小 device_name 等)
  • 使用不同的键创建新快照
  • 使用正确的 block_device_mapping 重新注册一个新的 ami 这是代码
      - name: get ami
        amazon.aws.ec2_ami_info:
          image_ids: ami-<id>
          region: "{{ region }}"
        register: ami
      
      - name: save snapshot ids and device_name and volume_size
        set_fact:
          snapshot_ids: "{{ ami | json_query('images[].block_device_mappings[].ebs.snapshot_id') }}"
          device_name: "{{ ami  | json_query('images[].block_device_mappings[].device_name') }}"
          volume_size: "{{ ami | json_query('images[].block_device_mappings[].ebs.volume_size') }}"

基本上,上面的每一个都是我需要(但可以扩展)的 3 个东西(device_name、snap_id、volume_size)中每一个的列表

然后:

      - name: get kms arn
        aws_kms_info:
          filters:
            alias: "{{ kms_keys.alias }}"
          region: "{{ region }}"
        register: aws_kms_facts_out
      - debug:
          var: aws_kms_facts_out
      - set_fact:
          kms_arn: "{{ aws_kms_facts_out['keys'][0].key_arn }}"

      - name: copy snap with new encryption key
        community.aws.ec2_snapshot_copy:
          source_region: "{{ region }}"
          region: "{{ region }}"
          source_snapshot_id: "{{ item }}"
          encrypted: yes
          kms_key_id: "{{ kms_arn }}"
        loop: "{{ snapshot_ids }}"
        register: new_snapshots

然后这里是捕获

      - set_fact:
          new_snapshot_ids: "{{ new_snapshots| json_query('snapshots[].snapshot_id') }}"

      - name: creating the block_device_mappings structure (still a list of objects)
        ansible.builtin.debug:
          msg: '{
                  "device_name": "{{ item.2 }}",
                  "delete_on_termination": "true",
                  "snapshot_id": "{{ item.0 }}",
                  "volume_size": {{ item.1 }},
                  "volume_type": "gp2"
                }'
        loop: "{{ new_snapshot_ids|zip_longest(volume_size,device_name)|list }}"
        register: block_device_mappings  

      - set_fact:
          bdm: "{{ block_device_mappings | json_query('results[].msg') }}"

终于

      - name: create new ami from newly created snapshots
        amazon.aws.ec2_ami:
          region: "{{ region }}"
          name: "{{ instance.name }}-{{ ansible_date_time.date }}"
          state: present
          architecture: x86_64
          virtualization_type: hvm
          root_device_name: /dev/sda1
          device_mapping: "{{ bdm }}"

这是您无需任何额外技巧即可完成的方法。 当然,这不适合我的特定用例,但您可以将其调整到任何情况,不需要完全拆卸和重新组装 Json 本身