RetrievePlaybookDependencies
Retrieves all Playbook (and Sub-Playbook) Names, Integrations, Automation Scripts, Commands not using-brand, and lists for a provided Playbook name. Also accepts inputs for incident types, layouts, incident fields, indicator fields, jobs, mappers, and pre-process rules connected to the parent playbook. Results can be output as an HTML or Markdown list.
python · Community Common Scripts
Source
import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 from typing import Any import traceback import re from json import dumps """ STANDALONE FUNCTION """ def perform_rest_call(method: str, endpoint: str, body=None): """ Perform a REST Call :param endpoint: :param body: `dict` representing the HTTP body :param method: `get` or `post` :return: The contents from the API Call """ args = { "uri": endpoint, } if body: args["body"] = dumps(body) result = demisto.executeCommand(f"core-api-{method}", args) if len(result) < 1 or "Contents" not in result[0] or "response" not in result[0].get("Contents"): raise Exception(f"Error with REST call to endpoint {endpoint}") return result[0]["Contents"]["response"] def get_tasks_list(tasks): # {'1':{'id': '1'}, ...} -> [{'id':'1'}, ...] tasks_list = [] for task_number in tasks: tasks_list.append(tasks[task_number]) return tasks_list def append_commands(commands, subplaybook_name, subplaybook_json): for t in get_tasks_list(subplaybook_json): # commands not using-brand task = t.get("task", {}) if task.get("isCommand") and task.get("scriptId", "").startswith("|"): key = task.get("scriptId", "").replace("|||", "") # These are base commands and should be excluded from brandless list if key not in ["domain", "file", "ip", "url"]: if key in commands: if subplaybook_name not in commands[key]: commands[key].append(subplaybook_name) else: commands.update({key: [subplaybook_name]}) return commands def append_to_playbooks_and_integrations(playbooks, integrations, script_ids, commands, playbook_json): playbook_name = playbook_json.get("name") if playbook_name and playbook_name not in playbooks: playbooks.append(playbook_name) for b in argToList(playbook_json.get("brands")): if b and b != "Builtin" and b not in integrations: integrations.append(b) for script_id in playbook_json.get("scriptIds"): script_ids.append(script_id) commands = append_commands(commands, playbook_name, playbook_json.get("tasks")) return playbooks, integrations, script_ids, commands def get_subplaybook_tasks(playbooks, integrations, script_ids, commands, lists, task): # recursively go through all subplaybook tasks and append to playbooks and integrations _task = task.get("task", {}) try: subplaybook_json = perform_rest_call("get", f"playbook/{_task.get('playbookId')}") except Exception: raise Exception(f"Playbook {_task.get('name')} not found") playbooks, integrations, script_ids, commands = append_to_playbooks_and_integrations( playbooks, integrations, script_ids, commands, subplaybook_json ) for t in get_tasks_list(subplaybook_json.get("tasks")): if t.get("type") == "regular": lists = get_xsoar_list_name(t, lists) elif t.get("type") == "playbook" and t.get("task", {}).get("playbookId"): # playbookId does not exist if the playbook the task references is missing playbooks, integrations, script_ids, commands, lists = get_subplaybook_tasks( playbooks, integrations, script_ids, commands, lists, t ) return playbooks, integrations, script_ids, commands, lists def create_markdown_list( incident_types, layouts, incident_fields, indicator_fields, jobs, lists, mappers, pre_process_rules, scripts, integrations, playbooks, parent_playbook, ): markdown_string = "## XSOAR Objects\n" markdown_string += ( "* Incident Types\n * None\n" if len(incident_types) == 0 else "* Incident Types\n * " + "\n * ".join(incident_types) + "\n" ) markdown_string += "* Layouts\n * None\n" if len(layouts) == 0 else "* Layouts\n * " + "\n * ".join(layouts) + "\n" markdown_string += ( "* Incident Fields\n * None\n" if len(incident_fields) == 0 else "* Incident Fields\n * " + "\n * ".join(incident_fields) + "\n" ) markdown_string += ( "* Indicator Fields\n * None\n" if len(indicator_fields) == 0 else "* Indicator Fields\n * " + "\n * ".join(indicator_fields) + "\n" ) markdown_string += "* Jobs\n * None\n" if len(jobs) == 0 else "* Jobs\n * " + "\n * ".join(jobs) + "\n" markdown_string += "* Lists\n * None\n" if len(lists) == 0 else "* Lists\n * " + "\n * ".join(lists) + "\n" markdown_string += "* Mappers\n * None\n" if len(mappers) == 0 else "* Mappers\n * " + "\n * ".join(mappers) + "\n" markdown_string += ( "* Pre-Process Rules\n * None\n" if len(pre_process_rules) == 0 else "* Pre-Process Rules\n * " + "\n * ".join(pre_process_rules) + "\n" ) markdown_string += "* Parent Playbook: " + parent_playbook + "\n" markdown_string += ( "* Custom Automations\n * None\n" if len(scripts) == 0 else "* Custom Automations\n * " + "\n * ".join(scripts) + "\n" ) markdown_string += ( "* Integrations\n * None\n" if len(integrations) == 0 else "* Integrations\n * " + "\n * ".join(integrations) + "\n" ) markdown_string += "* Playbooks\n * None\n" if len(playbooks) == 0 else "* Playbooks\n * " + "\n * ".join(playbooks) + "\n" return markdown_string def create_html_list( incident_types, layouts, incident_fields, indicator_fields, jobs, lists, mappers, pre_process_rules, scripts, integrations, playbooks, parent_playbook, ): html_string = "<h2>XSOAR Objects</h2><ul>" html_string += ( "<li>Incident Types</li><ul><li>None</li></ul>" if len(incident_types) == 0 else "<li>Incident Types</li><ul><li>" + "</li><li>".join(incident_types) + "</li></ul>" ) html_string += ( "<li>Layouts</li><ul><li>None</li></ul>" if len(layouts) == 0 else "<li>Layouts</li><ul><li>" + "</li><li>".join(layouts) + "</li></ul>" ) html_string += ( "<li>Incident Fields</li><ul><li>None</li></ul>" if len(incident_fields) == 0 else "<li>Incident Fields</li><ul><li>" + "</li><li>".join(incident_fields) + "</li></ul>" ) html_string += ( "<li>Indicator Fields</li><ul><li>None</li></ul>" if len(indicator_fields) == 0 else "<li>Indicator Fields</li><ul><li>" + "</li><li>".join(indicator_fields) + "</li></ul>" ) html_string += ( "<li>Jobs</li><ul><li>None</li></ul>" if len(jobs) == 0 else "<li>Jobs</li><ul><li>" + "</li><li>".join(jobs) + "</li></ul>" ) html_string += ( "<li>Lists</li><ul><li>None</li></ul>" if len(lists) == 0 else "<li>Lists</li><ul><li>" + "</li><li>".join(lists) + "</li></ul>" ) html_string += ( "<li>Mappers</li><ul><li>None</li></ul>" if len(mappers) == 0 else "<li>Mappers</li><ul><li>" + "</li><li>".join(mappers) + "</li></ul>" ) html_string += ( "<li>Pre-Process Rules</li><ul><li>None</li></ul>" if len(pre_process_rules) == 0 else "<li>Pre-Process Rules</li><ul><li>" + "</li><li>".join(pre_process_rules) + "</li></ul>" ) html_string += "<li>Parent Playbook: " + parent_playbook + "</li>" html_string += ( "<li>Custom Automations</li><ul><li>None</li></ul>" if len(scripts) == 0 else "<li>Custom Automations</li><ul><li>" + "</li><li>".join(scripts) + "</li></ul>" ) html_string += ( "<li>Integrations</li><ul><li>None</li></ul>" if len(integrations) == 0 else "<li>Integrations</li><ul><li>" + "</li><li>".join(integrations) + "</li></ul>" ) html_string += ( "<li>Playbooks</li><ul><li>None</li></ul>" if len(playbooks) == 0 else "<li>Playbooks</li><ul><li>" + "</li><li>".join(playbooks) + "</li></ul></ul>" ) return html_string def get_xsoar_list_name(task, lists): # Search for lists in tasks if "scriptArguments" in task: script_arguments = task.get("scriptArguments") # Check if the complex argument is a list try: if ( script_arguments["value"]["complex"]["accessor"] not in lists and script_arguments["value"]["complex"]["root"] == "lists" ): lists.append(script_arguments["value"]["complex"]["accessor"]) except KeyError: pass r = re.findall(r"['\{]lists\.(.*?)?[.'\}]", str(script_arguments)) if r: for list_name in r: if list_name not in lists: lists.append(list_name) return lists """ COMMAND FUNCTION """ def retrieve_playbook_dependencies(args: dict[str, Any]) -> CommandResults: playbooks: list[str] = [] integrations: list[str] = [] script_ids: list[str] = [] commands: dict[str, Any] = {} # commands not using brand lists: list[str] = [] # XSOAR List names parent_playbook = args.get("playbook_name") or "" # Call parent playbook's data, then recursivley call all subplaybooks' data playbooks_json = perform_rest_call("post", "playbook/search", {"query": f'''name:"{parent_playbook}"'''}) match_found = False if playbooks_json.get("playbooks"): for playbook_json in playbooks_json.get("playbooks"): if playbook_json.get("name") == parent_playbook: match_found = True break if not match_found: raise Exception(f"""Playbook '{parent_playbook}' not found""") playbooks, integrations, script_ids, commands = append_to_playbooks_and_integrations( playbooks, integrations, script_ids, commands, playbook_json ) for task in get_tasks_list(playbook_json.get("tasks")): if task.get("type") == "regular": lists = get_xsoar_list_name(task, lists) elif task.get("type") == "playbook": playbooks, integrations, script_ids, commands, lists = get_subplaybook_tasks( playbooks, integrations, script_ids, commands, lists, task ) # Sort scripts into base scripts and custom scripts, and get the displayname for custom scripts script_ids = list(set(script_ids)) custom_scripts: list[str] = [] base_scripts: list[str] = [] for script_id in script_ids: if "-" in script_id: custom_scripts.append(perform_rest_call("post", f"automation/load/{script_id}").get("name")) else: base_scripts.append(perform_rest_call("post", f"automation/load/{script_id}").get("name")) # Dedup integrations = list(set(integrations)) playbooks = list(set(playbooks)) # Format results for output base_scripts.sort() custom_scripts.sort() integrations.sort() lists.sort() playbooks.sort() # Sort, format and display brandless commands' possible integrations and the playbooks they were located in if len(commands) > 0: integration_result = perform_rest_call("get", "settings/integration-commands") integration_commands: dict[str, list[str]] = {} # Find the integrations connected to brand-less commands for integration in integration_result: for command in integration.get("commands", []): if command.get("name") and command.get("name") in commands: integration_commands.setdefault(integration.get("display"), []).append(command.get("name")) if len(integration_commands) > 0: # Format into markdown table displaying integration, command, and playbook it was found in integration_commands_str = "## Warning\n\n" integration_commands_str += "Commands found with no clear integration connected to them. " integration_commands_str += "These integrations have been included in the main markdown list uncritically, " integration_commands_str += "but are listed below in case revisions are needed:\n" integration_commands_str += "Recommended Action: Locate command calls in playbooks and confirm tasks are using " integration_commands_str += "branded commands. EXAMPLE:\n\n" integration_commands_str += " UNBRANDED: 'command_name'\n BRANDED: 'command_name (Integration Name)'\n" integration_commands_str += "| Integration | Brandless Command | Located in Playbook(s) |\n" integration_commands_str += "|---|---|---|" for key in integration_commands: integrations.append(key) for command in integration_commands.get(key, []): command_playbooks = (", ").join(commands.get(command)) integration_commands_str += f"\n| {key} | {command} | {command_playbooks} |" return_results(CommandResults(readable_output=integration_commands_str)) # Create final markdown and html for documentation # Possible opportunities for automating these as well incident_types = argToList(args.get("incident_types")) if args.get("incident_types") else [] layouts = argToList(args.get("layouts")) if args.get("layouts") else [] incident_fields = argToList(args.get("incident_fields")) if args.get("incident_fields") else [] indicator_fields = argToList(args.get("indicator_fields")) if args.get("indicator_fields") else [] jobs = argToList(args.get("jobs")) if args.get("jobs") else [] mappers = argToList(args.get("mappers")) if args.get("mappers") else [] pre_process_rules = argToList(args.get("pre_process_rules")) if args.get("pre_process_rules") else [] markdown_string = create_markdown_list( incident_types, layouts, incident_fields, indicator_fields, jobs, lists, mappers, pre_process_rules, custom_scripts, integrations, playbooks, parent_playbook, ) if args.get("Markdown") == "true": demisto.results(markdown_string) html_string = create_html_list( incident_types, layouts, incident_fields, indicator_fields, jobs, lists, mappers, pre_process_rules, custom_scripts, integrations, playbooks, parent_playbook, ) if args.get("HTML") == "true": demisto.results(html_string) dependencies = { "Parent Playbook": args.get("playbook_name"), "Playbooks": playbooks, "Integrations": integrations, "Automations": {"CustomScripts": custom_scripts, "BaseScripts": base_scripts}, "Commands": commands, "Lists": lists, "MarkdownString": markdown_string, "HTMLString": html_string, } parent_playbook = parent_playbook.replace(" ", "_") outputs = {f"{parent_playbook}": dependencies} return CommandResults( readable_output=f'''Retrieved Dependencies for Playbook "{parent_playbook}"''', outputs_prefix="RetrievePlaybookDependencies", outputs_key_field="", outputs=outputs, ) """ MAIN FUNCTION """ def main(): try: return_results(retrieve_playbook_dependencies(demisto.args())) except Exception as ex: demisto.error(traceback.format_exc()) return_error(f"Failed to execute RetrievePlaybookDependencies. Error: {str(ex)}") """ ENTRY POINT """ if __name__ in ("__main__", "__builtin__", "builtins"): main()
README
Retrieves all Playbook (and Sub-Playbook) names, Integrations, Automation Scripts, Commands and Lists for a provided Playbook name.
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
Inputs
| Argument Name | Description |
|---|---|
| playbook_name | Name of Playbook |
Outputs
| Path | Description | Type |
|---|---|---|
| RetrievePlaybookDependencies.Parent Playbook | Name of parent playbook provided. | Unknown |
| RetrievePlaybookDependencies.Playbooks | Names of all Playbooks and Sub-Playbooks used. | Unknown |
| RetrievePlaybookDependencies.Integrations | Names of all Integrations used. | Unknown |
| RetrievePlaybookDependencies.Automations | Names of all Automation Scripts used. | Unknown |
| RetrievePlaybookDependencies.Commands | Names of all brandless Commands used. | Unknown |
| RetrievePlaybookDependencies.Lists | Names of all Lists used. | Unknown |
| RetrievePlaybookDependencies.MarkdownString | Markdown formatted string data of playbook dependencies. | Unknown |
Troubleshooting
Multi-tenant environments should be configured with the Cortex Rest API instance when using this
automation. Make sure the Use tenant parameter (in the Cortex Rest API integration) is checked
to ensure that API calls are made to the current tenant instead of the master tenant.