JMESPath Query in Ansible

Viewed 199

--- EDIT ---

Partial-Solution: Messed around with the JMESPath syntax and was able to successfully get a match for the first test case (without the optional variable) using:

jmesquery: "{{ datacenter }}{{ subcategory }}.{{ refine_hosts }}.[*][].[*][][]"

I am writing an Ansible Playbook that takes a list of hosts from a network server, parses the JSON list, and finds hostnames that matches the user's input when they deploy the playbook as a Jenkin's Job through it's API.

The issue I am encountering is that I am unable to successfully query the JSON host list. Currently, I am only trying to run the following test case:

datacenter: a
subcategory: bc
refine_hosts: QA

However, the final version of this playbook should be able to take in values for datacenter, subcategory, and refine_hosts with an optional input value of host_type. An example test case including the optional input value would be the following:

datacenter: a
subcategory: bc
refine_hosts: QA
host_type: WEBSITE

In my playbook, I am using JMESPath within the following task:

- name: Build HOSTS list
  set_fact:
    hosts_list: "{{ jsondata | json_query(jmesquery) }}"
  vars:
    jmesquery: '%%datacenter%%-%%subcategory%%.%%refine_hosts%%.[*][*][][]'

The JSON host list is structured in the following manner (I am unable to edit the structure of the host list, but it will always follow the following structure nonetheless):

{
   "a-bc":{
      "all":{
         "webServer":[

         ],
         "archive":[
            "someHostAlias-123.privateDomain.com"
         ],
         "central":[
            "someHostAlias-456.privateDomain.com"
         ]
      },
      "QA":{
         "xyz":{
            "INBOUND_HTTP":[
               "someHostAlias-789.privateDomain.com"
            ],
            "WEBSITE":[
               "someHostAlias-1011.privateDomain.com"
            ]
         }
      }
   }
}

I have been using the following websites for this issue:

I apologize if the query seems obvious, this is my first attempt at an Ansible Playbook. All help/feedback is greatly appreciated.

1 Answers

One of the issue of your query is that you are confusing [*] — a list projection — that selects all the elements of a list with .* — an object projection — that selects all the properties of a dictionary.

So, one solution in JMESPath, would be to do:

jmesquery: >-
  "{{ datacenter }}-{{ subcategory }}".{{ refine_hosts }}.*.
  {{ host_type if host_type | default('') != '' else '*' }}[] | []

Given the playbook:

- hosts: localhost
  gather_facts: no

  tasks:
    - debug:
        msg: "{{ jsondata | json_query(jmesquery) }}"
      loop: "{{ fake_user_input }}"
      loop_control:
        label: "{{ jmesquery }}"
      vars:
        jmesquery: >-
          "{{ datacenter }}-{{ subcategory }}".{{ refine_hosts }}.*.
          {{ host_type if host_type | default('') != '' else '*' }}[] | []
        
        datacenter: "{{ item.datacenter }}"
        subcategory: "{{ item.subcategory }}"
        refine_hosts: "{{ item.refine_hosts }}"
        host_type: "{{ item.host_type | default('') }}"

        fake_user_input:
          - datacenter: a
            subcategory: bc
            refine_hosts: QA
            host_type: WEBSITE
          - datacenter: a
            subcategory: bc
            refine_hosts: QA

        jsondata:
          a-bc:
            all:
              webServer: []
              archive:
              - someHostAlias-123.privateDomain.com
              central:
              - someHostAlias-456.privateDomain.com
            QA:
              xyz:
                INBOUND_HTTP:
                - someHostAlias-789.privateDomain.com
                WEBSITE:
                - someHostAlias-1011.privateDomain.com

This yields:

ok: [localhost] => (item="a-bc".QA.*. WEBSITE[] | []) => 
  msg:
  - someHostAlias-1011.privateDomain.com
ok: [localhost] => (item="a-bc".QA.*. *[] | []) => 
  msg:
  - someHostAlias-789.privateDomain.com
  - someHostAlias-1011.privateDomain.com
Related