CreatePlbkDoc

Purpose: This automation will produce docx file detailing the tasks in the given playbook. It can produce a table or paragraph format of the report. Author: Mahmood Azmat Input1: Name of the playbook (Mandatory) Input2: Format type needed. Table or Paragraph. Paragraph is default. Input3: Name of the docx file that will be produced. Give the full name including the ".docx" extension. (Mandatory) Requirements: This automation requires "Core REST API" integration enabled and connected to the XSOAR itself. Automation uses it to read the objects of the playbook.

python · Community Common Scripts

Source

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401

# import json
from docx import Document
from docx.enum.section import WD_ORIENT  # pylint: disable=E0611
from docx.shared import Inches

# Purpose:      This automation will produce docx file detailing the tasks in the given playbook. It can
#               produce a table or paragraph format of the report.
#               The automation will create seprate tables (in case of Table format), or separate paragraphs
#               (in case of Paragraph format).
#               The definition of a section is path starting from a section header type of task, and ends
#               when it hits another section header in that path pf traversal.
# Author:       Mahmood Azmat
# Input1:       Name of the playbook (Mandatory)
# Input2:       Format type needed. Table or Paragraph. Paragraph is default.
# Input3:       Name of the docx file that will be produced. Give the full name including the ".docx"
#               extension. (Mandatory)
# Requirements: This automation requires "Core REST API" integration enabled and connected to the XSOAR itself.
#               Automation uses it to read the objects of the playbook.

retVal: dict = {}
DEMISTO_PLAYBOOKS_PATH = "/playbook/search"
document = Document()
SectionID = ""
Table = ""
Paragraph = ""
TaskIDsInLogic = []
TaskIDsInLogic.append("0")
OutPutType = ""
ConditionBranch = ""
PlaybookIndex = -1
NodesVisitedInThisSection = []
NodesVisitedInAllSections = {}
#  This is a 2 level associative array. First level is the ID of the section task and
#  second level has the IDs visited in that section


def post_api_request(url, body):
    api_args = {"uri": url, "body": body}

    raw_res = demisto.executeCommand("core-api-post", api_args)
    try:
        res = raw_res[0]["Contents"]["response"]
        return res
    except KeyError:
        return_error(f"API Request failed, no response from API call to {url}")
    except TypeError:
        return_error(f"API Request failed, failedto {raw_res}")


def EnterHeader(HeaderStr):
    document.add_heading(HeaderStr, level=1)


def StartParagraph(Name, Description):
    global Paragraph
    document.add_heading(Name, level=2)

    if Description == "[Blank]":
        Description = ""

    Paragraph = document.add_paragraph(Description)  # type: ignore[assignment]
    Paragraph.paragraph_format.left_indent = Inches(0.25)  # type: ignore


def StartTable():
    global Table
    Table = document.add_table(rows=1, cols=2)
    Table.style = "Light Grid Accent 1"  # type: ignore
    hdr_cells = Table.rows[0].cells  # type: ignore
    hdr_cells[0].text = "Name"
    hdr_cells[1].text = "Description"


def TraverseTasks(TaskID):
    global retVal, document, Table, Paragraph, NodesVisitedInAllSections, PlaybookIndex
    global OutPutType, ConditionBranch, NodesVisitedInThisSection, SectionID

    if TaskID in NodesVisitedInThisSection:
        # If this node has already been visited then we will skip it. This avoids putting the same task into the
        # document mutiple times.
        # You may have mutiple paths ending to the same task within the same section so
        # we do not want to make multiple entries of that.
        return

    else:
        NodesVisitedInThisSection.append(TaskID)
        NodesVisitedInAllSections[SectionID] = NodesVisitedInThisSection

    if TaskID is None:
        return

    else:
        curTask = retVal[PlaybookIndex]["tasks"][str(TaskID)]

        taskDescription = curTask["task"].get("description", "[Blank]")

        Description = taskDescription
        Type = curTask["type"]

        if str(TaskID) == "0":
            Name = "Playbook Triggered"

        else:
            Name = curTask["task"]["name"]

            if Type == "condition":
                Name = Name + " - Conditional Task "

            if Type == "playbook":
                Name = Name + " - Sub-playbook "

            if ConditionBranch != "":
                Name = Name + " (Condition branch->" + ConditionBranch + ")"

        if Type == "title" or Type == "start":
            if Type == "title":
                if curTask.get("nextTasks") is not None:
                    EnterHeader(Name)
                    if OutPutType == "TABLE":
                        StartTable()

            else:
                EnterHeader(Name)
                if OutPutType == "TABLE":
                    StartTable()
        else:
            if OutPutType == "TABLE":
                if Table == "":
                    StartTable()

                row_cells = Table.add_row().cells  # type: ignore
                row_cells[0].text = Name
                row_cells[1].text = Description

            else:
                StartParagraph(Name, Description)

        if curTask.get("nextTasks") is not None:
            if curTask["type"] != "condition":
                for NextTaskID in curTask["nextTasks"]["#none#"]:
                    TypeOfNextTask = retVal[PlaybookIndex]["tasks"][str(NextTaskID)]["task"]["type"]
                    NameOfNextTask = retVal[PlaybookIndex]["tasks"][str(NextTaskID)]["task"]["name"]

                    if TypeOfNextTask == "title":
                        NameOfNextTask = NameOfNextTask + " - Section "

                    if ConditionBranch != "":
                        NameOfNextTask = NameOfNextTask + " (Condition branch->" + ConditionBranch + ")"

                    if TypeOfNextTask != "title":
                        TraverseTasks(NextTaskID)

                    else:
                        if OutPutType == "TABLE":
                            row_cells = Table.add_row().cells  # type: ignore
                            row_cells[0].text = NameOfNextTask
                        else:
                            StartParagraph(NameOfNextTask, "")

                        if str(NextTaskID) not in TaskIDsInLogic:
                            TaskIDsInLogic.append(str(NextTaskID))
            else:
                for TempKeys in curTask.get("nextTasks"):
                    for NextTaskID in curTask["nextTasks"][TempKeys]:
                        TypeOfNextTask = retVal[PlaybookIndex]["tasks"][str(NextTaskID)]["task"]["type"]
                        NameOfNextTask = retVal[PlaybookIndex]["tasks"][str(NextTaskID)]["task"]["name"]

                        ConditionBranch = TempKeys

                        if TypeOfNextTask == "title":
                            NameOfNextTask = NameOfNextTask + " - Section "

                        if ConditionBranch == "#default#":
                            ConditionBranch = "Else"

                        NameOfNextTask = NameOfNextTask + " (Condition branch->" + ConditionBranch + ")"

                        if TypeOfNextTask != "title":
                            TraverseTasks(NextTaskID)

                        else:
                            if OutPutType == "TABLE":
                                row_cells = Table.add_row().cells  # type: ignore
                                row_cells[0].text = NameOfNextTask
                            else:
                                StartParagraph(NameOfNextTask, "")

                            if str(NextTaskID) not in TaskIDsInLogic:
                                TaskIDsInLogic.append(str(NextTaskID))

                ConditionBranch = ""

    return


""" MAIN FUNCTION """


def main():
    # print ("Test")
    global retVal, TaskIDsInLogic, NodesVisitedInThisSection, PlaybookIndex
    global document, SectionID
    global OutPutType
    global ConditionBranch
    args = demisto.args()
    DocFileName = args.get("DocFileName")
    PlaybookName = args.get("PlaybookName")
    OutPutType = args.get("Output_Format")
    OutPutType = str(OutPutType).upper()

    if OutPutType == "":
        OutPutType = "PARAGRAPH"

    TempStr = PlaybookName + "- Playbook"

    if OutPutType == "TABLE":
        sections = document.sections
        section = sections[-1]

        # Swapping width with height to make the table landscape format
        old_width = section.page_width
        old_height = section.page_height
        section.orientation = WD_ORIENT.LANDSCAPE
        section.page_width = old_height
        section.page_height = old_width

    document.add_heading(TempStr, 0)
    retVal = post_api_request(DEMISTO_PLAYBOOKS_PATH, {"query": PlaybookName}).get("playbooks")

    if retVal is None:
        demisto.results("Playbook not found. Please check the Playbook name.")
        return

    i = 0
    j = 0

    while j < len(retVal):
        PlaybookNameTempStr = retVal[j].get("nameRaw")

        if PlaybookNameTempStr == PlaybookName:
            PlaybookIndex = j

        j = j + 1

    if PlaybookIndex == -1:
        demisto.results("Playbook not found. Please check the Playbook name.")
        return

    while TaskIDsInLogic[i] is not None:
        ConditionBranch = ""
        NodesVisitedInThisSection.clear()
        SectionID = TaskIDsInLogic[i]
        TraverseTasks(str(TaskIDsInLogic[i]))
        i = i + 1

        try:
            assert TaskIDsInLogic[i]

        except Exception:
            break

    if OutPutType == "TABLE":
        for tb in document.tables:
            for cell in tb.columns[0].cells:
                cell.width = Inches(3)

            for cell in tb.columns[1].cells:
                cell.width = Inches(6)

    document.save(DocFileName)

    with open(DocFileName, "rb") as f:
        filedata = f.read()

    demisto.results(fileResult(DocFileName, filedata))
    return


""" ENTRY POINT """

if __name__ in ("__main__", "__builtin__", "builtins"):
    main()

README

Purpose: This automation will produce docx file detailing the tasks in the given playbook. It can produce a table or paragraph format of the report.

Author: Mahmood Azmat

Input1: Name of the playbook (Mandatory)
Input2: Format type needed. Table or Paragraph. Paragraph is default.
Input3: Name of the docx file that will be produced. Give the full name including the “.docx” extension. (Mandatory)

Requirements: This automation requires “Core REST API” integration enabled and connected to the XSOAR itself. Automation uses it to read the objects of the playbook.

Script Data


Name Description
Script Type python3
Tags General, Utility

Inputs


Argument Name Description
DocFileName  
PlaybookName  
Output_Format  

Outputs


There are no outputs for this script.

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.