By Popular Demand – Automating the Cisco Identity Services Engine (ISE) External RESTful Services (ERS) API Suite

My last post, which has turned into one of my more popular posts, covered automating the Cisco ISE MnT Nodes REST API with Postman and Ansible. We also discovered there is an XML limitation with the MnT REST API.

I’ve been asked by several people if a similar approach can be used with the Cisco ISE ERS. Not only can it be done – because ERS returns XML or JSON – it can be done much easier without the additional XML-to-JSON conversion step.

Cisco ISE ERS

Unlike the MnT, which has a very small subset (3 Session APIs for example) of available APIs, ERS has a lot of available APIs to work with!

First you have to enable ERS

You need a User with ERS permissions

To reach the ERS Software Developer Kit (SDK) then visit:

https://ise.my.domain.com:9060/ers/sdk

Now this, which can be a bit misleading, will launch the Quick Reference tab

In the bottom left – there are other panels of information – namely the API References

Now go API-shopping!

This is a pretty incredible list of APIs available to us, each with a dedicated documentation guide, and a rich set of XML or JSON data sets for a single record or all records.

REST API Development

Let’s use the Authorization Profile API to start with, partially to compliment the MnT Session information API documentation we have, let’s enumerate our AuthZ profiles.

First let’s explore the documentation!

Side note – I am usually a do-it-first then read-the-docs-later type of developer but in this case I need the particular URL to actually get to the API.

It’s also good practice !

Various ways to interact with the API are listed – for this example we are interested in the Get-All API

Each API will provide the resource definition – which helps you understand the returned data structure and is it valuable to capture

For example vlan nameID in this returns the name of the VLAN each of my AuthZ policies places authorized clients. This is very valuable to me.

Then check out the actual XML and JSON sample responses so you can see what to expect back in Postman and eventually your JSON files

And then, and this is what we are after, the JSON

Now the key here is the Get-All we still need to go find the request details, which look like this:

So we have the method (GET) , the URI (https://ise.my.domain.com:9060/ers/config/authorizationprofile), and the required (Content-Type and Accept) headers.

API Tip: Before we get started I want to highlight that the ERS API returns 20 results by default. This might not seem important but what if you have 27 records? 95 records? 1,200 records?

The API uses pagination and returns groups of results (20 by default) per page of responses. This maximum records response can be adjusted with a maximum of 100 records per response.

This is done by adding ?size=(1 – 100)

Some of my datasets are less than 100 records so I add ?size=100 to my string to get, effectively, all records back.

We will cycle back to how to handle paginated responses later in this post. For now lets just some datasets that come back in a single response to ease into this concept.

Take it to Postman

Next step we build our Cisco ISE Postman Collection (or in my case add to my existing collection) and setup our request

The results are interesting – I have found 27 AuthZ Profiles and I get the basic internal ISE ID, name, and description of each.

Part of the pagination tip includes how ISE handles Get-All responses.

  1. Get-All
  2. List Total
  3. List Each Resource summary
  4. Include the Resource’s detailed href

The href attribute specifies the URL of the page the link goes to.

So we follow this link for a specific AuthZ profile and see what the individual resource details look like:

Ok here we go!

Move it into Ansible URI

Create a new YAML file – the Ansible playbook – and convert the working Postman string, with authentication and headers, and register the JSON response from the Get-All Authorization Profile with up to 100 records.

Now we have the first Postman response back – the list of AuthZ Profiles – registered in AuthorziationProfiles_List which we now can loop-over and go get each profile’s href and get that JSON. Follow the trail.

So here our loop simply loops over the natural JSON list we get back. If you were to recall the resources is a JSON list

So the actual URL we are visiting on each loop is the item.link.href !

Again I like to have this JSON in a file using the copy module and the | to_nice_json filter.

Which looks just like the Postman body:

Which we then move into a Jinja2 template

Which is easy enough

We loop over each result in AuthorizatonProfiles_Details.results which again is the natural JSON list so each key is simply {{ result.json.AuthorizationProfile.xx }}; .name for example.

Which results in the business-ready CSV !

Refactoring working code – rinse and repeatability

One amazing thing about the ERS API suite is how uniform and standard they all seem to be.

Meaning you can copy the playbook above and make the following adjustments to reflect the API you are developing against

  1. Rename the playbook
  2. Update all comments and task names:
  3. Update the URL
  4. Update all registered variable names
  5. Update all output file names

In fact with a few Find / Replace operations you can copy and transform the first API playbook to any other API playbook.

As an example my CiscoISEAuthenticationProfiles.yml file and my CiscoISEdACLList.yml playbooks are both exactly 55 lines of code each! They are exactly the same minus the unique identifiers linking back to the targeted API.

Handling Pagination

So far I’ve avoided the paginated responses but let’s add pagination handling into the playbook.

Here is an example, my Network Device API

https://ise.my.domain.com:9060/ers/config/networkdevice?size=100

Returns over 1,200 items, with 100 items per page.

So how do we deal with this?

So we should have what we need to convert this to Ansible and automatically handle the pagination using some Ansible math filters.

So inside Page_count we have .json.SearchResult.total (1215) which we can use to establish the number of pages we need to loop over.

And speaking of loops Ansible has a with_sequence looping mechanism that almost seems designed to work with paginated URI responses.

Meaning we can set the start of our sequence (1) and end of our sequence (the total number of responses divided by the number of responses per page (100) rounded up to the next whole number.

So here we go

Pay attention to the page={{ item }} which is each sequence number in the with_sequence loop.

We loop starting at 1 and end at the Total pages divided by 100 (make sure to parenthesize this math) then we use the | round filter specifying to round up (ceil) to the largest next whole number (0) then finally set that value as an integer so the sequence can treat it as a number.

This is pretty neat stuff here is the task in Ansible for me I was expecting 13 pages

Now I need a json_query to loop over the results (outer) and results (inner)

So lets set a fact to do just that

Now we should be able to iterate over this list of URIs

This seems to be working

I’m taking a coffee break while this runs – back in a few minutes!

Compiling

If you know me by now – my next step is always to put this output into a .json file

Which looks like this:

Which I then pass to the Jinaj2 template

Which looks like this:

Which then looks like this!

Which I have filted down to a sample – there are all 1,200+ records in this file!

So now I can handle any paginated responses – with only one or two extra steps!

I hope this series on the Cisco ISE REST APIs – both the ERS and MnT – has been valuable to you ! As you can see regardless or XML or JSON; paginated or not; we can easily use Ansible and a few other tools to automate the Cisco ISE REST APIs!