This is the demo reference for a Cisco Identity Services Engine (ISE) webinar delivered on October 4, 2022. You may find the recording in the CiscoISE YouTube Channel.
The basic curl commands for REST operations with ISE
The --data
option may be used with a local file beginning with @
or use inline JSON:
curl --include --insecure --location \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--request POST {URL} \
--data @filename.json
--data '
{
...JSON Data...
}'
curl --include --insecure --location \
--header 'Accept: application/json' \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--request GET {URL}
curl --include --insecure --location \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--request PUT {URL}
Available in ISE 3.2+
curl --include --insecure --location \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--request PATCH {URL}
curl --include --insecure --location \
--header 'Accept: application/json' \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--request DELETE {URL}
- ☰ > Administration > System > Settings > API Settings
- Select API Service Settings tab
- ✅ ERS (Read/Write)
- ✅ Open API (Read/Write)
- Save
Download ERS OpenAPI file to local computer for use with Postman later!
- Reveiw the swagger ui
- Create a Repository
{
"name": "FTP",
"protocol": "FTP",
"serverName": "198.18.133.36",
"path": "/",
"userName": "ise",
"password": "C1sco12345"
}
- Perform a backup with the repository
- ☰ > Administration > System > Admin Access > Administrators > Admin Groups
- ERS Admin : Full access permission to External RESTful Services (ERS) APIs. Admins assigned to this admin group can perform CRUD (POST, PUT, DELETE, and GET) operations.
- ERS Operator : Read-only access permission to the External RESTful Services (ERS) APIs.
- ☰ > Administration > System > Admin Access > Administrators > Admin Users
- + Add > Create an Admin User
Name:
ers-admin
Password:ISEisC00L
Admin Groups: ERS Admin Submit - + Add > Create an Admin User
Name:
ers-operator
Password:ISEisC00L
Admin Groups:ERS Admin
Submit
curl is a tool for transfering data from or to a server. It supports these protocols: DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, POP3, POP3S, RTMP, RTMPS, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET or TFTP. The command is designed to work without user interaction.
curl
has many options!
- there is a short option format
-
and a long--
option format:
curl --help
curl --help all
These are the most popular options that I use:
--head
: retrieve headers only--include
: include the response headers in the output--insecure
: disable certificate validation--location
: follow redirects--silent
: disable progress meter/bar--output <file>
: Write output to<file>
instead of stdout--styled-output
: Enables the automatic use of bold font styles when writing HTTP headers--verbose
: Makes curl verbose during the operation
Basic HTTP GET request:
curl http://ise.securitydemo.net
This returns 301 Moved Permanently
8-(
Use the --include
the response headers to make more sense of this:
Include the HTTP response headers in the output
curl --include http://ise.securitydemo.net
Update the redirect Location
again and again and again!
curl --include https://ise.securitydemo.net
curl --include https://ise.securitydemo.net:443/admin/
curl --include https://ise.securitydemo.net:443/admin/login.jsp
And that is why I like to always use the --location
option to automatically follow redirects!
curl --include --location http://ise.securitydemo.net
Allow insecure connections with demo or lab instances that do not have a signed certificate:
curl --include --location http://ise.trust0.net
curl --include --insecure --location http://ise.trust0.net
Now it is time to try an actual ISE API! See all of the ISE API endpoints/resources @ https://cs.co/ise-api
Also note the resources for Managing APIs via APIs:
- OpenAPI
- ERS API Refer to Versioning for When a specific API endpoint was first supported by ISE. You may need to upgrade to perform some of the things you see here today.
curl --include --location --insecure https://ise.securitydemo.net/ers/config/networkdevice
results in a 401 Unauthorized because we are not authorized API users.
curl --include --location --insecure --user admin:ISEisC00L https://ise.securitydemo.net/ers/config/networkdevice
Response:
HTTP/1.1 415 (Unsupported Media Type)
...
<?xml version="1.0" encoding="utf-8" standalone="yes"?><ns3:ersResponse operation="GET-getAll-networkdevice" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ns3="ers.ise.cisco.com"><link rel="related" href="https://ise.securitydemo.net/ers/config/networkdevice" type="application/xml"/><messages><message type="ERROR" code="Resource media type exception"><title>Illegal Request Header: one or more of 'accept' / 'content-type' / 'ers-media-type' headers is not supported.</title></message></messages></ns3:ersResponse>
Request XML output with the Accept:
header:
curl --insecure --silent \
--user admin:ISEisC00L \
--header 'Accept: application/xml' \
https://ise.securitydemo.net/ers/config/networkdevice \
XML in a long, concatenated string is not easy to read. You may use the Unix pipe (|
) to redirect it to the xmllint program for pretty printing. A linter is a static code analysis tool for syntax checking.
⚠ You must remove
--include
or the linting will fail!
💡 Use
--silent
to remove the progress table
💡 The final
-
at the end of the command tells xmllint to usestdin
for the input
curl --insecure --silent \
--user admin:ISEisC00L \
--header 'Accept: application/xml' \
https://ise.securitydemo.net/ers/config/networkdevice \
| xmllint --pretty 1 -
Request JSON output instead of XML with the Accept:
header:
curl --include --location --insecure --user admin:ISEisC00L --header 'Accept: application/json' https://ise.securitydemo.net/ers/config/networkdevice
YAY! Finally we got a JSON result from ISE!
For ISE versions before ISE 3.1, you will need to specify port 9060 when invoking the ERS APIs:
curl --include --location --insecure --user admin:ISEisC00L --header 'Accept: application/json' https://ise.securitydemo.net:9060/ers/config/networkdevice
If you are expecting JSON data and you get HTML instead, you either have not enabled APIs or you need to specify the port.
Are these command lines looking long? It's time to break them up so they are easier to read. In the Unix bash shell, the backslash character \
may be used to remove any special meaning for the next character read and for line continuation bash manual. For line continuation, the backslash must be the last character on the line.
curl --include --insecure --location \
--user admin:ISEisC00L \
--header 'Accept: application/json' \
https://ise.securitydemo.net/ers/config/networkdevice
You may also make JSON data pretty by piping the data through jq
, a command line utility for performing JSON queries :
⚠ You must remove
--include
or the linting will fail!
curl --insecure --location --silent \
--user admin:ISEisC00L \
--header 'Accept: application/json' \
https://ise.securitydemo.net/ers/config/networkdevice \
| jq
jq
is very powerful in extracting JSON data. If you only wanted the network device names, you could do:
curl --insecure --location --silent \
--user admin:ISEisC00L \
--header 'Accept: application/json' \
https://ise.securitydemo.net/ers/config/networkdevice \
| jq '.SearchResult.resources[].name'
It's not very interesting with only 1 network device but if you query against endpointgroup
or sgt
then you can begin to see the power.
Environmental variables are variables that are defined for the current shell and are inherited by any child shells or processes. Environmental variables are used to pass information into processes that are spawned from the shell. Use environment variables to securely load and use your credentials without showing anyone looking over your shoulder or in your terminal history.
You may view all current environment variables with the env
command and individual variables with printenv
.
env
env | grep ISE
printenv ISE_REST_PASSWORD
💡 Environment variables traditionally are named in UPPERCASE while shell variables are lowercase
Define environment variables using the export
command:
export ISE_REST_USERNAME=admin
export ISE_REST_PASSWORD=ISEisC00L
export ISE_HOSTNAME=ise.securitydemo.net
env | grep ISE
Use environment variables by referencing them with a $
. You may show them individually using the echo
command :
echo $ISE_REST_USERNAME $ISE_REST_PASSWORD $ISE_HOSTNAME
Now try a previous curl
command using environment variables:
curl --insecure --location --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Accept: application/json' \
https://$ISE_HOSTNAME/ers/config/networkdevice
Save your environment variables in a text file in ~/.secrets/
directory for fast and easy loading later using the source
command:
ls -1 ~/.secrets
cat ~/.secrets/ise_azure.sh
cat ~/.secrets/ise_dcloud.sh
source ~/.secrets/ise_dcloud.sh
env | grep ISE
We previously got information from the ISE ERS API for a network device :
curl --insecure --location --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Accept: application/json' \
https://$ISE_HOSTNAME/ers/config/networkdevice
but this only contains 4 attributes:
id
name
description
link
To get all of the configuration for a specific network device, you must use the name
or id
attributes which is what the link attribute was showing you:
curl --insecure --location --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Accept: application/json' \
https://$ISE_HOSTNAME/ers/config/networkdevice/{id}
GET profilerprofile (>600)
curl --insecure --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Accept: application/json' \
https://ise.securitydemo.net/ers/config/profilerprofile \
| jq
GET the end of the profilerprofile list
curl --include --insecure --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
"https://ise.securitydemo.net/ers/config/profilerprofile?size=100\&page=7" \
| jq
🐞 Filter does not return all of the matching resources! The matching result count is 4 but it only shows 1!
curl --include --insecure --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
https://ise.securitydemo.net/ers/config/profilerprofile?filter=name.STARTSW.i
Find endpointgroup
curl --include --insecure --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
https://ise.securitydemo.net:9060/ers/config/endpointgroup \
| jq -c .SearchResult.resources[] \
| grep Meraki -
Create a new endpoint
curl --include --insecure --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
https://ise.securitydemo.net:9060/ers/config/endpoint \
--data '
{
"ERSEndPoint" : {
"name" : "New Endpoint",
"description" : "My Endpoint",
"mac" : "FE:ED:DA:DD:BE:EF",
"staticGroupAssignment" : true,
"groupId" : "1e2700a0-8c00-11e6-996c-525400b48521"
}
}'
Response Header :
HTTP/1.1 201
Location: https://ise.securitydemo.net:9060/ers/config/endpoint/0bd811b0-892f-11eb-b0e1-b2ca5a4c3815
curl --include --insecure --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
https://ise.securitydemo.net:9060/ers/config/endpoint \
--data @AC17C80C17A2.json
curl --include --insecure --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Content-Type:application/json' \
--header 'Accept: application/json' \
https://$ISE_HOSTNAME:9060/ers/config/internaluser \
--data '
{
"InternalUser" : {
"name" : "thomas",
"password" : "ISEisC00L",
"changePassword" : false
}
}'
Do it again to get a 500 Error because the resource already exists!
⚠ Requires guestapi
user!
curl --include --insecure --silent \
--user $guestapi_username:$guestapi_password \
--header 'Content-Type:application/json' \
--header 'Accept: application/json' \
https://$ISE_HOSTNAME:9060/ers/config/guestuser \
--data '
{
"GuestUser": {
"guestType": "Daily (default)",
"portalId" : "bd48c1a1-9477-4746-8e40-e43d20c9f429",
"guestInfo": {
"enabled": "true",
"userName": "rigo",
"password": "ISEisC00L"
},
"guestAccessInfo": {
"validDays": 1,
"fromDate": "03/27/2021 17:40",
"toDate": "03/28/2021 17:40",
"location": "San Jose"
}
}
}'
Cisco IP Phone
curl --include --insecure --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--request PUT https://ise.securitydemo.net:9060/ers/config/endpoint \
--data '
{
"ERSEndPoint" : {
"name" : "IP Phone",
"description" : "Thomas IP Phone",
"mac" : "FE:ED:DA:DD:BE:EF",
"staticGroupAssignment" : true,
"groupId" : "14f5cac0-8c00-11e6-996c-525400b48521"
}
}'
Response:
HTTP/1.1 200
{
"UpdatedFieldsList" : {
"updatedField" : [ {
"field" : "groupId",
"oldValue" : "1e2700a0-8c00-11e6-996c-525400b48521",
"newValue" : "14f5cac0-8c00-11e6-996c-525400b48521"
}, {
"field" : "description",
"oldValue" : "My Endpoint",
"newValue" : "Thomas IP Phone"
} ]
}
}
Change network device name or password
GET hotspotportal (only 1) and look at the detail
curl --insecure --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Accept: application/json' \
https://ise.securitydemo.net/ers/config/hotspotportal \
| jq
curl --include --insecure --location \
--header 'Accept: application/json' \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--request GET https://$ISE_HOSTNAME/ers/config/hotspotportal/{id}
curl --include --insecure --location \
--header 'Content-Type:application/json' \
--header 'Accept: application/json' \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--request PATCH https://$ISE_HOSTNAME/ers/config/hotspotportal/{id}} \
--data '
{
"HotspotPortal": {
"settings": {
"aupSettings": {
"requireAccessCode": true,
"accessCode": "ISEisC00L"
}
}
}
}'
curl --include --insecure --location \
--header 'Content-Type:application/json' \
--header 'Accept: application/json' \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--request PATCH https://$ISE_HOSTNAME/ers/config/hotspotportal/{id} \
--data '
{
"HotspotPortal": {
"settings": {
"aupSettings": {
"accessCode": "ISEisC00LerNow"
}
}
}
}'
curl --include --insecure --location \
--header 'Content-Type:application/json' \
--header 'Accept: application/json' \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--request DELETE https://$ISE_HOSTNAME/ers/config/networkdevice/{id}
curl --include --insecure --silent \
--user $ISE_REST_USERNAME:$ISE_REST_PASSWORD \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--request DELETE https://ise.securitydemo.net:9060/ers/config/endpoint/{id}
ISE 2.7+ portal responds with HTTP/1.1 200
instead of HTTP/1.1 200 OK
!
curl --include https://ise.securitydemo.net:8443/portal/PortalSetup.action?portal={id}
- New Workspace
- Workspace Name : give your workspace a name
- Collections : your requests for an API
- APIs : Collections & environments with schemas
- Environments : sets of variables for use in context with requests
Install specific Python version and activate virtual environment
pipenv install --python 3.7
pipenv shell
pipenv install requests
Set a RADIUS secondary shared secret on all network devices
./second_radius_shared_secret.py