在Ansible中修改JSON的最佳方法

9

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

{
  "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个回答

12

我不确定什么是最好的方法,但其中一个选择是编写一个简单的库模块来处理更新机制。您可以使用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,那么你可以在playbook中像这样使用它:

- 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

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

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

Ansible将在相对于playbook的library目录中查找。在这个例子中,将自定义模块放置在playbook/library/json_modify.py中。 - Sion
@larsks 我知道这个帖子很旧了,但是json_modify的代码似乎会改变生成的json,在结尾处添加一个额外的闭合括号。结果失败了。 - Juan Jimenez
@JuanJimenez 我认为不会:代码没有做任何可能导致添加不平衡括号的操作(它只是使用 'json' 和 'jsonpointer' 模块操纵数据,这两个模块都非常稳定)。如果您有失败示例的链接,请告诉我,我很乐意更新答案。 - larsks
谢谢,它运行得很好。很遗憾没有 Ansible 的方法来代替它。我尝试了 set_fact 和 combine,但没有成功。最好通过一个模块在纯 Python 中实现...但是很遗憾! - fabiog1901

2

0

这可能有点老了,但我认为它对许多人来说可能很有用,所以我将把我的解决方案发布在这里,作为我个人能找到的“修改JSON的最佳方式”。

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

所以我必须:

  • 获取旧快照的数据(例如大小、设备名称等)
  • 使用不同的密钥创建新快照
  • 重新注册具有正确块设备映射的新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') }}"

基本上,以上每个都是我需要的三件事(设备名称、快照 ID、卷大小)的列表之一(但可以扩展)。然后:
      - 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本身的情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接