json_query get value for a random key with regex

131 views Asked by At

I have a situation where I'm writing a playbook for changing attributes of virtual machine, and the input I get comes from an automation that outputs the following, which is then passed in extra_vars:

{
  "vmvars": {
    "v_ads1as_operating_system": "Linux",
    "v_mdjx2d_vm_name": "myvm123",
    "v_srsj4d_mount_point": "tmp"
  }
}

As you can see, the key in vmvars, the string v_<something> is not consistent and can be anything. The remaining string of the key for e.g. _operating_system remains the same.

I know the key should be unique. Well it is unique, sadly some of it is random.
Now in order to get the values I've tried the following, however none of them seem to work.

---
- hosts: localhost
  tasks:
    - name: Get os_type from input
      set_fact:
        os_type: "{{ vmvars[item] | json_query(['*operating_system']) }}"
      loop: "{{ vmvars.keys() | list }}"

More queries that I've tried.

- set_fact:
    os_type: "{{ vmvars[item] | json_query(vmvars['*operating_system']) }}"
- set_fact:
    os_type: "{{ vmvars[item] | json_query([?contains(item,'operating_system')]) }}"
1

There are 1 answers

0
β.εηοιτ.βε On BEST ANSWER

You will have two issues using JMESPath and json_query in this use case:

  1. JMESPath cannot do regex filtering. There is a proposal to introduce it, but, as you can see from the fact that the issue below is still open, it is not currently implemented in the query language.
    Reference: https://github.com/jmespath/jmespath.jep/issues/23
  2. You cannot really act upon dynamic keys in JMESPath, because: the way to get the keys — via the function keys() — will destroy the value and the way to get the value of dynamic keys — via an object projection .* — will destroy the key.

This said, you can use the filter dict2items in order to make, from a dictionary like

foo: bar
baz: qux

a normalised list:

- key: foo
  value: bar
- key: baz
  value: qux

The resulting list is easier to filter, since you can now apply filter on values and not on keys, which tend to be more complex.

So, with that list, you can use the filter selectattr to target the key containing _operating_system and get its value.

Given the task:

- set_fact:
    os_type: >-
      {{
        (
          vmvars | dict2items | selectattr(
            'key', 'contains', '_operating_system'
          )
        ).0.value
      }}
  vars:
    vmvars:
      v_ads1as_operating_system: Linux
      v_mdjx2d_vm_name: myvm123
      v_srsj4d_mount_point: tmp

Ansible would yield:

ok: [localhost] => changed=false 
  ansible_facts:
    os_type: Linux

Note: the above output was generated running the playbook with the option -v, which, amongst other useful information, shows the result of a set_fact task.


You could also use a regex, with the match filter provided by Ansible, as your first intend was:

- set_fact:
    os_type: >-
      {{
        (
          vmvars | dict2items | selectattr(
            'key', 'match', 'v_.*_operating_system'
          )
        ).0.value
      }}

Giving the same result as above.