Usage of Ansible JMESPath in queries - ansible

I am using following JSON file:
sample.json:
{
"lldp_output['gathered']": [
{
"mode": "trunk",
"name": "GigabitEthernet0/0",
"trunk": {
"encapsulation": "dot1q"
}
},
{
"access": {
"vlan": 12
},
"mode": "access",
"name": "GigabitEthernet0/1"
},
{
"name": "GigabitEthernet0/2"
}
]
}
And the playbook:
---
- hosts: localhost
gather_facts: no
vars:
tmpdata: "{{ lookup('file','sample.json') | from_json }}"
tasks:
- name: Take 4
debug:
msg: "{{ tmpdata | community.general.json_query(lldp_output['gathered']) }}"
I get the following error:
TASK [Take 4] ********************************************************************************************
task path: /root/scripts/atest.yml:18
fatal: [localhost]: FAILED! => {
"msg": "Error in jmespath.search in json_query filter plugin:\n'lldp_output' is undefined"
}
How do I query the JSON shown so I get a list of all ports that have mode: trunk
When I run in a playbook:
---
- name: Find trunk ports
hosts: ios
tasks:
- name: Collect interface output
cisco.ios.ios_l2_interfaces:
config:
state: gathered
register:
"intf_output"
- debug:
var=intf_output
- name: Take 4
debug:
msg: "{{ intf_output | json_query(query) }}"
vars:
query: >-
"lldp_output['gathered']"[?mode=='trunk']
The structure returned is like following:
{
"intf_output": {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"failed": false,
"gathered": [
{
"name": "GigabitEthernet0/0"
},
{
"mode": "trunk",
"name": "GigabitEthernet0/1",
"trunk": {
"allowed_vlans": [
"10",
"20",
"30",
"100"
],
"encapsulation": "dot1q"
}
},
{
"mode": "trunk",
"name": "GigabitEthernet0/2",
"trunk": {
"allowed_vlans": [
"10",
"20",
"30",
"100"
],
"encapsulation": "dot1q"
}
},
{
"mode": "trunk",
"name": "GigabitEthernet0/3",
"trunk": {
"allowed_vlans": [
"10",
"20",
"30",
"100"
],
"encapsulation": "dot1q"
}
},
{
"mode": "trunk",
"name": "GigabitEthernet1/0",
"trunk": {
"allowed_vlans": [
"10",
"20",
"30",
"100"
],
"encapsulation": "dot1q"
}
},
{
"mode": "trunk",
"name": "GigabitEthernet1/1",
"trunk": {
"allowed_vlans": [
"10",
"20",
"30",
"100"
],
"encapsulation": "dot1q"
}
},
{
"name": "GigabitEthernet1/2"
},
{
"name": "GigabitEthernet1/3"
},
{
"name": "GigabitEthernet2/0"
},
{
"name": "GigabitEthernet2/1"
},
{
"name": "GigabitEthernet2/2"
},
{
"name": "GigabitEthernet2/3"
},
{
"name": "GigabitEthernet3/0"
},
{
"name": "GigabitEthernet3/1"
},
{
"name": "GigabitEthernet3/2"
},
{
"name": "GigabitEthernet3/3"
}
]
}
}
For each host I run against the playbook against.

The argument to json_query must be a string. Because you haven't quoted your argument, Ansible is looking for a variable named lldp_output. But you've got additonal problems, since you're trying to access a key named lldp_output['gathered'], but [ is a syntactically significant character in JSON (and JMESPath queries), so that needs to be escaped as well.
In order to avoid all sorts of quote escaping contortions, we can place the query itself into a variable, so that we have:
- hosts: localhost
vars:
tmpdata: "{{ lookup('file','sample.json') | from_json }}"
tasks:
- name: Take 4
debug:
msg: "{{ tmpdata | json_query(query) }}"
vars:
query: >-
"lldp_output['gathered']"
Note that we are using the >- block quote operator, which means that the value of query is the literal string "lldp_output['gathered']", including the outer quotes.
That playbook outputs:
TASK [Take 4] *********************************************************************************
ok: [localhost] => {
"msg": [
{
"mode": "trunk",
"name": "GigabitEthernet0/0",
"trunk": {
"encapsulation": "dot1q"
}
},
{
"access": {
"vlan": 12
},
"mode": "access",
"name": "GigabitEthernet0/1"
},
{
"name": "GigabitEthernet0/2"
}
]
}
To get just those systems with mode equal to trunk, just add that
criteria to your query:
- hosts: localhost
vars:
tmpdata: "{{ lookup('file','sample.json') | from_json }}"
tasks:
- name: Take 4
debug:
msg: "{{ tmpdata | json_query(query) }}"
vars:
query: >-
"lldp_output['gathered']"[?mode=='trunk']
This will output:
TASK [Take 4] *********************************************************************************
ok: [localhost] => {
"msg": [
{
"mode": "trunk",
"name": "GigabitEthernet0/0",
"trunk": {
"encapsulation": "dot1q"
}
}
]
}
Update
Given the data you've shown in your updated question, things are much
simpler, because you don't have the weird quoting you had in the
original question. With intf_output defined as shown, you can
write:
tasks:
- name: Take 4
debug:
msg: "{{ intf_output | json_query(query) }}"
vars:
query: >-
gathered[?mode=='trunk']

Related

How to Read and Output json Response using Ansible

How to read/display the following response (e.g: name, srcinft, dstinf) from FortiGate Firewall using Ansible. Or is there any way I can read this JSON output from file and display the fields i want.
{
"changed": false,
"meta": {
"http_method": "GET",
"size": 2,
"matched_count": 2,
"next_idx": 1,
"revision": "ac9c4e1d722b74695dee4fb3ce4fcd12",
"results": [
{
"policyid": 1,
"q_origin_key": 1,
"status": "enable",
"name": "test-policy01",
"uuid": "c4de3298-97ce-51ed-ccba-cafc556ba9e0",
"uuid-idx": 14729,
"srcintf": [
{
"name": "port2",
"q_origin_key": "port2"
}
],
"dstintf": [
{
"name": "port1",
"q_origin_key": "port1"
}
],
"action": "accept",
"ztna-status": "disable",
"srcaddr": [
{
"name": "all",
"q_origin_key": "all"
}
],
"dstaddr": [
{
"name": "all",
"q_origin_key": "all"
}
],
"policy-expiry": "disable",
"policy-expiry-date": "0000-00-00 00:00:00",
"service": [
{
"name": "ALL",
"q_origin_key": "ALL"
}
],
"tos": "0x00",
"sgt-check": "disable",
"sgt": []
},
{
"policyid": 2,
"q_origin_key": 2,
"status": "enable",
"name": "test-policy-02",
"uuid": "534b6c9c-97d1-51ed-7aa8-7544c628c7ea",
"uuid-idx": 14730,
"srcintf": [
{
"name": "port1",
"q_origin_key": "port1"
}
],
"dstintf": [
{
"name": "port2",
"q_origin_key": "port2"
}
],
"action": "accept",
"nat64": "disable",
"nat46": "disable",
"ztna-status": "disable",
"srcaddr": [
{
"name": "all",
"q_origin_key": "all"
}
],
"dstaddr": [
{
"name": "login.microsoft.com",
"q_origin_key": "login.microsoft.com"
}
],
"srcaddr6": [],
"reputation-direction6": "destination",
"policy-expiry-date": "0000-00-00 00:00:00",
"service": [
{
"name": "ALL_ICMP6",
"q_origin_key": "ALL_ICMP6"
}
],
"tos": "0x00",
"webcache": "disable",
"webcache-https": "disable",
"sgt-check": "disable",
"sgt": []
}
],
"vdom": "root",
"path": "firewall",
"name": "policy",
"version": "v7.2.3",
"build": 1262
},
"invocation": {
"module_args": {
"vdom": "root",
"access_token": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"selector": "firewall_policy",
"selectors": null
}
},
"_ansible_no_log": false
}
Expected result:
result:
test-policy-02:
dstintf:
- name: port2
q_origin_key: port2
srcintf:
- name: port1
q_origin_key: port1
test-policy01:
dstintf:
- name: port1
q_origin_key: port1
srcintf:
- name: port2
q_origin_key: port2
Unfortunately there is absolute no description or any further information.
However, regarding
How to read/display the following response (e.g: name, srcinft, dstinf) from FortiGate Firewall using Ansible.
you may have a look into the following simple and lazy approach with loop
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Include vars of stuff.yaml into the 'stuff' variable (2.2).
ansible.builtin.include_vars:
file: stuff.json
name: stuff
- name: Show list of dict
debug:
msg: "{{ stuff.meta.results }}"
- name: Print key:value
debug:
msg:
- "name: {{ item.name }}"
- "{{ item.srcintf }}"
- "{{ item.dstintf }}"
loop_control:
label: "policyid: {{ item.policyid }}"
loop: "{{ stuff.meta.results }}"
resulting into an output of
TASK [Print key:value] *****************
ok: [localhost] => (item=policyid: 1) =>
msg:
- 'name: test-policy01'
- - name: port2
q_origin_key: port2
- - name: port1
q_origin_key: port1
ok: [localhost] => (item=policyid: 2) =>
msg:
- 'name: test-policy-02'
- - name: port1
q_origin_key: port1
- - name: port2
q_origin_key: port2
Further Documentation
include_vars module – Load variables from files, dynamically within a task
Loops
Or is there any way I can read this JSON output from file and display the fields I want?
To get familiar with data structure, respective JSON response you've provided in your example, you could start with something like a JSONPathFinder. It will result into an path of
x.meta.results[0].name
x.meta.results[0].srcintf
x.meta.results[0].dstintf
x.meta.results[1].name
x.meta.results[1].srcintf
x.meta.results[1].dstintf
for the provided keys.
It is also possible to use jq on CLI
jq keys stuff.json
[
"_ansible_no_log",
"changed",
"invocation",
"meta"
]
and just proceed further with
jq '.meta.results' stuff.json
jq '.meta.results[0]' stuff.json
jq '.meta.results[1]' stuff.json
Further Q&A
How to get key names from JSON using jq?

Ansible - Extracting a variable from a large JSON output

The gcp_sql_instance_info module does not seem to allow you to specify the name of the database you are trying to get info for. All it does is every detail of every database in that project.
What would be the best way to extract the "ipAddress" of a database based on it's name and register that value to a variable?
{
"resources": [
{
"kind": "sql#instance",
"state": "RUNNABLE",
"databaseVersion": "POSTGRES_13",
"settings": {
"authorizedGaeApplications": [],
"tier": "db-custom-1-3840",
"kind": "sql#settings",
"availabilityType": "ZONAL",
"pricingPlan": "PER_USE",
"replicationType": "SYNCHRONOUS",
"activationPolicy": "ALWAYS",
"ipConfiguration": {
"privateNetwork": "example-network",
"authorizedNetworks": [],
"ipv4Enabled": false
},
"locationPreference": {
"zone": "us-west1-b",
"kind": "sql#locationPreference"
},
"dataDiskType": "PD_SSD",
"maintenanceWindow": {
"kind": "sql#maintenanceWindow",
"hour": 0,
"day": 0
},
"backupConfiguration": {
"startTime": "02:00",
"kind": "sql#backupConfiguration",
"backupRetentionSettings": {
"retentionUnit": "COUNT",
"retainedBackups": 7
},
"enabled": false,
"replicationLogArchivingEnabled": false,
"pointInTimeRecoveryEnabled": false,
"transactionLogRetentionDays": 7
},
"settingsVersion": "3",
"storageAutoResizeLimit": "0",
"storageAutoResize": true,
"dataDiskSizeGb": "100"
},
"ipAddresses": [
{
"type": "PRIVATE",
"ipAddress": "10.10.10.1"
}
],
"instanceType": "CLOUD_SQL_INSTANCE",
"backendType": "SECOND_GEN",
"name": "database-1",
"region": "us-west1",
"gceZone": "us-west1-b",
"databaseInstalledVersion": "POSTGRES_13_7",
"createTime": "2022-07-22T20:20:32.274Z"
},
{
"kind": "sql#instance",
"state": "RUNNABLE",
"databaseVersion": "MYSQL_8_0",
"settings": {
"authorizedGaeApplications": [],
"tier": "db-n1-standard-1",
"kind": "sql#settings",
"availabilityType": "ZONAL",
"pricingPlan": "PER_USE",
"replicationType": "SYNCHRONOUS",
"activationPolicy": "ALWAYS",
"ipConfiguration": {
"privateNetwork": "example-network",
"authorizedNetworks": [],
"ipv4Enabled": false
},
"locationPreference": {
"zone": "us-west1-c",
"kind": "sql#locationPreference"
},
"dataDiskType": "PD_SSD",
"backupConfiguration": {
"startTime": "21:00",
"kind": "sql#backupConfiguration",
"backupRetentionSettings": {
"retentionUnit": "COUNT",
"retainedBackups": 7
},
"enabled": false,
"transactionLogRetentionDays": 7
},
"settingsVersion": "1",
"storageAutoResizeLimit": "0",
"storageAutoResize": true,
"dataDiskSizeGb": "10"
},
"ipAddresses": [
{
"type": "PRIVATE",
"ipAddress": "10.10.10.2"
}
],
"instanceType": "CLOUD_SQL_INSTANCE",
"backendType": "SECOND_GEN",
"name": "database-2",
"region": "us-west1",
"gceZone": "us-west1-c",
"databaseInstalledVersion": "MYSQL_8_0_26",
"createTime": "2022-07-27T19:27:59.235Z"
}
]
Q: "Extract the ipAddress of a database based on its name."
A: Crete a dictionary. For example, select the first item from the list ipAddresses
name_ip: "{{ dict(resources|json_query(name_ip_query)) }}"
name_ip_query: '[].[name, ipAddresses[0].ipAddress]'
give
name_ip:
database-1: 10.10.10.1
database-2: 10.10.10.2
Notes
Example of a complete playbook for testing
- hosts: localhost
vars_files:
- resources.yml
vars:
name_ip: "{{ dict(resources|json_query(name_ip_query)) }}"
name_ip_query: '[].[name, ipAddresses[0].ipAddress]'
name_ver: "{{ dict(resources|json_query(name_ver_query)) }}"
name_ver_query: '[].[name, databaseVersion]'
ip_of_db1: "{{ name_ip['database-1'] }}"
tasks:
- debug:
var: name_ip
- debug:
var: name_ver
- debug:
var: ip_of_db1
gives
TASK [debug] *********************************************************************************
ok: [localhost] =>
name_ip:
database-1: 10.10.10.1
database-2: 10.10.10.2
TASK [debug] *********************************************************************************
ok: [localhost] =>
name_ver:
database-1: POSTGRES_13
database-2: MYSQL_8_0
TASK [debug] *********************************************************************************
ok: [localhost] =>
ip_of_db1: 10.10.10.1
There might be more IP addresses. The variable ipAddresses is a list. An option would be to create a list of all IP addresses. For example,
name_ips: "{{ dict(resources|json_query(name_ips_query)) }}"
name_ips_query: '[].[name, ipAddresses[].ipAddress]'
give
name_ips:
database-1: [10.10.10.1]
database-2: [10.10.10.2]
This is a working playbook to achieve the desired output:
- hosts: localhost
vars:
db_instance_name: instance-1
tasks:
- name: get info on an instance
gcp_sql_instance_info:
project: project_id
auth_kind: serviceaccount
service_account_file: "/path/to/sa/example.json"
register: cloud_sql_resources
- set_fact:
query: "resources[?contains(name,'{{ db_instance_name }}')].ipAddresses[].ipAddress"
- set_fact:
ipAddress: "{{ cloud_sql_resources | to_json | from_json | json_query(query) }}"
- debug:
msg: "{{ ipAddress }}"
You can run your playbook like this: ansible-playbook -v main.yaml --extra-vars "db_instance_name=instance-1", that would print:
TASK [debug] ********************************************************************************************************************
ok: [localhost] => {
"msg": [
"35.224.46.127"
]
}

How to remove null and replace with a string value in ansible

How to remove null and replace with a string novlaue?
I have below output stored in variable out1
{
"out1": [
[
{
"destination": "dest-a",
"interface": "e1/1",
"metric": "10",
"name": "A"
},
{
"destination": "dest-b",
"interface": "e1/2",
"metric": "10",
"name": "B"
},
{
"destination": "dest-c",
"interface": null,
"metric": "10",
"name": "C"
},
{
"destination": "dest-d",
"interface": null,
"metric": "10",
"name": "B"
}
]
]
}
I have a json_query in my code:
- debug: msg="{{out1 |json_query(myquery)}}"
vars:
myquery: "[].{dest: destination ,int: interface}"
register: out2
Above code will print the following:
{
"msg": [
{
"dest": "dest-a",
"int": "e1/1"
},
{
"dest": "dest-b",
"int": "e/12"
},
{
"dest": "dest-c",
"int": null
},
{
"dest": "dest-d",
"int": null
}
]
}
I want to replace or remove null with the string novalue.
I looked into some posts and found default("novalue") can do the trick but in my case it is not working. I tried following added default("novalue") to my debug task, but I am getting an error.
I am sure that the error resides in myquery, the way I interpret/understand default() might be wrong and might be used wrongly.
Can anyone help me here please?
- debug: msg="{{out1 |json_query(myquery)}}"
vars:
myquery: "[].{dest: destination ,int: interface|default("novalue")}"
register: out2
Otherwise, with another JMESPath expression to achieve this, you can use an or expression ||, that will display the value of interface or a string that you are free to define.
So, given your JSON:
[
{
"destination": "dest-a",
"interface": "e1/1",
"metric": "10",
"name": "A"
},
{
"destination": "dest-b",
"interface": "e1/2",
"metric": "10",
"name": "B"
},
{
"destination": "dest-c",
"interface": null,
"metric": "10",
"name": "C"
},
{
"destination": "dest-d",
"interface": null,
"metric": "10",
"name": "B"
}
]
And the JMESPath query
[].{dest: destination ,int: interface || 'novalue'}
This yields
[
{
"dest": "dest-a",
"int": "e1/1"
},
{
"dest": "dest-b",
"int": "e1/2"
},
{
"dest": "dest-c",
"int": "novalue"
},
{
"dest": "dest-d",
"int": "novalue"
}
]
And your task ends up being:
- debug:
msg: "{{ out1 | json_query(_query) }}"
vars:
_query: "[].{dest: destination ,int: interface || 'novalue')}"
register: out2
You are using the jinja2 default filter inside a jmespath (i.e. json_query) expression. This can't work.
You can use the jmespath function not_null in this case
The playbook:
---
- hosts: localhost
gather_facts: false
vars:
"out1": [
[
{
"destination": "dest-a",
"interface": "e1/1",
"metric": "10",
"name": "A"
},
{
"destination": "dest-b",
"interface": "e1/2",
"metric": "10",
"name": "B"
},
{
"destination": "dest-c",
"interface": null,
"metric": "10",
"name": "C",
},
{
"destination": "dest-d",
"interface": null,
"metric": "10",
"name": "B"
}
]
]
tasks:
- debug:
msg: "{{ out1 | json_query(myquery) }}"
vars:
myquery: >-
[].{dest: destination ,int: not_null(interface, 'no value')}
Gives:
PLAY [localhost] **************************************************************************************************************************************************************************************************************
TASK [debug] ******************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
{
"dest": "dest-a",
"int": "e1/1"
},
{
"dest": "dest-b",
"int": "e1/2"
},
{
"dest": "dest-c",
"int": "no value"
},
{
"dest": "dest-d",
"int": "no value"
}
]
}
PLAY RECAP ********************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Q: "How to remove 'null' and replace it with a string 'no value'?"
A: The variable out1 is a list of lists. Let's iterate it and create out2 with null replaced by 'no value' string. In each loop create the list of interface attributes with null replaced. Combine this list with the item and add it to the new list, e.g.
- set_fact:
out2: "{{ out2|d([]) + [item|zip(_item)|map('combine')] }}"
loop: "{{ out1 }}"
vars:
_item: "{{ item|json_query(_query) }}"
_query: |
[].{interface: not_null(interface, 'no value')}
gives
out2:
- - destination: dest-a
interface: e1/1
metric: '10'
name: A
- destination: dest-b
interface: e1/2
metric: '10'
name: B
- destination: dest-c
interface: no value
metric: '10'
name: C
- destination: dest-d
interface: no value
metric: '10'
name: B

Ansible search and query

Updated with suggestions from larsks.
With the following structure
"intf_output_ios": {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"failed": false,
"gathered": [
{
"name": "GigabitEthernet0/0"
},
{
"mode": "trunk",
"name": "GigabitEthernet0/1",
"trunk": {
"allowed_vlans": [
"10",
"20",
"30",
"99",
"100"
],
"encapsulation": "dot1q"
}
},
{
"mode": "trunk",
"name": "GigabitEthernet0/2",
"trunk": {
"allowed_vlans": [
"10",
"20",
"30",
"99",
"100"
],
"encapsulation": "dot1q"
}
},
{
"access": {
"vlan": 30
},
"mode": "access",
"name": "GigabitEthernet0/3"
},
{
"name": "GigabitEthernet1/0"
},
{
"name": "GigabitEthernet1/1"
},
{
"name": "GigabitEthernet1/2"
},
{
"name": "GigabitEthernet1/3"
},
{
"name": "GigabitEthernet2/0"
},
{
"name": "GigabitEthernet2/1"
},
{
"name": "GigabitEthernet2/2"
},
{
"name": "GigabitEthernet2/3"
},
{
"name": "GigabitEthernet3/0"
},
{
"name": "GigabitEthernet3/1"
},
{
"name": "GigabitEthernet3/2"
},
{
"access": {
"vlan": 99
},
"mode": "access",
"name": "GigabitEthernet3/3"
}
]
}
To print only the ports in VLAN 30 use the following?
- name: "P901T6: Set fact to include only access ports - IOS"
set_fact:
access_ports_ios_2: "{{ intf_output_ios | json_query(query) }}"
vars:
query: >-
gathered[?access.vlan==`30`]
- name: "P901T7: Dump list of access ports - IOS"
debug:
var=access_ports_ios_2
NOTE: It is important to use 30 (with backticks) and not '30'
I have gone through https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#managing-list-variables without really understanding how to fix this. If someone has some good link that would be very useful
With a structure like
ok: [access01] => {
"access_ports_ios": [
{
"access": {
"vlan": 30
},
"mode": "access",
"name": "GigabitEthernet0/3"
},
{
"access": {
"vlan": 99
},
"mode": "access",
"name": "GigabitEthernet3/3"
}
]
}
To get ports in vlan 30 use:
- debug:
var: access_ports_ios|json_query(query)
vars:
query: >-
[?access.vlan==`30`]
Note:
If you want to use a variable for vlan instead of hard-coding it. I had to do as follows:
- name: Debug 4
debug:
var: access_ports_ios|json_query('[?access.vlan==`{{ src_vlan | int}}`]')
You're asking for gathered.access, but gathered is a list and does not have an access attribute. You want "all items from gathered for which access.vlan is 30 (and note that the value of access.vlan is an integer, not a string):
- debug:
var: intf_output_ios|json_query(query)
vars:
query: >-
gathered[?access.vlan==`30`]
Which given you example input produces:
TASK [debug] *******************************************************************
ok: [localhost] => {
"intf_output_ios|json_query(query)": [
{
"access": {
"vlan": 30
},
"mode": "access",
"name": "GigabitEthernet0/3"
}
]
}
I'm going to reiterate advice I often give for json_query questions: use something like jpterm or the JMESPath website to test JMESPath expressions against your actual data. This makes it much easier to figure out where an expression might be going wrong.

Q: Ansible - How can I merge 2 lists of hashes with a common key/value pair

How can I merge 2 lists of hashes based on a key/value pair, using Ansible 2.4.4
"foo": [
{
"hostname": "web1.example.com",
"guid": "73e85eb2-2ad5-4699-8a09-adf658a11ff2"
},
{
"hostname": "web2.example.com",
"guid": "827025fe-f13c-4fc8-ba51-7ff582596bbd"
},
{
"hostname": "web3.example.com",
"guid": "bba27304-c1bb-4889-aa44-125626be8488"
}
]
"bar": [
{
"ipaddress": "1.1.1.1",
"guid": "73e85eb2-2ad5-4699-8a09-adf658a11ff2"
},
{
"ipaddress": "2.2.2.2",
"guid": "827025fe-f13c-4fc8-ba51-7ff582596bbd"
},
{
"ipaddress": "3.3.3.3",
"guid": "bba27304-c1bb-4889-aa44-125626be8488"
}
]
I want something like:
"foobar" : [
{
"hostname": "web1.example.com",
"guid": "73e85eb2-2ad5-4699-8a09-adf658a11ff2",
"ipaddress": "1.1.1.1"
},
{
"hostname": "web2.example.com",
"guid": "827025fe-f13c-4fc8-ba51-7ff582596bbd",
"ipaddress": "2.2.2.2"
},
{
"hostname": "web3.example.com",
"guid": "bba27304-c1bb-4889-aa44-125626be8488",
"ipaddress": "3.3.3.3"
}
]
I've looked into several Ansible / Jinja2 filters including combine, union, map, custom plugins, but not having much success. I need to be able to match on the guid.
not sure if there is a smarter way, but to achieve what you need you can use the nested query plugin to loop over the combinations of the elements from the 2 list variables, find the combinations that have the common field equal, and then construct a new dictionary element and append it to the "final" list variable.
playbook:
- hosts: localhost
gather_facts: false
vars:
foo:
- hostname: web1.example.com
guid: 73e85eb2-2ad5-4699-8a09-adf658a11ff2
- hostname: web2.example.com
guid: 827025fe-f13c-4fc8-ba51-7ff582596bbd
- hostname: web3.example.com
guid: bba27304-c1bb-4889-aa44-125626be8488
bar:
- ipaddress: 1.1.1.1
guid: 73e85eb2-2ad5-4699-8a09-adf658a11ff2
- ipaddress: 2.2.2.2
guid: 827025fe-f13c-4fc8-ba51-7ff582596bbd
- ipaddress: 3.3.3.3
guid: bba27304-c1bb-4889-aa44-125626be8488
tasks:
- name: merge lists
set_fact:
merged_list: "{{ merged_list|default([]) + [{ 'hostname': item[0].hostname, 'guid': item[0].guid, 'ipaddress': item[1].ipaddress }] }}"
when: "item[0].guid == item[1].guid"
loop: "{{ query('nested', foo, bar) }}"
- name: print results
debug:
var: merged_list
result:
TASK [print results] ************************************************************************************************************************************************************************************************
ok: [localhost] => {
"merged_list": [
{
"guid": "73e85eb2-2ad5-4699-8a09-adf658a11ff2",
"hostname": "web1.example.com",
"ipaddress": "1.1.1.1"
},
{
"guid": "827025fe-f13c-4fc8-ba51-7ff582596bbd",
"hostname": "web2.example.com",
"ipaddress": "2.2.2.2"
},
{
"guid": "bba27304-c1bb-4889-aa44-125626be8488",
"hostname": "web3.example.com",
"ipaddress": "3.3.3.3"
}
]
}

Resources