How to Read and Output json Response using Ansible - 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?

Related

Ansible Filtering with Values within Dictionary of the List (FortiOS Facts)

I am tyring to use filter with Values within Dictionary of the List (FortiOS Facts). Sample Resp onse from FortiGate (Facts)
{
"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"
}
],
Ansible Filter works fine for the following code
- name: To Fetch Existing Firewall Polices Based on Selector firewall_policy
fortios_configuration_fact:
vdom: "{{ vdom }}"
access_token: "{{ fortigate_access_token }}"
selector: "firewall_policy"
filters:
- action=="accept"
register: existing_fw_policy_object
when: existing_fw_name is defined
- name: Display Existing Firewall Policy
debug:
msg: "{{ existing_fw_policy_object }}"
What is the way to filter on srcintf. Wanted to Filter Firewall Policy with port2.

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"
]
}

Looping through Ansible Register output and printing the result as list

This is the sample task which give me the output which contains a list of self IPs. I want to capture this in Result and just print only the name of self IP.
I was able to print if I give a list index, but I can't loop through it to capture it as a separate list to use it in a different task.
- name: Port Lock Down - Allow Default
bigip_device_info:
gather_subset:
- self-ips
provider: "{{provider}}"
register: Result
- name : Display the selfips
debug:
var: Result
TASK [Display the Selfip config] **************************************************************************************************************************************************************************
ok: [LD-F5-01] => {
"Result": {
"ansible_facts": {
"ansible_net_queried": true,
"ansible_net_self_ips": [
{
"address": "3.3.3.3",
"allow_access_list": [
"tcp:8080",
"udp:53",
"gre:0",
"default"
],
"floating": "no",
"full_path": "/Common/test-self-ip-1",
"name": "test-self-ip-1",
"netmask": "255.255.255.0",
"netmask_cidr": 24,
"traffic_group": "/Common/traffic-group-local-only",
"traffic_group_inherited": "no",
"vlan": "/Common/test-vlan-1"
},
{
"address": "4.4.4.4",
"allow_access_list": "all",
"floating": "no",
"full_path": "/Common/test-self-ip-2",
"name": "test-self-ip-2",
"netmask": "255.255.255.0",
"netmask_cidr": 24,
"traffic_group": "/Common/traffic-group-local-only",
"traffic_group_inherited": "no",
"vlan": "/Common/test-vlan-1"
}
]
},
"changed": false,
"failed": false,
"queried": true,
"self_ips": [
{
"address": "3.3.3.3",
"allow_access_list": [
"tcp:8080",
"udp:53",
"gre:0",
"default"
],
"floating": "no",
"full_path": "/Common/test-self-ip-1",
"name": "test-self-ip-1",
"netmask": "255.255.255.0",
"netmask_cidr": 24,
"traffic_group": "/Common/traffic-group-local-only",
"traffic_group_inherited": "no",
"vlan": "/Common/test-vlan-1"
},
{
"address": "4.4.4.4",
"allow_access_list": "all",
"floating": "no",
"full_path": "/Common/test-self-ip-2",
"name": "test-self-ip-2",
"netmask": "255.255.255.0",
"netmask_cidr": 24,
"traffic_group": "/Common/traffic-group-local-only",
"traffic_group_inherited": "no",
"vlan": "/Common/test-vlan-1"
}
]
}
}
- name : Display the selfips
debug:
var: Result.self_ips[0]['name']
TASK [Display the selfips] ********************************************************************************************************************************************************************************
ok: [LD-F5-01] => {
"Result.self_ips[0]['name']": "test-self-ip-1"
}
Can someone help me to use the for loop to get all the self IPs names as list instead of one single output?
You actually do not need a loop to achieve this, you can use the map filter of Jinja instead:
Result.self_ips | map(attribute='name')
Given the task:
- debug:
var: Result.self_ips | map(attribute='name')
vars:
Result:
self_ips:
- address: 3.3.3.3
allow_access_list:
- 'tcp:8080'
- 'udp:53'
- 'gre:0'
- default
floating: 'no'
full_path: /Common/test-self-ip-1
name: test-self-ip-1
- address: 4.4.4.4
allow_access_list: all
floating: 'no'
full_path: /Common/test-self-ip-2
name: test-self-ip-2
This yields the result:
Result.self_ips | map(attribute='name'):
- test-self-ip-1
- test-self-ip-2

Usage of Ansible JMESPath in queries

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']

How to access duplicate values in a JSON array and set each to a unique variable in ansible?

I am running an ansible playbook which outputs my information in JSON. This array has a loop that has multiple identical keys with different values. Please see below, I am interested in the "intf_id":
"stdout": [
{
"TABLE_cdp_neighbor_brief_info": {
"ROW_cdp_neighbor_brief_info": [
{
"capability": [
"switch",
"IGMP_cnd_filtering"
],
"device_id": "osw1J15",
"ifindex": "83886080",
"intf_id": "mgmt0",
"platform_id": "cisco WS-C2960X-48TS-LL",
"port_id": "GigabitEthernet0/45",
"ttl": "160"
},
{
"capability": [
"router",
"switch",
"Supports-STP-Dispute"
],
"device_id": "spine01",
"ifindex": "436232192",
"intf_id": "Ethernet1/49",
"platform_id": "N9K-C9508",
"port_id": "Ethernet1/11",
"ttl": "159"
},
{
"capability": [
"router",
"switch",
"Supports-STP-Dispute"
],
"device_id": "spine02",
"ifindex": "436232704",
"intf_id": "Ethernet1/50",
"platform_id": "N9K-C9508",
"port_id": "Ethernet1/11",
"ttl": "127"
},
{
"capability": [
"router",
"switch",
"IGMP_cnd_filtering",
"Supports-STP-Dispute"
],
"device_id": "leaf1J1402",
"ifindex": "436234240",
"intf_id": "Ethernet1/53",
"platform_id": "N9K-C93180YC-EX",
"port_id": "Ethernet1/53",
"ttl": "175"
},
{
"capability": [
"router",
"switch",
"IGMP_cnd_filtering",
"Supports-STP-Dispute"
],
"device_id": "leaf1J1402",
"ifindex": "436234752",
"intf_id": "Ethernet1/54",
"platform_id": "N9K-C93180YC-EX",
"port_id": "Ethernet1/54",
"ttl": "175"
}
]
},
"neigh_count": "5"
}
]
Currently I can access the first iteration of "intf_id" with :
- debug: msg="{{ list.stdout[0].TABLE_cdp_neighbor_brief_info.ROW_cdp_neighbor_brief_info[0].intf_id }}"
I feel like I'm close, but cannot figure out how to get each iteration of "intf_id". I can get subsequent ones by changing ROW_cdp_neighbor_brief_info[0] to [1] or [2], etc. I need to be able to access each key, without knowing how many keys there will be. Each value to the keys also need to be individually callable by subsequent tasks.
1) How do I get ansible to debug each iteration?
2) Depending on the answer to 1, how can I assign a particualr variable to each value? (ie. interface1, interface2, interface3...)
ansible-playbook json_query.yml
tasks:
- name:
debug:
msg: "{{ item }}"
loop: "{{ stdout | json_query('TABLE_cdp_neighbor_brief_info.ROW_cdp_neighbor_brief_info[*].intf_id') }}"
(output abridged)
"msg": "mgmt0"
"msg": "Ethernet1/49"
"msg": "Ethernet1/50"
"msg": "Ethernet1/53"
"msg": "Ethernet1/54"
You can use the filter json_query with jmespath syntax to loop over the various elements intf_id this way
---
- hosts: localhost
gather_facts: no
tasks:
- name: populate router data from json
set_fact:
data: "{{ lookup('file','so-router-info.json') }}"
- name: looping over interfaces
debug:
var: item
loop: "{{ data | json_query('TABLE_cdp_neighbor_brief_info.ROW_cdp_neighbor_brief_info[*].intf_id') }}"
it gives this output
…
TASK [looping over interfaces] ********************************************************************************************************
ok: [localhost] => (item=mgmt0) => {
"item": "mgmt0"
}
ok: [localhost] => (item=Ethernet1/49) => {
"item": "Ethernet1/49"
}
ok: [localhost] => (item=Ethernet1/50) => {
"item": "Ethernet1/50"
…
You can use the site http://jmespath.org/ to tests filters

Resources