Как в Ansible сравнить 2 json по ключу?

Пишу роль, которая монтирует общие папки для пользователя, если он входит в ту или иную группу. Всё получается за исключением сравнения двух объектов по ключу. После всех операций у меня есть вот что:

Список того, что в принципе можно смонтировать (но если прав нет, то конечно же внутрь не пустит):

"united_info_about_shares_fact": [
    {
        "PathToShare": "//depit.my.domain/depit",
        "ShareAccessGroups": "it_group",
        "ShareName": "Общая папка управления IT"
    },
    {
        "PathToShare": "//share.edu.my.domain/share",
        "ShareAccessGroups": "edu_ecc_share",
        "ShareName": "Общая папка для студентов и преподавателей"
    },
    {
        "PathToShare": "//storage.my.domain/kaf_raket-dvig",
        "ShareAccessGroups": "kaf_raket-dvig",
        "ShareName": "Общая папка кафедры ракетных двигателей"
    },
    {
        "PathToShare": "//storage.my.domain/dek_fisis",
        "ShareAccessGroups": "dek_fisis_share",
        "ShareName": "Общая папка деканата ФИСИС"
    },
    {
        "PathToShare": "//storage.my.domain/kaf_fiziki-tverd-tela",
        "ShareAccessGroups": "kaf_fiziki-tverd-tela_share",
        "ShareName": "Общая папка кафедры физики твёрдого тела"
    }
]

Список того в каких группах пользователь состоит:

"user_groups_list_fact": [
    {
      "ShareAccessGroups": "it_group"
    },
    {
      "ShareAccessGroups": "vpn_users"
    },
    {
      "ShareAccessGroups": "wifi_users"
    },
    {
      "ShareAccessGroups": "edu_ecc_share"
    },
    {
      "ShareAccessGroups": "site_users"
    }
]

Т.е. я хочу, чтобы мне выдало:

    {
        "PathToShare": "//depit.my.domain/depit",
        "ShareAccessGroups": "it_group",
        "ShareName": "Общая папка управления IT"
    },
    {
        "PathToShare": "//share.edu.my.domain/share",
        "ShareAccessGroups": "edu_ecc_share",
        "ShareName": "Общая папка для студентов и преподавателей"
    },

Гугление привело меня на https://stackoverflow.com/a/58487241 и https://stackoverflow.com/a/56396809, но адаптировать под себя я не смог. Получилось как-то так, но код не работает:

- debug:
    msg: "{{ item.PathToShare }}"
  loop: "{{ united_info_about_shares_fact }}"
  when: "united_info_about_shares_fact | intersect(user_groups_list_fact | json_query('[*].ShareAccessGroups')) | length == 0"

UPD. Наковырял ещё, но смог добиться получения только одного элемента:

- set_fact:
    matching_by_groups: "{{ united_info_about_shares_fact | json_query('[*].ShareAccessGroups') | intersect(user_groups_list_fact | json_query('[*].ShareAccessGroups')) }}"
  
- set_fact:
    needed_shares: "{{ united_info_about_shares_fact | json_query(query_for_get_needed_shares) }}"
  loop: "{{ united_info_about_shares_fact }}"
  with_items: "{{ united_info_about_shares_fact }}"
  vars:
    query_for_get_needed_shares: "[?ShareAccessGroups == `{{ matching_by_groups[0] }}`]"

Ответы (1 шт):

Автор решения: don Rumata

Всё оказалось просто как и думал изначально, но всё равно со своими заморочками. Итак:

Надо перестать думать, что если мне нужно это и это и это значения - значит нужно искать в гугле слово and. Нужно искать or.

Получается вот так:

- set_fact:
    needed_shares: "{{ united_info_about_shares_fact | json_query(query_for_get_needed_shares) }}"
  vars:
    query_for_get_needed_shares: "[? ShareAccessGroups == 'it_group' || ShareAccessGroups == 'vpn_users' || ShareAccessGroups == 'edu_ecc_share']"

Т.к. к группе vpn_users никаких общих папок не привязано, то мне выдаётся ровно то, что я хотел получить (см. вопрос).

Но возникла проблема: как переделать и передать этот список, чтобы по дороге ничего не поломалось? Преобразовать {"ShareAccessGroups": "it_group"}, в ShareAccessGroups == 'it_group', запихнуть в set_facts и передать - не получается, хотя везде всё вроде ок. Т.е. конструкция

query_for_get_needed_shares: "[? `{{ user_groups_list_for_json_query_fact }}` ]"

не сработала. Зато сработало формирование файла и include_vars. Получилось монструозно, зато работает и идемпотентность:

Преобразовываем json из вопроса в строку, которую сможет прожевать jmespath:

- set_fact:
    user_groups_list_for_json_query_fact:
      "{{ user_groups_list_fact | join(' || ')
      | replace(var_vanilla_json_to_jmespath_query_string_part_01, ' == ')
      | replace(var_vanilla_json_to_jmespath_query_string_part_02, '')
      | replace(var_vanilla_json_to_jmespath_query_string_part_03, '')
      | regex_replace('^', var_vanilla_json_to_jmespath_query_string_start)
      | regex_replace('$', var_vanilla_json_to_jmespath_query_string_end)
      | string }}"
  vars:
    var_vanilla_json_to_jmespath_query_string_part_01: "': "
    var_vanilla_json_to_jmespath_query_string_part_02: "{'"
    var_vanilla_json_to_jmespath_query_string_part_03: "}"
    var_vanilla_json_to_jmespath_query_string_start: '"[? '
    var_vanilla_json_to_jmespath_query_string_end: ' ]"'

Пишем в файл:

- copy:
    content: |
      ---
      query_for_get_needed_shares: {{ user_groups_list_for_json_query_fact }}
    dest: '{{ main_user_temp_directory }}/vars_query_for_get_needed_shares.yml'

Инклудим и получаем новый факт:

- name: Include vars with all joined groups for "json_query"
  include_vars: '{{ main_user_temp_directory }}/vars_query_for_get_needed_shares.yml'

- set_fact:
    needed_shares: "{{ united_info_about_shares_fact | json_query(query_for_get_needed_shares) }}"

P.S. В принципе, если в дальнейшем понадобится дёргать список групп, в которых на данный момент состоит пользователь, то формирование файла vars_query_for_get_needed_shares.yml лучше вытащить в отдельную таску\плейбук\роль и сделать более общее название. Тогда можно будет инклудить из других мест экономя, на времени преобразования (я использовал выхлоп /usr/bin/id).

→ Ссылка