Ansible: delete one route from route table in AWS - ansible

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

Related

Loop with conditional in 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"

Output of Ansibble task

I am using command hcloud to create cloud server in Hetzner. I get an output like this:
changed: [localhost] => (item={'name': 'TEST-VARIABLES', 'server_type': 'cx11', 'os_image': 'ubuntu-20.04', 'server_labels': 'Name=test-server', 'server_location': 'hel1'}) => {
"ansible_loop_var": "item",
"changed": true,
"hcloud_server": {
"backup_window": "None",
"datacenter": "hel1-dc2",
"delete_protection": false,
"id": "19461514",
"image": "ubuntu-20.04",
"ipv4_address": "11.111.111.111",
"ipv6": "1a71:7f9:c011:0b09::/64",
"labels": {
"Name": "test-server"
},
"location": "hel1",
"name": "TEST-VARIABLES",
"placement_group": null,
"rebuild_protection": false,
"rescue_enabled": false,
"server_type": "cx11",
"status": "running"
},
"invocation": {
"module_args": {
"allow_deprecated_image": false,
"api_token": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"backups": null,
"datacenter": null,
"delete_protection": null,
"endpoint": "https://api.SERVER.cloud/v1",
"firewalls": null,
"force": false,
"force_upgrade": false,
"id": null,
"image": "ubuntu-20.04",
"labels": {
"Name": "test-server"
},
"location": "hel1",
"name": "TEST-VARIABLES",
"placement_group": null,
"rebuild_protection": null,
"rescue_mode": null,
"server_type": "cx11",
"ssh_keys": null,
"state": "present",
"upgrade_disk": false,
"user_data": null,
"volumes": null
}
},
"item": {
"name": "TEST-VARIABLES",
"os_image": "ubuntu-20.04",
"server_labels": "Name=test-server",
"server_location": "hel1",
"server_type": "cx11"
},
"root_password": "DFLDJFLDFDLFKJDLFKJ"
}
I try to get line with "ipv4_address": "11.111.111.111", and "root_password": "DFLDJFLDFDLFKJDLFKJ", but when I use task:
---
- name: Create a basic server
hcloud_server:
api_token: "{{ token }}"
name: "{{ item.name }}"
server_type: "{{ item.server_type }}"
image: "{{ item.os_image }}"
labels: "{{ item.server_labels }}"
location: "{{ item.server_location }}"
state: present
register: server_info
with_items: "{{ server }}"
- name: Here IP
debug:
var: server_info.root_password
I got error:
TASK [/etc/ansible/roles/CREATE-server : Here IP.] *********************************************************************************************************
ok: [localhost] => {
"server_info.root_password": "VARIABLE IS NOT DEFINED!"
}
Could you please help, how I can get IP line and password line, to use them in the next task (for example to send via email). Thank you!
you register the content of loop, so your result is a list (results):
- name: display
debug:
msg: "ip: {{ item.hcloud_server.ipv4_address }} has password: {{ item.root_password }}"
loop: "{{ server_info.results }}"
result: here you have just one server declared, so just one item in the list results
"msg": "ip: 11.111.111.111 has password: DFDFDFDFDFDFDFDF"
if you want to limit the output of record, you could add loop_control parameter to loop with argument label:
loop: "{{ server_info.results }}"
loop_control:
label: "{{ item.hcloud_server.ipv4_address }}"
you could put another comment if you want with label or even empty string:
loop: "{{ server_info.results }}"
loop_control:
label: "result"

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

Is it possible to set/lookup Ansible facts when play returns multiple values

I have the following playbook in AWX that looks up Infoblox hosts based on their Mac Address and then outputs the information in a more user friendly format.
The current playbook works providing that a single host with that Mac address exists but fails if there are multiple.
---
- hosts: localhost
connection: local
vars:
niosip: ""
niosmac: ""
niosdhcp: ""
nioshostname: ""
niossearchcatagory: "{{ 'name' if searchcatagory == 'Hostname' else 'ipv4addr' if searchcatagory == 'IP Address' else 'mac' if searchcatagory == 'Mac Address'}}"
pre_tasks:
- include_vars:
file: creds.yml
tasks:
- name: fetch host record
set_fact:
host: "{{ lookup('nios', 'record:host', filter={niossearchcatagory: searchcriteria, 'view': 'Internal'}, provider=nios_provider) }}"
- name: Set niosip
set_fact:
niosip: "{{ host.ipv4addrs[0].ipv4addr }}"
nioshostname: "{{ host.name }}"
niosdhcp: "{{ host.ipv4addrs[0].configure_for_dhcp }}"
niosmac: "{{ host.ipv4addrs[0].mac }}"
when: host != [] and host.ipv4addrs[0].mac is defined
- name: Set niosip
set_fact:
niosip: "{{ host.ipv4addrs[0].ipv4addr }}"
nioshostname: "{{ host.name }}"
niosdhcp: "{{ host.ipv4addrs[0].configure_for_dhcp }}"
when: host != [] and host.ipv4addrs[0].mac is undefined
- name: Host not found
debug:
msg: 'Cant find related host'
when: host == []
- name: Display Display Registration Info
debug:
msg:
- Hostname = {{ nioshostname }}
- IP = {{ niosip }}
- Mac Address {{ niosmac }}
- Registered for DHCP = {{ niosdhcp }}
when: host != [] and host.ipv4addrs[0].mac is defined
Variables niossearchcatagory and searchcriteria are passed into the playbook via an AWX Survey.
I've searched possible options around using loops or splitting the output down but I'm really at a loss on the best way to process this.
If the output matches this then the playbook works as expected
{
"changed": false,
"ansible_facts": {
"host": [
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnVrLmFjLmJoYW0udGVzdC5zbmF0LWF3eHRlc3Q1:snat-awxtest5.test.com/Internal",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudWsuYWMuYmhhbS50ZXN0LnNuYXQtYXd4dGVzdDUuMTQ3LjE4OC4zMS40Lg:192.168.31.4/snat-awxtest5.test.com/Internal",
"configure_for_dhcp": false,
"host": "snat-awxtest5.test.com",
"ipv4addr": "192.168.31.4",
"mac": "10:20:30:40:50:60"
}
],
"name": "snat-awxtest5.test.com",
"view": "Internal"
},
]
},
"_ansible_no_log": false
}
And here's an example of the play returning multiple values
{
"changed": false,
"ansible_facts": {
"host": [
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnVrLmFjLmJoYW0udGVzdC5zbmF0LWF3eHRlc3Q1:snat-awxtest5.test.com/Internal",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudWsuYWMuYmhhbS50ZXN0LnNuYXQtYXd4dGVzdDUuMTQ3LjE4OC4zMS40Lg:192.168.31.4/snat-awxtest5.test.com/Internal",
"configure_for_dhcp": false,
"host": "snat-awxtest5.test.com",
"ipv4addr": "192.168.31.4",
"mac": "10:20:30:40:50:60"
}
],
"name": "snat-awxtest5.test.com",
"view": "Internal"
},
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnVrLmFjLmJoYW0udGVzdC5zbmF0LW15d2Vi:snat-myweb.test.com/Internal",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudWsuYWMuYmhhbS50ZXN0LnNuYXQtbXl3ZWIuMTQ3LjE4OC4zMS4yLg:192.168.31.2/snat-myweb.test.com/Internal",
"configure_for_dhcp": false,
"host": "snat-myweb.test.com",
"ipv4addr": "192.168.31.2",
"mac": "10:20:30:40:50:60"
}
],
"name": "snat-myweb.test.com",
"view": "Internal"
},
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnVrLmFjLmJoYW0udGVzdC5zbmF0LXdlYg:snat-web.test.com/Internal",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudWsuYWMuYmhhbS50ZXN0LnNuYXQtd2ViLjE0Ny4xODguMzEuMy4:192.168.31.3/snat-web.test.com/Internal",
"configure_for_dhcp": false,
"host": "snat-web.test.com",
"ipv4addr": "192.168.31.3",
"mac": "10:20:30:40:50:60"
}
],
"name": "snat-web.test.com",
"view": "Internal"
}
]
},
"_ansible_no_log": false
}
And this results in an error as the variables host.name, host.ipv4addrs etc.. don't exist which I presume is becasue there are multiples.
Any help on how to output each registration would be gratefully received.

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