Search code examples
jsonjenkinsansibleyamljmespath

JMESPath Query in Ansible



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


Solution

  • 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