Loop with conditional in ansible - ansible

I have a keycloak realm template in ansible and want to load array of values from that template.
My template file looks something like this:
{
// Some json here
"roles": {
"client": {
"demo-app": [
{
"name": "Administrator",
"composite": true,
"composites": {
"client": {
"realm-management": [
"view-users",
"manage-identity-providers",
"query-clients",
"query-users",
]
}
},
"clientRole": true,
},
{
"name": "Developer",
"composite": false,
"clientRole": true,
},
{
"name": "Operator",
"composite": false,
"clientRole": true,
}
],
}
}
}
Now I want to read realm-management array values from the template. For that, I have written task in ansible. It goes as below:
- name: 'Ensure realm-management roles are added to demo-app "Administrator" role'
debug:
msg: "{{ item }}"
loop: "{{ lookup('template', 'realm/DemoRealm-realm.json.j2').roles.client['demo-app'] }} | subelements('composites.client.realm-management') }}"
when:
- item.name == 'Administrator'
- item.composite == true
- item.composites is defined
My expectation is to print values from realm-management array. But the task I have added is also looping through Developer and Operator objects ignoring conditional and as they don't have composites key in them, task is failing. I have tried numerous approaches but none worked. Please suggest me what to do.

You can use json_query to validate object properties like so:
loop: "{{ lookup('template', 'realm/DemoRealm-realm.json.j2').roles.client['demo-app'] | json_query('[?name==`Administrator` && composite==`true` && composites]') }}"
when: item.composites is defined"

Related

ansible json_query with character "-"

I have this json file and I need extract some data but problem is with this character -
{
"changed": true,
"failed": false,
"meta": {
"request_url": "pm/config/adom/AMIS/obj/dynamic/interface",
"response_code": 0,
"response_data": [
{
"color": 0,
"defmap-zonemember": [],
"description": "BYOD VLAN sub-interface.\nUsed in policy for firewalls maintaining BYOD traffic on LIBOs",
"dynamic_mapping": null,
"name": "BYOD",
"single-intf": "enable"
},
{
"color": 0,
"defmap-zonemember": [],
"dynamic_mapping": [
{
"_scope": [
{
"name": "ftg02",
"vdom": "dev"
}
],
"intrazone-deny": "enable",
"local-intf": [
"port8"
]
}
],
"name": "DMZ",
"single-intf": "disable"
},
{
"color": 0,
"defmap-zonemember": [],
"description": "Guest VLAN sub-interface. Used in policy for firewalls maintaining Guestnet traffic on LIBOs",
"dynamic_mapping": null,
"name": "Guest",
"single-intf": "enable"
}
]
}
}
I would like to get result of this "name": "DMZ" - DMZ with some condition
if local-intf == port8
I have got this error
FAILED! => {"msg": "template error while templating string: expected token ',', got 'local'. String: {{ item | json_query('dynamic_mapping[0]['local-intf'][0]') }}"}
here is my playbook which does not work \
- name: interface
debug:
msg: "{{ item.name }}"
loop: "{{ interface_info.meta.response_data }}"
when:
- item.dynamic_mapping != 'null'
- item | json_query('dynamic_mapping[0]._scope[0].name') == 'ftg02'
- item | json_query('dynamic_mapping[0]._scope[0].vdom') == 'dev'
- item | json_query('dynamic_mapping[0]['local-intf'][0]) == 'port8' #this line does not work
is some way to dont use loop ? Loop is to slow .
thank you for help
Try this
- debug:
msg: "{{ interface_info.meta.response_data|
selectattr('dynamic_mapping')|
selectattr('dynamic_mapping.0.local-intf.0', '==', 'port8')|
selectattr('dynamic_mapping.0._scope.0.name', '==', 'ftg02')|
selectattr('dynamic_mapping.0._scope.0.vdom', '==', 'dev')|
map(attribute='name')|first }}"
should give the first item
msg: DMZ

Ansible: delete one route from route table in AWS

I have a route table in AWS where there is a subnet getting routed to one host for each host. I can setup those routes automatically using this code:
- name: Add route to host container network
ec2_vpc_route_table:
region: region
vpc_id: "vpc-somestring"
purge_subnets: false
purge_routes: false
lookup: id
route_table_id: rtb-somestring
routes:
- dest: "1.2.3.0/24"
instance_id: "i-somestring"
This is fine for creating new hosts automatically. But if I want to remove a host, I want to delete the matching route table entry.
I thought, I could just fetch the route table using ec2_vpc_route_table_info, then take the routes filtered with rejectattr and feed it back to ec2_vpc_route_table, replacing the whole table. But, info gives me this format of routing tables:
"all_routes": [
{
"destination_cidr_block": "1.2.3.0/24",
"gateway_id": null,
"instance_id": "i-somestring",
"instance_owner_id": "1234567890",
"interface_id": "eni-somestring",
"network_interface_id": "eni-somestring",
"origin": "CreateRoute",
"state": "active"
},
{
"destination_cidr_block": "5.5.5.0/21",
"gateway_id": "local",
"instance_id": null,
"interface_id": null,
"network_interface_id": null,
"origin": "CreateRouteTable",
"state": "active"
},
{
"destination_cidr_block": null,
"destination_ipv6_cidr_block": "affe:affe:affe:affe::/56",
"gateway_id": "local",
"instance_id": null,
"interface_id": null,
"network_interface_id": null,
"origin": "CreateRouteTable",
"state": "active"
}
]
However, I can't feed that table to ec2_vpc_route_table, because that module just wants a list looking like this:
[
{
"dest": "1.2.3.0/24",
"instance_id": "i-somestring"
},
{
"dest": "5.5.5.0/21",
"gateway_id": "local
},
{
"dest": "affe:affe:affe:affe::/56",
"gateway_id": "local"
}
]
Why is the output of the info module not in a format that I can feed back to the route_table module? How can I convert the output into a format that I can feed back to the route_table module?
Thanks for any input.
a sample of solution:
- hosts: localhost
gather_facts: false
vars:
all_routes: "{{ lookup('file', 'zson.json') | from_json }}"
tasks:
- name: display json
debug:
var: all_routes
- name: create new json
set_fact:
result: "{{ result | d([]) + [{ 'dest': _block, _key: _gateway }] }}"
vars:
_block: "{{ item.destination_cidr_block if item.destination_cidr_block != None else item.destination_ipv6_cidr_block }}"
_gateway: "{{ item.gateway_id if item.gateway_id != None else item.instance_id }}"
_key: "{{ 'gateway_id' if item.gateway_id != None else 'instance_id' }}"
loop: "{{all_routes }}"
- name: display result
debug:
var: result
result:
ok: [localhost] => {
"result": [
{
"dest": "1.2.3.0/24",
"instance_id": "i-somestring"
},
{
"dest": "5.5.5.0/21",
"gateway_id": "local"
},
{
"dest": "affe:affe:affe:affe::/56",
"gateway_id": "local"
}
]
}

Ansible, how to query deeply nested json keys

I'm using Ansible to make an API call that returns a huge data set, and I need to be able to get a nested value to print to the screen. I tried using json_query but not sure what I'm doing wrong.
My task:
- name: Get certificate by CN name.
uri:
method: GET
url: "https://mayapi/api/1/certificates?filter=cn;{{ inventory_hostname }}"
headers:
Authorization: Bearer {{ login.json.token }}
Content-Type: application/json
validate_certs: no
register: certs
- name: Print certs for application
debug:
msg: "{{ certs.json | json_query(items) }}"
This is a small snippet of the output. I want to be able to print ID, and email.
{
"msg": {
"changed": false,
"connection": "close",
"content_length": "65833",
"content_type": "application/json",
"cookies": {},
"cookies_string": "",
"date": "Mon, 10 May 2021 21:33:29 GMT",
"elapsed": 0,
"failed": false,
"json": {
"items": [
{
"active": true,
"application": [
{
"director": {
"active": true,
"email": "user#domain.com",
"fullname": "John Doe",
"id": 1611,
"manager": "John Doe",
"managerEmail": "johndoe#email.com",
"username": "jdoe"
},
...
...
...
}
I get the following error indicating "certs.items" doesn't exist:
FAILED! => {"msg": "Error in jmespath.search in json_query filter plugin:\n'items' is undefined"}
I was expecting all of the items to get printed to the screen and then if I wanted something below items I would do items.active, items.application, etc... But this is not correct since I keep erroring out.
I also tried looping through cert.json and cert.json.items:
- name: Print certs for application
debug:
msg: "{{ item.application.name }}"
loop: "{{ certs.json}}"
But get this error message:
{"msg": "Invalid data passed to 'loop', it requires a list, got this instead: {u'items': [{u'status': u'Active-Pending Install'...shows all the data of the nested json
Then I tried this:
- name: Print certs for application
debug:
msg: "{{ item.application.name }}"
loop: "{{ certs.json.items}}"
But got this error message:
{"msg": "Invalid data passed to 'loop', it requires a list, got this instead: <built-in method items of dict object at 0x7f0c9ec43050>. Hint: If you passed a list/dict of just one element, try adding wantlist=True to your lookup invocation or use q/query instead of lookup."}
Made some progress with this:
- name: Print certs for application
debug:
msg: "KEY:::: {{ item.key }}, VALUE:::: {{ item.value.0 }}"
loop: "{{ lookup('dict', certs.json) }}"
when: "'items' in item.key"
ignore_errors: yes
But this only prints items in index 0 of the list:
"msg": "KEY:::: items, VALUE:::: {u'status': u'Active-Pending Install', u'serialHex': u'1111', u'validityStart': u'2021-05-10T21:01:36+00:00', u'cn': u'node2.test.corp.net', u'validityEnd': u'2023-05-10T21:11:36+00:00', u'application': [{u'uuid': u'2222', u'name': u'abc'}], u'certType': u'CertType.INTERNAL', u'id': 2582, u'issuer': u'server1'}"
I'm trying to print the 'cn', 'id', and 'serialHex' values from each list element for the key 'items'.
This is the data set that I'm trying to query with Ansible:
{
"total": 2,
"items": [
{
"application": [
{
"uuid": "111",
"name": "CDE"
}
],
"validityEnd": "2023-05-10T21:11:36+00:00",
"certType": "CertType.INTERNAL",
"issuer": "server1",
"id": 2582,
"validityStart": "2021-05-10T21:01:36+00:00",
"status": "Active-Pending Install",
"serialHex": "aaa",
"cn": "node2.corp.net"
},
{
"application": [
{
"uuid": "222",
"name": "CDE"
}
],
"validityEnd": "2023-05-10T21:05:26+00:00",
"certType": "CertType.INTERNAL",
"issuer": "server1",
"id": 2581,
"validityStart": "2021-05-10T20:55:26+00:00",
"status": "Active-Pending Install",
"serialHex": "bbbb",
"cn": "node1.corp.net"
}
]
}
You are regrettably stepping on a quirk of "objects in ansible are python dicts" in that .items() and .keys() and quite a few other attributes-which-are-methods cannot be referenced using the . notation since jinja2 believes you intend to call that method. Rather, one must use the __getitem__ syntax of ["items"] in order to make it abundantly clear that you mean the dict key, and not the method of the same name
tasks:
- name: use json_query as you were originally asking
debug:
msg: >-
{{ certs.json | json_query('items[*].{c: cn,i: id,s: serialHex}') }}
- name: or a jinja2 for loop as you separately attempted
debug:
msg: >-
[
{%- for i in certs.json["items"] -%}
{{ "" if loop.first else "," }}
{{ [i.cn, i.id, i.serialHex ]}}
{%- endfor -%}
]
produces the output from their respective steps:
TASK [debug] ******************************************************************************************************************************
ok: [localhost] => {
"msg": [
{
"c": "node2.corp.net",
"i": 2582,
"s": "aaa"
},
{
"c": "node1.corp.net",
"i": 2581,
"s": "bbbb"
}
]
}
TASK [debug] ******************************************************************************************************************************
ok: [localhost] => {
"msg": [
[
"node2.corp.net",
2582,
"aaa"
],
[
"node1.corp.net",
2581,
"bbbb"
]
]
}

How to assign ansible variable output to another variable from nested list

I need help to perform join operation of Ansible task output. I have output as below:
{
"ansible_facts": {
"server_and_its_status": [
{
"name": "server1",
"session": "enabled"
},
{
"name": "server2",
"session": "enabled"
}
]
},
"changed": false
}
I want the output to be formatted as:
{
"server1": "enabled"
"server2": "enabled"
}
I have tried many ways to get the output from my task as below but no luck:
- name: Get the server details
set_fact:
server_and_its_status: "{{ jsondata1 | json_query('data.member[].nodes[].{name: name, session: sessionStatus}') | join(':','name','session') }}"
How can I achieve this?
Use filter items2dict, e.g.
- set_fact:
srv_stat: "{{ server_and_its_status|
items2dict(key_name='name', value_name='session') }}"
gives
srv_stat:
server1: enabled
server2: enabled

set_fact create a list with items

I have task which calls an API and I register the o/p in a varaible;
- name: Get Object storage account ID
uri:
url: 'https://api.softlayer.com/rest/v3.1/SoftLayer_Network_Storage_Hub_Cleversafe_Account/getAllObjects.json?objectFilter={"username":{"operation":"{{ item }}"}}'
method: GET
user: abxc
password: 66c94c447a6ed8a0cf058774fe38
validate_certs: no
register: old_existing_access_keys_sl
with_items: '{{ info["personal"].sl_cos_accounts }}'
old_existing_access_keys_sl holds:
"old_existing_access_keys_sl.results": [
{
"json": [
{
"accountId": 12345,
"id": 70825621,
"username": "xyz-11"
}
]
},
{
"json": [
{
"accountId": 12345,
"id": 70825621,
"username": "abc-12"
}
]
}
I want to make a list of id's for further processing an tried the following task but this did not work:
- name: Create a list of account ids
set_fact:
admin_usernames = "{{ item.json[0].id | list }}"
with_items: old_existing_access_keys_sl.results
I am not sure if that's even possible. I also tried this:
- name: create a list
set_fact:
foo: "{% set foo = [] %}{% for i in old_existing_access_keys_sl.results %}{{ foo.append(i) }}{% endfor %}"
foo always comes as blank and as a string:
TASK [result] *****************************************************************************************************************************************
ok: [localhost] => {
"foo": ""
}
Given your example data, you can extract a list of ids using the json_query filter, like this:
---
- hosts: localhost
gather_facts: false
vars:
old_existing_access_keys_sl:
results:
[
{
"json": [
{
"accountId": 12345,
"id": 70825621,
"username": "xyz-11"
}
]
},
{
"json": [
{
"accountId": 12345,
"id": 70825621,
"username": "abc-12"
}
]
}
]
tasks:
- debug:
var: old_existing_access_keys_sl|json_query('results[*].json[0].id')
This will output:
TASK [debug] **********************************************************************************************************************************************************************************
ok: [localhost] => {
"old_existing_access_keys_sl|json_query('results[*].json[0].id')": [
70825621,
70825621
]
}
If you want to store these in a new variable, you can replace that debug task with set_fact:
- set_fact:
admin_ids: "{{ old_existing_access_keys_sl|json_query('results[*].json[0].id') }}"
Update
For a list of dictionaries, just change the json_query expression:
- debug:
var: "old_existing_access_keys_sl|json_query('results[*].json[0].{id: id, username: username}')"
For more information, see the jmespath website for documentation and examples.

Resources