"The What?" - In this blog I want to cover an important Ansible module for those looking to gain an understanding of how to use Ansible to consume APIs without the need for custom modules. The URI module is part of the ansible-base and is included with all Ansible installations. This module allows interactions with HTTP & HTTPS web services.
"The Why?" - Learning how to automate mundane tasks is important, automation saves time, & for the general understanding of how components interact with each other to accomplish your end goal. Network automation is the future.
"The How?" - In the example below I will breakdown an Ansible playbook that I created that allows you to consume ISE APIs specifically using the Ansible URI module to automagically update a network device configuration settings within the ISE database.
This playbook is specifically intended to automatically update the following settings for an individual Network Device IP Address:
Device Owner
Device Location
TrustSec Notification & Updates CoA 'Send From' PSN
---
- name: ISE
hosts: localhost
connection: local
gather_facts: false
vars:
ise_user: XXXX
ise_pass: XXXX
tasks:
- name: Get Existing NAD ID string
uri:
url: https://XXXX:9060/ers/config/networkdevice?filter=ipaddress.EQ.{{ ip_addr }}
user: "{{ ise_user }}"
password: "{{ ise_pass }}"
headers:
Accept: application/json
content-type: application/json
ers-media-type: network.networkdevice.1.1
status_code: 200
method: GET
validate_certs: no
register: nad_id
- name: Print returned ISE json data
debug:
msg: "{{ nad_id.json }}"
- name: Get ISE ID String
set_fact:
id: "{{ nad_id | json_query(jmesquery) }}"
vars:
jmesquery: '*.SearchResult.resources[*].id'
- name: Extract NAD ID from Nested List
set_fact:
id: "{{ id[0][0] }}"
- name: Print ISE NAD ID
debug:
msg: "{{ id }}"
- name: Update NAD Details in ISE DB
uri:
url: https://XXXX:9060/ers/config/networkdevice/{{ id }}
user: "{{ ise_user }}"
password: "{{ ise_pass }}"
headers:
Accept: application/json
content-type: application/json
ers-media-type: network.networkdevice.1.1
status_code: 200
method: PUT
body_format: json
body: '{"NetworkDevice": {"id": "{{ id }}","name": "XXXX{{ site_id }}","profileName": "Cisco","coaPort": "1700","authenticationSettings" : {},"snmpsettings" : {"pollingInterval" : 3600,"linkTrapQuery" : "false","macTrapQuery" : "false",},"trustsecsettings" : {"deviceAuthenticationSettings" :{},"sgaNotificationAndUpdates" : {"downlaodEnvironmentDataEveryXSeconds" : 86400,"downlaodPeerAuthorizationPolicyEveryXSeconds" : 86400,"reAuthenticationEveryXSeconds" : 86400,"downloadSGACLListsEveryXSeconds" : 86400,"otherSGADevicesToTrustThisDevice" : "true","sendConfigurationToDevice" : "true","sendConfigurationToDeviceUsing" : "ENABLE_USING_COA","coaSourceHost" : "{{ ise_coa_node }}"
},"deviceConfigurationDeployment" : {"includeWhenDeployingSGTUpdates" : "true",}},"NetworkDeviceIPList": [{"ipaddress": "{{ ip_addr }}","mask": 32,}],"NetworkDeviceGroupList": ["Location#All Locations#{{ location }}","Device Type#All Device Types#SDA#{{ owner }}","IPSEC#Is IPSEC Device#No"]}}'
validate_certs: no
register: ise_update
- name: Print ISE Update Notification
debug:
msg: "{{ ise_update }}"
From the top:
The connection is actually set to execute locally since we are actually specifying an API to consume in the individual tasks/plays further in the playbook via the uri module with the url information.
It is important to note that the headers for all the APIs and requirements can be found/pulled from the SDK (see below for more info)
The first task (- name: Get Existing NAD ID string) is using a GET call (in relation to CRUD it would be a READ) to get the given IP Address ID string from ISE. This ID String will be appended to the following REST call to update the resource in the ISE DB
The succeeding tasks do the following:
parse the returned data to extract the ID string and store as variable (- name: Get ISE ID String).
Then do to how the string is nested we extract the ID string & set it as a new variable (- name: Extract NAD ID from Nested List)
Now that we have the extracted ID string from the original GET call we are ready to update the NAD in the ISE DB via (- name: Update NAD Details in ISE DB)
This is where testing to understand the JSON body syntax requirements can be hectic at first, but once you get a feel (LOOK AT THE SDK for other examples regarding requirements) you can really rock n roll
The final task prints the returned data from ISE which will show you old & new config. Essentially what the old config was & what is was changed to allowing you to confirm on the Ansible side that everything worked as expected.
NOTE: Important to know that the following vars are required via user input or survey if using tower:
{{ ip_addr }} = network device IP that you wish to update
{{ site_id }} = site identifier that appends to device hostname (this is optional; you can just update the code to have your full NAD name)
{{ location }} = location of the device based on your ISE location groups
{{ owner }} = who owns the device based on your ISE NAD device type groups
{{ ise_coa_node }} = PSN you wish ISE to use to send CTS updates via CoA
Remember that the variable {{ id }} is discovered during playbook execution.
If testing this: Be sure to have ERS enabled, update the user/pass, & url location. Test on a non-production device.
Lastly, to cover some additional information:
To understand more on Ansible modules check out their documentation here: Ansible Module Documentation
As for gaining more information on ISE APIs to include functionality, consuming, & required syntax use the ISE SDK. The SDK can be found via https://<you ise pan ip>:9060/ers/sdk#
The online SDK aides in explaining operations, parameters, & responses in relation to the ISE ERS API. ERS or External REST Services is for external clients to perform CRUD operations. See more on CRUD here: CURL, REST, & CRUD Tidbit.
That wraps this up. See more under the Automation tab. Cheers!