App Discovery API Guide
This guide provides Python client samples for the Cisco Secure Access App Discovery API.
Note: Your Secure Access API key must have the permissions to read and write on the reports key scopes. For more information about the API key scopes, see Secure Access OAuth 2.0 Scopes.
First get your Secure Access API key, set up your environment, and install the Secure Access API client. For more information, see Samples Overview.
Run the Script
- Copy the script to a local file called
main.py. Locate the script in your environment in a directory above theciscodirectory. - Run
python3 main.py.
main.py
"""
Copyright (c) 2025 Cisco and/or its affiliates.
This software is licensed to you under the terms of the Cisco Sample
Code License, Version 1.1 (the "License"). You may obtain a copy of the
License at
https://developer.cisco.com/docs/licenses
All use of the material herein must be in accordance with the terms of
the License. All rights not expressly granted by the License are
reserved. Unless required by applicable law or agreed to separately in
writing, software distributed under the License is distributed on an "AS
IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied.
"""
from datetime import datetime
import pandas as pd
from requests_toolbelt import MultipartEncoder
import requests
import json
import os
from dotenv import load_dotenv
from cisco.secure_access import API
from cisco.secure_access import reports
from cisco.secure_access import GET
from cisco.secure_access import POST
from cisco.secure_access import PATCH
from cisco.secure_access import token_url
from cisco.secure_access import client_id
from cisco.secure_access import client_secret
# applications
app_discovery_applications_endpoint = "appDiscovery/applications"
app_discovery_applications_details_endpoint = "appDiscovery/applications/{}"
app_discovery_applications_details_identities_endpoint = "appDiscovery/applications/{}/identities"
app_discovery_applications_details_attributes_endpoint = "appDiscovery/applications/{}/attributes"
app_discovery_applications_details_risks_endpoint = "appDiscovery/applications/{}/risk"
# applications csv files
applications_csv = "applications.csv"
applications_details_csv = "application_details.csv"
applications_patch_details_csv = "application_patch_details.csv"
applications_identities_csv = "application_identities.csv"
applications_attributes_csv = "application_attributes.csv"
applications_risks_csv = "application_risks.csv"
# protocols
app_discovery_protocols_endpoint = "appDiscovery/protocols"
app_discovery_protocols_details_endpoint = "appDiscovery/protocols/{}"
app_discovery_protocols_details_identities_endpoint = "appDiscovery/protocols/{}/identities"
# protocols csv files
protocols_csv = "protocols.csv"
protocols_details_csv = "protocol_details.csv"
protocols_identities_csv = "protocol_identities.csv"
# application categories
app_discovery_application_categories_endpoint = "appDiscovery/applicationCategories"
# application categories csv file
application_categories_csv = "application_categories.csv"
# The directory where to write out files
output_dir = os.environ.get('OUTPUT_DIR') or os.get_env['OUTPUT_DIR']
load_dotenv()
def get_csv_filepath(filename: str):
return output_dir + "/" + filename
def get_applications_report(api):
''' Get report of applications. '''
try:
response = api.Query(reports, app_discovery_applications_endpoint, GET)
# Check if the API request was successful
if response.status_code == 200:
df = pd.json_normalize(response.json()['items'])
df.to_csv(get_csv_filepath(applications_csv), mode='w')
print(f"Success. GET {app_discovery_applications_endpoint}, {response.json()}")
return response.json()
else:
print(f"Failed to get the applications report. Status code: {response.status_code}, Response: {response.text}.")
return None
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}.")
def get_application_details(api, id):
''' Get the properties of the application. '''
try:
if id is None:
raise ValueError("id is required to get the application details.")
url = app_discovery_applications_details_endpoint.format(id)
# Get the properties for the application
response = api.Query(reports, url, GET)
# Check if the API request was successful
if response.status_code == 200:
df = pd.json_normalize(response.json())
df.to_csv(get_csv_filepath(applications_details_csv), mode='w')
print(f"Success. GET {url}, {response.json()}.")
return response.json()
else:
print(f"Failed to get the application {id}. Status code: {response.status_code}, Response: {response.text}.")
return
except Exception as e:
print(f"An error occurred: {e}.")
def patch_application(api, id, label=None):
''' Update application. '''
try:
if id is None or label is None:
raise ValueError("id and label are required to update the application.")
url = app_discovery_applications_details_endpoint.format(id)
# Prepare the payload
payload = {
"label": label
}
# Update an application
response = api.Query(reports, url, PATCH, payload)
# Check the response status
if response.status_code == 200:
df = pd.json_normalize(response.json())
df.to_csv(get_csv_filepath(applications_patch_details_csv), mode='w')
print(f"Success. PATCH {app_discovery_applications_details_endpoint}, {response.json()}")
return response.json()
else:
print(f"Failed to update the application. Status code: {response.status_code}, Response: {response.text}.")
return None
except Exception as e:
print(f"An error occurred: {e}.")
def get_application_attributes_details(api, id):
''' Get the application attributes. '''
try:
if id is None:
raise ValueError("id is required to get the application attributes.")
url = app_discovery_applications_details_attributes_endpoint.format(id)
# Get the application attributes
response = api.Query(reports, url, GET)
# Check if the API request was successful
if response.status_code == 200:
print(f"Success. GET {url}, {response.json()['attributesCategories']}.")
return response.json()
else:
print(f"Failed to get the application {id} attributes. Status code: {response.status_code}, Response: {response.text}.")
return
except Exception as e:
print(f"An error occurred: {e}.")
def get_application_identities_details(api, id):
''' Get the application identities. '''
try:
if id is None:
raise ValueError("id is required to get the application identities.")
url = app_discovery_applications_details_identities_endpoint.format(id)
# Get the application identities
response = api.Query(reports, url, GET)
# Check if the API request was successful
if response.status_code == 200:
df = pd.json_normalize(response.json()['items'])
df.to_csv(get_csv_filepath(applications_identities_csv), mode='w')
print(f"Success. GET {url}, {response.json()}.")
return response.json()
else:
print(f"Failed to get the application {id} identities. Status code: {response.status_code}, Response: {response.text}.")
return
except Exception as e:
print(f"An error occurred: {e}.")
def get_application_risks_details(api, id):
''' Get the application risks. '''
try:
if id is None:
raise ValueError("id is required to get the application risks.")
url = app_discovery_applications_details_risks_endpoint.format(id)
# Get the application risks
response = api.Query(reports, url, GET)
# Check if the API request was successful
if response.status_code == 200:
df = pd.json_normalize(response.json())
df.to_csv(get_csv_filepath(applications_risks_csv), mode='w')
print(f"Success. GET {url}, {response.json()}.")
return response.json()
else:
print(f"Failed to get the application {id} risks. Status code: {response.status_code}, Response: {response.text}.")
return
except Exception as e:
print(f"An error occurred: {e}.")
def get_protocols_report(api):
''' Get report of application protocols usage. '''
try:
# Get application protocols that have traffic in the organization
response = api.Query(reports, app_discovery_protocols_endpoint, GET)
# Check if the API request was successful
if response.status_code == 200:
df = pd.json_normalize(response.json()['items'])
df.to_csv(get_csv_filepath(protocols_csv), mode='w')
print(f"Success. GET {app_discovery_protocols_endpoint}, {response.json()}")
return response.json()
else:
print(f"Failed to get the applications protocol report. Status code: {response.status_code}, Response: {response.text}.")
return None
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}.")
def get_protocol_details(api, id):
''' Get the properties of the application protocol. '''
try:
if id is None:
raise ValueError("id is required to get the protocol details.")
url = app_discovery_protocols_details_endpoint.format(id)
# Get the properties for the protocol
response = api.Query(reports, url, GET)
# Check if the API request was successful
if response.status_code == 200:
df = pd.json_normalize(response.json())
df.to_csv(get_csv_filepath(protocols_details_csv), mode='w')
print(f"Success. GET {url}, {response.json()}.")
return response.json()
else:
print(f"Failed to get the application protocol {id}. Status code: {response.status_code}, Response: {response.text}.")
return
except Exception as e:
print(f"An error occurred: {e}.")
def get_protocol_identities_details(api, id):
''' Get the protocol identities. '''
try:
if id is None:
raise ValueError("id is required to get the protocol identities.")
url = app_discovery_protocols_details_identities_endpoint.format(id)
# Get the application identities
response = api.Query(reports, url, GET)
# Check if the API request was successful
if response.status_code == 200:
df = pd.json_normalize(response.json()['items'])
df.to_csv(get_csv_filepath(protocols_identities_csv), mode='w')
print(f"Success. GET {url}, {response.json()}.")
return response.json()
else:
print(f"Failed to get the protocol {id} identities. Status code: {response.status_code}, Response: {response.text}.")
return
except Exception as e:
print(f"An error occurred: {e}.")
def get_application_categories(api):
''' Get application categories. '''
try:
response = api.Query(reports, app_discovery_application_categories_endpoint, GET)
# Check if the API request was successful
if response.status_code == 200:
df = pd.json_normalize(response.json()['items'])
df.to_csv(get_csv_filepath(application_categories_csv), mode='w')
print(f"Success. GET {app_discovery_application_categories_endpoint}, {response.json()}")
return response.json()
else:
print(f"Failed to get the application categories. Status code: {response.status_code}, Response: {response.text}.")
return None
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}.")
def main():
# Exit out if the required client_id or client_secret is not set
for var in ['API_KEY', 'API_SECRET', 'OUTPUT_DIR']:
if os.environ.get(var) == None:
print("Required environment variable: {} not set".format(var))
exit()
# Get an API token
api = API(token_url, client_id, client_secret)
try:
data = get_applications_report(api)
id = None
if data and 'items' in data:
item_one = data['items'][0]
id = item_one['id']
# applications
if id:
get_application_details(api, id)
patch_application(api, id, 'unreviewed')
get_application_attributes_details(api, id)
get_application_identities_details(api, id)
get_application_risks_details(api, id)
# protocols
data = get_protocols_report(api)
id = None
if data and 'items' in data:
item_one = data['items'][0]
id = item_one['id']
if id:
get_protocol_details(api, id)
get_application_identities_details(api, id)
# application categories
get_application_categories(api)
except Exception as e:
print(e)
# main
if __name__ == "__main__":
main()