SendAllPANWIoTDevicesToCiscoISE
Gets all available devices from the IoT cloud and updates or creates them on Cisco ISE using the custom attributes.
python · IoT 3rd Party Integrations by Palo Alto Networks (Deprecated)
Source
import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 PANW_IOT_INSTANCE = demisto.args().get("panw_iot_3rd_party_instance") CISCO_ISE_ACTIVE_INSTANCE = demisto.args().get("active_ise_instance") GET_EP_ID_CMD = "cisco-ise-get-endpoint-id-by-name" CISCO_ISE_FIELD_MAP = { "ip": ["ZingboxIpAddress", "PanwIoTIpAddress"], "ip address": ["ZingboxIP", "PanwIoTIP"], "ip_address": ["ZingboxIP", "PanwIoTIP"], "profile": ["ZingboxProfile", "PanwIoTProfile"], "category": ["ZingboxCategory", "PanwIoTCategory"], "risk_score": ["ZingboxRiskScore", "PanwIoTRiskScore"], "risk score": ["ZingboxRiskScore", "PanwIoTRiskScore"], "confidence": ["ZingboxConfidence", "PanwIoTConfidence"], "confidence score": ["ZingboxConfidence", "PanwIoTConfidence"], "confidence_score": ["ZingboxConfidence", "PanwIoTConfidence"], "tag": ["ZingboxTag", "PanwIoTTag"], "asset_tag": ["ZingboxTag", "PanwIoTTag"], "Tags": ["ZingboxTag", "PanwIoTTag"], "hostname": ["ZingboxHostname", "PanwIoTHostname"], "osCombined": ["ZingboxOS", "PanwIoTOS"], "model": ["ZingboxModel", "PanwIoTModel"], "vendor": ["ZingboxVendor", "PanwIoTVendor"], "Serial Number": ["ZingboxSerial", "PanwIoTSerial"], "Serial_Number": ["ZingboxSerial", "PanwIoTSerial"], "endpoint protection": ["ZingboxEPP", "PanwIoTEPP"], "endpoint_protection": ["ZingboxEPP", "PanwIoTEPP"], "AET": ["ZingboxAET", "PanwIoTAET"], "External Network": ["ZingboxInternetAccess", "PanwIoTInternetAccess"], } INT_FIELDS = ["risk_score", "risk score", "confidence", "confidence score", "confidence_score"] def send_status_to_panw_iot_cloud(status, msg): """ Reports status details back to PANW IoT Cloud. param status: Status (error, disabled, success) to be send to PANW IoT cloud. param msg: Debug message to be send to PANW IoT cloud. """ resp = demisto.executeCommand( "panw-iot-3rd-party-report-status-to-panw", { "status": status, "message": msg, "integration_name": "ise", "playbook_name": "PANW IoT 3rd Party Cisco ISE Integration - Bulk Export to Cisco ISE", "asset_type": "device", "timestamp": int(round(time.time() * 1000)), "using": PANW_IOT_INSTANCE, }, ) if isError(resp[0]): err_msg = f'Error, failed to send status to PANW IoT Cloud - {resp[0].get("Contents")}' raise Exception(err_msg) def get_active_ise_instance_or_error_msg(): """ Get the active configured Cisco ISE instance, if not found then return the error message. """ response = demisto.executeCommand("GetCiscoISEActiveInstance", {}) err_msg = None active_instance = None data = response[0].get("EntryContext", {}) if "PaloAltoIoTIntegrationBase.ActiveNodeInstance" in data: active_instance = data.get("PaloAltoIoTIntegrationBase.ActiveNodeInstance") elif "PaloAltoIoTIntegrationBase.NodeErrorStatus" in data: err_msg = data.get("PaloAltoIoTIntegrationBase.NodeErrorStatus") return active_instance, err_msg def extract_ise_api_error(err_msg): """ Extract any connection error or error code if possible, Otherwise just return the original error """ err_msg = err_msg.split("-")[0] if err_msg.startswith("Error in API call to Cisco"): start = err_msg.find("[") + 1 end = err_msg.find("]") return err_msg[start:end] elif err_msg.startswith("Connection Error. Verify"): return "Connection Error" else: return err_msg def get_devices_from_panw_iot_cloud(offset, page_size): """ Gets assets from PANW IoT cloud. param offset: Offset number for the asset list. param page_size: Page size of the response being requested. """ resp = demisto.executeCommand( "panw-iot-3rd-party-get-asset-list", {"asset_type": "device", "increment_type": None, "offset": offset, "pageLength": page_size, "using": PANW_IOT_INSTANCE}, ) if isError(resp[0]): err_msg = f'Error, could not get assets from PANW IoT Cloud - {resp[0].get("Contents")}' raise Exception(err_msg) return resp[0]["Contents"] def convert_device_map_to_cisco_ise_attributes(device_map): """ Converts a PANW IoT device_map to Cisco ISE custom attributes map. param device_map: Single PANW IoT device_map with device attributes . """ attribute_list = {} if "deviceid" in device_map: if device_map["deviceid"] is None or device_map["deviceid"] == "": return None attribute_list["mac"] = device_map["deviceid"] if not is_mac_address(attribute_list["mac"]): return None zb_attributes = {} for field in device_map: if device_map[field] is None or device_map[field] == "": continue if field in CISCO_ISE_FIELD_MAP: if field in INT_FIELDS: try: int_val = int(device_map[field]) except Exception: continue zb_attributes[CISCO_ISE_FIELD_MAP[field][0]] = int_val zb_attributes[CISCO_ISE_FIELD_MAP[field][1]] = int_val else: zb_attributes[CISCO_ISE_FIELD_MAP[field][0]] = device_map[field] zb_attributes[CISCO_ISE_FIELD_MAP[field][1]] = device_map[field] attribute_list["zb_attributes"] = zb_attributes return attribute_list def update_existing_endpoint(mac, attr_map, ep_id, active_instance): """ Update an existing endpoint with the given custom attributes. Param mac: mac address of the endpoint that needs to be updated. Param attr_map: a map containing various ise custom attributes. Param ep_id: ID for endpoint that needs to be updated. Param active_instance: The primary/active ISE instance. """ attribute_names = "" attribute_values = "" for key in attr_map: attribute_names += key + "," attribute_values += str(attr_map[key]) + "," attribute_names = attribute_names[:-1] attribute_values = attribute_values[:-1] resp = demisto.executeCommand( "cisco-ise-update-endpoint-custom-attribute", { "id": ep_id, "macAddress": mac, "attributeName": attribute_names, "attributeValue": attribute_values, "using": active_instance, }, ) if isError(resp[0]): err_msg = f'Error, failed to update custom attributes for endpoint {id} - {resp[0].get("Contents")}' raise Exception(err_msg) def create_new_ep(mac, attr_map, active_instance): """ Create a new endpoint with the given params Param mac: mac address of the endpoint that needs to be created. Param attr_map: a map containing various ise custom attributes. Param active_instance: The primary/active ISE instance. """ resp = demisto.executeCommand( "cisco-ise-create-endpoint", {"mac_address": mac, "attributes_map": attr_map, "using": active_instance} ) if isError(resp[0]): err_msg = f'Failed to create new Endpoint {mac} - {resp[0].get("Contents")}' raise Exception(err_msg) def create_or_update_ep(mac, attr_map): """ Check if an enpoint exists in ISE, if not create one with the custom attributes otherwise update it. If at any point the connection goes down or we get a 401 - unautherized access we will attempt to get the new active instance. Params mac: Mac adress of the endpoint. attr_map: Custom attributes for the endpoint. """ global CISCO_ISE_ACTIVE_INSTANCE global GET_EP_ID_CMD cmd_mac_syntax_map = {"cisco-ise-get-endpoint-id-by-name": "mac_address", "cisco-ise-get-endpoint-id": "macAddress"} # Check if this mac address (endpoint) is present in ISE by attempting to get its ID resp = demisto.executeCommand(GET_EP_ID_CMD, {cmd_mac_syntax_map[GET_EP_ID_CMD]: mac, "using": CISCO_ISE_ACTIVE_INSTANCE}) if isError(resp[0]): err_msg = extract_ise_api_error(resp[0].get("Contents")) # 404 Not Found or empty results, we need to create a new EP if err_msg == "404" or err_msg == "list index out of range": create_new_ep(mac, attr_map, CISCO_ISE_ACTIVE_INSTANCE) # 405 - Method not allowed means we need to switch to an old filter based API elif err_msg == "405": GET_EP_ID_CMD = "cisco-ise-get-endpoint-id" # The primary went down (connection Error) or 401 if a fail over occurred (this primary/active # is not a secondary/standby device).We should attempt to get the new Primary/Active # instance is possible. elif err_msg == "Connection Error" or err_msg == "401": # Failover can take up to 10 minutes, its ok to just wait even if its a standalone ISE noe. msg = f"ISE instance is down. Trying again in 10 minutes. Error = {err_msg}" demisto.info(f"PANW_IOT_3RD_PARTY_BASE {msg}") send_status_to_panw_iot_cloud("error", msg) time.sleep(10 * 60) # Try again to get a new active instance new_active_instance, err_msg = get_active_ise_instance_or_error_msg() if new_active_instance is None: raise Exception(err_msg) else: CISCO_ISE_ACTIVE_INSTANCE = new_active_instance msg = f"Found new active ISE instance {CISCO_ISE_ACTIVE_INSTANCE}" send_status_to_panw_iot_cloud("success", msg) else: raise Exception(resp[0].get("Contents")) else: ep_id = resp[0]["EntryContext"]["Endpoint(val.ID === obj.ID)"]["ID"] update_existing_endpoint(mac, attr_map, ep_id, CISCO_ISE_ACTIVE_INSTANCE) def get_all_panw_iot_devices_and_send_to_cisco_ise(): """ Retrieves all devices from PANW IoT Cloud, 1000 devices at a time and sends it to the primary/active cisco ise. """ count = 0 offset = 0 page_size = 1000 unique_macs = set() while True: device_list = get_devices_from_panw_iot_cloud(offset, page_size) size = len(device_list) count += size for device in device_list: attrs = convert_device_map_to_cisco_ise_attributes(device) if attrs is not None: mac = attrs["mac"] attr_map = attrs["zb_attributes"] if mac not in unique_macs: create_or_update_ep(mac, attr_map) unique_macs.add(mac) time.sleep(0.5) if size == page_size: offset += page_size msg = f"Successfully exported {count} devices to Cisco ISE" send_status_to_panw_iot_cloud( "success", msg, ) else: break return ( f"Total {count} devices pulled from PANW IoT Cloud.\n" f"Exported {len(unique_macs)} devices (with available mac addresses) to Cisco ISE" ) def main(): try: status_msg = get_all_panw_iot_devices_and_send_to_cisco_ise() except Exception as ex: send_status_to_panw_iot_cloud("error", str(ex)) return_error(str(ex)) send_status_to_panw_iot_cloud("success", status_msg) return_results(status_msg) if __name__ in ("__main__", "__builtin__", "builtins"): main()
README
Retrieves all devices from PANW IoT Cloud and exports them as endpoints with custom attributes to Cisco ISE.
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
| Tags | PANW IoT 3rd Party Integration, Cisco ISE |
| Cortex XSOAR Version | 6.0.0 |
Used In
This script is used in the following playbooks and scripts.
- Bulk Export to Cisco ISE - PANW IoT 3rd Party Integration
Dependencies
This script uses the following commands and scripts.
- panw-iot-3rd-party-get-asset-list
- panw-iot-3rd-party-report-status-to-panw
- cisco-ise-get-endpoint-id
- cisco-ise-get-endpoint-id-by-name
- cisco-ise-create-endpoint
- cisco-ise-update-endpoint-custom-attribute
- GetCiscoISEActiveInstance
Inputs
| Argument Name | Description |
|---|---|
| active_ise_instance | Name of the Active Cisco ISE instance. |
| panw_iot_3rd_party_instance | Name of the configured PANW Iot 3rd Party instance. |
Outputs
There are no outputs for this script.