Exemplo n.º 1
0
    def _fn_snow_helper_add_task_note_function(self, event, *args, **kwargs):
        """Function: A helper function to add a Note to a Task from a Workflow with a different parent object type"""

        log = logging.getLogger(__name__)

        try:

            # Instansiate helper (which gets appconfigs from file)
            res_helper = ResilientHelper(self.options)

            # Get the function inputs:
            inputs = {
                "sn_res_id":
                res_helper.get_function_input(kwargs,
                                              "sn_res_id"),  # text (required)
                "sn_note_text":
                res_helper.get_function_input(
                    kwargs, "sn_note_text")  # number (required)
            }

            # Create payload dict with inputs
            payload = FunctionPayload(inputs)

            yield StatusMessage("Function Inputs OK")

            # Instansiate new Resilient API object
            res_client = self.rest_client()

            # Parse incident_id and task_id from sn_res_id
            ids = res_helper.parse_res_id(payload.inputs["sn_res_id"])

            # url for POST
            url = "/tasks/{0}/comments".format(ids.get("task_id"))

            # Create data for POST
            request_data = {
                "text": {
                    "format": "html",
                    "content": payload.inputs["sn_note_text"]
                }
            }

            yield StatusMessage("Adding Task Note to {0}".format(
                payload.inputs["sn_res_id"]))

            # POST to Resilient API, add the Note
            res_client.post(url, request_data)

            # Set results
            results = payload.as_dict()

            log.info("Complete")

            # Produce a FunctionResult with the results
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
    def _fn_snow_helper_update_datatable_function(self, event, *args, **kwargs):
        """Function: A helper function that updates the ServiceNow Records Data Table when the status of an Incident/Task is changed."""

        log = logging.getLogger(__name__)

        try:
            # Instansiate helper (which gets appconfigs from file)
            res_helper = ResilientHelper(self.options)

            # Get the function inputs:
            inputs = {
                "incident_id": res_helper.get_function_input(kwargs, "incident_id"),  # number (required)
                "task_id": res_helper.get_function_input(kwargs, "task_id", True),  # number (optional)
                "sn_resilient_status": res_helper.get_function_input(kwargs, "sn_resilient_status"),  # text (required)
            }

            # Create payload dict with inputs
            payload = FunctionPayload(inputs)

            yield StatusMessage("Function Inputs OK")

            # Instansiate new Resilient API object
            res_client = self.rest_client()

            # Instansiate a reference to the ServiceNow Datatable
            res_datatable = ServiceNowRecordsDataTable(res_client, payload.inputs["incident_id"])

            # Get the datatable data and rows
            res_datatable.get_data()

            # Generate the res_id
            payload.res_id = res_helper.generate_res_id(payload.inputs["incident_id"], payload.inputs["task_id"])

            # Search for a row that contains the res_id
            row_found = res_datatable.get_row("sn_records_dt_res_id", payload.res_id)

            # Get current time (*1000 as API does not accept int)
            now = int(time.time() * 1000)

            if row_found:

                resilient_status = res_helper.state_to_text(payload.inputs.get("sn_resilient_status"))

                yield StatusMessage("Row found for {0}. Updating resilient_status to {1}".format(
                    payload.res_id, resilient_status))

                if resilient_status == "Active":
                    resilient_status = res_helper.convert_text_to_richtext("Active", "green")

                else:
                    resilient_status = res_helper.convert_text_to_richtext("Closed", "red")

                cells_to_update = {
                    "sn_records_dt_time": now,
                    "sn_records_dt_res_status": resilient_status
                }

                # Update the row
                update_row_response = res_datatable.update_row(row_found, cells_to_update)
                payload.row_id = update_row_response["id"]

            else:
                payload.success = False
                err_msg = "No row found for the {0} {1}"

                if payload.inputs["task_id"]:
                    err_msg = err_msg.format("Task", payload.inputs["task_id"])

                else:
                    err_msg = err_msg.format("Incident", payload.inputs["incident_id"])

                yield StatusMessage(err_msg)

            results = payload.as_dict()

            log.debug("RESULTS: %s", results)
            log.info("Complete")

            # Produce a FunctionResult with the results
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
Exemplo n.º 3
0
    def _fn_snow_add_note_to_record_function(self, event, *args, **kwargs):
        """Function: Function that adds a Note to a ServiceNow Record. Option to add the note as a 'Work Note' or 'Additional Comment'."""

        log = logging.getLogger(__name__)

        try:
            # Instansiate helper (which gets appconfigs from file)
            res_helper = ResilientHelper(self.options)

            # Get the function inputs:
            inputs = {
                "incident_id": res_helper.get_function_input(kwargs, "incident_id"),  # number (required)
                "task_id": res_helper.get_function_input(kwargs, "task_id", True),  # number
                "sn_note_text": res_helper.get_function_input(kwargs, "sn_note_text"),  # text (required)
                "sn_note_type": res_helper.get_function_input(kwargs, "sn_note_type")["name"]  # select, text (required)
            }

            # Convert rich text comment to plain text
            soup = BeautifulSoup(inputs["sn_note_text"], 'html.parser')
            soup = soup.get_text()
            inputs["sn_note_text"] = soup.replace(u'\xa0', u' ')

            # Create payload dict with inputs
            payload = FunctionPayload(inputs)

            yield StatusMessage("Function Inputs OK")

            # Instansiate new Resilient API object
            res_client = self.rest_client()

            # Get the datatable
            datatable = ServiceNowRecordsDataTable(res_client, payload.inputs["incident_id"])

            # Generate res_id using incident and task id
            res_id = res_helper.generate_res_id(payload.inputs["incident_id"], payload.inputs["task_id"])

            # Get the sn_ref_id
            sn_ref_id = datatable.get_sn_ref_id(res_id)

            if not sn_ref_id:
                payload.success = False
                err_msg = "Failed to add Note. This {0} has not been created in ServiceNow yet. {0} ID: {1}"

                if payload.inputs["task_id"]:
                    err_msg = err_msg.format("Task", payload.inputs["task_id"])

                else:
                    err_msg = err_msg.format("Incident", payload.inputs["incident_id"])

                raise ValueError(err_msg)

            else:
                # Generate the request_data
                request_data = {
                    "sn_ref_id": sn_ref_id,
                    "sn_table_name": res_helper.SN_TABLE_NAME,
                    "type": "comment",
                    "sn_note_text": payload.inputs["sn_note_text"],
                    "sn_note_type": payload.inputs["sn_note_type"]
                }

                yield StatusMessage("Adding Note to ServiceNow Record {0}".format(sn_ref_id))

                # Call POST and get response
                add_in_sn_response = res_helper.sn_api_request("POST", "/add", data=json.dumps(request_data))
                payload.res_id = res_id
                payload.sn_ref_id = add_in_sn_response["sn_ref_id"]

            results = payload.as_dict()
            log.debug("RESULTS: %s", results)

            log.info("Complete")

            # Produce a FunctionResult with the results
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
    def _fn_snow_lookup_sysid_function(self, event, *args, **kwargs):
        """Function: Function that gets the 'sys_id' of a ServiceNow Record."""

        log = logging.getLogger(__name__)

        try:
            # Instansiate helper (which gets appconfigs from file)
            res_helper = ResilientHelper(self.options)

            # Get the function inputs:
            inputs = {
                "sn_query_field":
                res_helper.get_function_input(
                    kwargs, "sn_query_field"),  # text (required)
                "sn_table_name":
                res_helper.get_function_input(
                    kwargs, "sn_table_name"),  # text (required)
                "sn_query_value":
                res_helper.get_function_input(
                    kwargs, "sn_query_value")  # text (required)
            }

            # Create payload dict with inputs
            payload = FunctionPayload(inputs)

            yield StatusMessage("Function Inputs OK")

            yield StatusMessage(
                "Querying ServiceNow for a sys_id. table: {0} field: {1} value: {2}"
                .format(payload.inputs.get("sn_table_name"),
                        payload.inputs.get("sn_query_field"),
                        payload.inputs.get("sn_query_value")))

            # Call custom endpoint '/get_sys_id' with 3 params
            get_sys_id_response = res_helper.sn_api_request(
                "GET", "/get_sys_id", params=payload.inputs)

            # Get response text
            response_result = get_sys_id_response.text

            # Check if result is there
            if response_result is not None and "result" in response_result:
                response_result = json.loads(response_result)

                # Check if sys_id is defined
                if response_result["result"]["sys_id"]:
                    payload.success = True
                    payload.sys_id = response_result["result"]["sys_id"]
                    yield StatusMessage("sys_id found: {0}".format(
                        payload.sys_id))
                else:
                    yield StatusMessage("No sys_id found")

            # Handle error messages
            elif response_result is not None and "error" in response_result:
                response_result = json.loads(response_result)
                err_msg = response_result["error"]["message"]
                if "invalid table name" in err_msg:
                    err_msg = '"{0}" is an invalid ServiceNow table name'.format(
                        payload.inputs["sn_table_name"])
                raise ValueError(err_msg)

            # Set results to the payload
            results = payload.as_dict()

            log.debug("RESULTS: %s", results)
            log.info("Complete")

            # Produce a FunctionResult with the results
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
    def _fn_snow_create_record_function(self, event, *args, **kwargs):
        """Function: Function that uses the '/create' custom endpoint in ServiceNow to create a ServiceNow record from an IBM Resilient Incident or Task"""

        log = logging.getLogger(__name__)

        try:
            # Instansiate helper (which gets appconfigs from file)
            res_helper = ResilientHelper(self.options)

            # Get the function inputs:
            inputs = {
                "incident_id": res_helper.get_function_input(kwargs, "incident_id"),  # number (required)
                "task_id": res_helper.get_function_input(kwargs, "task_id", True),  # number (optional)
                "sn_init_work_note": res_helper.get_function_input(kwargs, "sn_init_work_note", True),  # text (optional)
                "sn_optional_fields": res_helper.get_function_input(kwargs, "sn_optional_fields", True)  # text, JSON String (optional)
            }

            # Convert 'sn_optional_fields' JSON string to Dictionary
            try:
                inputs["sn_optional_fields"] = json.loads(inputs["sn_optional_fields"], object_hook=res_helper._byteify)
            except Exception:
                raise ValueError("sn_optional_fields JSON String is invalid")

            # Create payload dict with inputs
            payload = FunctionPayload(inputs)

            yield StatusMessage("Function Inputs OK")

            # Instansiate new Resilient API object
            res_client = self.rest_client()

            # Instansiate a reference to the ServiceNow Datatable
            datatable = ServiceNowRecordsDataTable(res_client, payload.inputs["incident_id"])

            # Generate the res_link
            payload.res_link = res_helper.generate_res_link(payload.inputs["incident_id"], self.host, payload.inputs["task_id"])

            # Generate the request_data
            req = res_helper.generate_sn_request_data(
                res_client,
                datatable,
                payload.inputs["incident_id"],
                res_helper.SN_TABLE_NAME,
                payload.res_link,
                payload.inputs["task_id"],
                payload.inputs["sn_init_work_note"],
                payload.inputs["sn_optional_fields"])

            # If we fail to generate the request_data (because the ServiceNow record already exists) 
            # raise an Action Status message and set success to False
            if not req.get("success"):
                err_msg = req.get("data")
                yield StatusMessage(err_msg)
                payload.reason = err_msg
                payload.success = False

            else:
                request_data = req.get("data")

                yield StatusMessage(u"Creating a new ServiceNow Record for the {0}: {1}".format(
                    "Incident" if request_data.get("type") is "res_incident" else "Task", res_helper.str_to_unicode(request_data.get("incident_name")) if request_data.get("incident_name") is not None else res_helper.str_to_unicode(request_data.get("task_name"))))

                # Call POST and get response
                create_in_sn_response = res_helper.sn_api_request("POST", "/create", data=json.dumps(request_data))

                if create_in_sn_response is not None:

                    # Add values to payload
                    payload.res_id = create_in_sn_response["res_id"]
                    payload.sn_ref_id = create_in_sn_response["sn_ref_id"]
                    payload.sn_sys_id = create_in_sn_response["sn_sys_id"]
                    payload.sn_record_state = create_in_sn_response["sn_state"]
                    payload.sn_record_link = res_helper.generate_sn_link("number={0}".format(payload.sn_ref_id))
                    payload.sn_time_created = int(time.time() * 1000)  # Get current time (*1000 as API does not accept int)

                    yield StatusMessage("New ServiceNow Record created {0}".format(payload.sn_ref_id))

                    try:
                        yield StatusMessage("Adding a new row to the ServiceNow Records Data Table")

                        # Add row to the datatable
                        add_row_response = datatable.add_row(
                            payload.sn_time_created,
                            request_data.get("incident_name") if request_data.get("incident_name") is not None else request_data.get("task_name"),
                            "Incident" if request_data.get("type") is "res_incident" else "Task",
                            payload.res_id,
                            payload.sn_ref_id,
                            res_helper.convert_text_to_richtext("Active"),
                            res_helper.convert_text_to_richtext("Sent to ServiceNow"),
                            """<a href="{0}">RES</a> <a href="{1}">SN</a>""".format(payload.res_link, payload.sn_record_link))

                        payload.row_id = add_row_response["id"]

                    except Exception as err_msg:
                        payload.success = False
                        raise ValueError("Failed to add row to datatable {0}".format(err_msg))

                else:
                    payload.success = False
                    raise ValueError("The response from ServiceNow was empty")

            results = payload.as_dict()

            log.debug("RESULTS: %s", results)
            log.info("Complete")

            # Produce a FunctionResult with the results
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
    def _fn_snow_close_record_function(self, event, *args, **kwargs):
        """Function: Function that uses the '/close_record' custom endpoint in ServiceNow to change the state of a ServiceNow Record and add Close Notes and a Close Code to the Record."""

        err_msg = None

        log = logging.getLogger(__name__)

        try:
            # Instansiate helper (which gets appconfigs from file)
            res_helper = ResilientHelper(self.options)

            # Get the function inputs:
            inputs = {
                "incident_id": res_helper.get_function_input(kwargs, "incident_id"),  # number (required)
                "task_id": res_helper.get_function_input(kwargs, "task_id", True),  # number (optional)
                "sn_res_id": res_helper.get_function_input(kwargs, "sn_res_id", True),  # number (optional)
                "sn_record_state": res_helper.get_function_input(kwargs, "sn_record_state"),  # number (required)
                "sn_close_notes": res_helper.get_function_input(kwargs, "sn_close_notes"),  # text (required)
                "sn_close_code": res_helper.get_function_input(kwargs, "sn_close_code"),  # text (required)
                "sn_close_work_note": res_helper.get_function_input(kwargs, "sn_close_work_note", True),  # text (optional)
            }

            # Create payload dict with inputs
            payload = FunctionPayload(inputs)

            yield StatusMessage("Function Inputs OK")

            # Instansiate new Resilient API object
            res_client = self.rest_client()

            # Get the datatable and its data
            datatable = ServiceNowRecordsDataTable(res_client, payload.inputs["incident_id"])

            # Generate the res_id
            res_id = res_helper.generate_res_id(payload.inputs["incident_id"], payload.inputs["task_id"], payload.inputs["sn_res_id"])

            # Get the sn_ref_id
            sn_ref_id = datatable.get_sn_ref_id(res_id)

            if not sn_ref_id:
                err_msg = "Failed to close this {0} in ServiceNow. This {0} has not been created in ServiceNow yet. {0} ID: {1}"

                if payload.inputs["task_id"]:
                    err_msg = err_msg.format("Task", payload.inputs["task_id"])

                else:
                    err_msg = err_msg.format("Incident", payload.inputs["incident_id"])

                payload.success = False
                payload.reason = err_msg
                yield StatusMessage(err_msg)

            else:
                # Generate the request_data
                request_data = {
                    "sn_ref_id": sn_ref_id,
                    "sn_table_name": res_helper.SN_TABLE_NAME,
                    "sn_close_code": payload.inputs["sn_close_code"],
                    "sn_close_notes": payload.inputs["sn_close_notes"],
                    "sn_record_state": payload.inputs["sn_record_state"],
                    "sn_close_work_note": payload.inputs["sn_close_work_note"]
                }

                try:
                    yield StatusMessage("Closing ServiceNow Record {0}".format(sn_ref_id))
                    close_in_sn_response = res_helper.sn_api_request("POST", "/close_record", data=json.dumps(request_data))
                    payload.sn_ref_id = sn_ref_id
                    payload.sn_record_state = close_in_sn_response["sn_state"]

                    try:
                        yield StatusMessage("Updating ServiceNow Records Data Table Status to {0}".format(close_in_sn_response["sn_state"]))

                        row_to_update = datatable.get_row("sn_records_dt_sn_ref_id", sn_ref_id)

                        cells_to_update = {
                            "sn_records_dt_time": int(time.time() * 1000),
                            "sn_records_dt_snow_status": res_helper.convert_text_to_richtext(close_in_sn_response["sn_state"], "red")
                        }

                        # Update the row
                        datatable.update_row(row_to_update, cells_to_update)

                    except Exception as err:
                        payload.success = False
                        raise ValueError("Failed to update ServiceNow Status in Datatable: {0}".format(err))

                except Exception as err:
                    err_msg = "Failed to close ServiceNow Record {0}".format(err)
                    payload.success = False
                    payload.reason = err_msg
                    yield StatusMessage(err_msg)

            results = payload.as_dict()
            log.debug("RESULTS: %s", results)
            log.info("Complete")

            # Produce a FunctionResult with the results
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
def selftest_function(opts):
    """
    This test uses configs in the app.config file to call the custom '/test_connection' endpoint
    To try and get a status_code=200, else its a failure
    """
    options = opts.get("fn_service_now", {})

    res_helper = ResilientHelper(options)

    try:

        LOG.info("Trying to connect to %s", res_helper.SN_HOST)

        res = res_helper.sn_api_request("GET", "/test_connection")

        status_code = res.status_code

        LOG.info("Status Code: %s", status_code)

        if status_code == 200:
            LOG.info("Test was successful!")
            return {"state": "success"}

        else:

            if res is not None and res.content is not None:
                response_result = json.loads(res.content)
                err_msg = response_result["error"]["message"]
                err_detail = response_result["error"]["detail"]

            else:
                err_msg = "Could not connect to ServiceNow"
                err_detail = "Unknown"

            err_reason_msg = """Could not connect to ServiceNow.
            status_code: {0}
            reason: {1}
            detail: {2}
            ---------
            Current Configs in app.config file::
            ---------
            sn_host: {3}
            sn_username: {4}
            sn_table_name: {5}
            sn_api_uri: {6}\n""".format(status_code, err_msg, err_detail,
                                        res_helper.SN_HOST,
                                        res_helper.SN_USERNAME,
                                        res_helper.SN_TABLE_NAME,
                                        res_helper.SN_API_URI)

            LOG.error(err_reason_msg)

            return {"state": "failure", "reason": err_reason_msg}

    except Exception as err:
        err_reason_msg = """Could not connect to ServiceNow.
            error: {0}
            ---------
            Current Configs in app.config file::
            ---------
            sn_host: {1}
            sn_username: {2}
            sn_table_name: {3}
            sn_api_uri: {4}\n""".format(err, res_helper.SN_HOST,
                                        res_helper.SN_USERNAME,
                                        res_helper.SN_TABLE_NAME,
                                        res_helper.SN_API_URI)

        LOG.error(err_reason_msg)

        return {"state": "failure", "reason": err_reason_msg}
    def _fn_snow_update_record_function(self, event, *args, **kwargs):
        """Function: Function that uses the '/update' custom endpoint in ServiceNow to update a ServiceNow Record with a given dictionary of field name/value pairs."""

        log = logging.getLogger(__name__)

        try:
            # Instansiate helper (which gets appconfigs from file)
            res_helper = ResilientHelper(self.options)

            # Get the function inputs:
            inputs = {
                "incident_id":
                res_helper.get_function_input(
                    kwargs, "incident_id"),  # number (required)
                "task_id":
                res_helper.get_function_input(kwargs, "task_id",
                                              True),  # number (optional)
                "sn_res_id":
                res_helper.get_function_input(kwargs, "sn_res_id",
                                              True),  # text (optional)
                "sn_update_fields":
                res_helper.get_function_input(
                    kwargs, "sn_update_fields")  # text, JSON String (required)
            }

            # Convert 'sn_update_fields' JSON string to Dictionary
            try:
                inputs["sn_update_fields"] = json.loads(
                    inputs.get("sn_update_fields"),
                    object_hook=res_helper._byteify)
            except Exception:
                raise ValueError(
                    "sn_update_fields JSON String is invalid: {0}".format(
                        inputs.get("sn_update_fields")))

            # Create payload dict with inputs
            payload = FunctionPayload(inputs)

            yield StatusMessage("Function Inputs OK")

            # Instansiate new Resilient API object
            res_client = self.rest_client()

            sn_ref_id = payload.inputs.get("sn_ref_id")

            if sn_ref_id is None:

                # Generate res_id using incident and task id
                res_id = res_helper.generate_res_id(
                    payload.inputs.get("incident_id"),
                    payload.inputs.get("task_id"))

                # Instansiate a reference to the ServiceNow Datatable
                datatable = ServiceNowRecordsDataTable(
                    res_client, payload.inputs.get("incident_id"))

                # Get the sn_ref_id
                sn_ref_id = datatable.get_sn_ref_id(res_id)

                if sn_ref_id is None:
                    payload.success = False
                    err_msg = "Failed to update a ServiceNow Record. This {0} has not been created in ServiceNow yet. {0} ID: {1}"

                    if payload.inputs["task_id"]:
                        err_msg = err_msg.format("Task",
                                                 payload.inputs.get("task_id"))

                    else:
                        err_msg = err_msg.format(
                            "Incident", payload.inputs.get("incident_id"))

                    raise ValueError(err_msg)

            yield StatusMessage(
                "Updating ServiceNow Record {0}".format(sn_ref_id))

            # Get sn_update_fields
            sn_update_fields = payload.inputs.get("sn_update_fields")

            # Initialize list for request_data
            fields = []

            # Loop fields and format for request
            for field_name in sn_update_fields:
                field_value = sn_update_fields[field_name]
                fields.append({
                    "name": field_name,
                    "value": sn_update_fields[field_name]
                })
                yield StatusMessage("Updating {0} to {1}".format(
                    field_name, field_value))

            request_data = {
                "sn_ref_id": sn_ref_id,
                "sn_table_name": res_helper.SN_TABLE_NAME,
                "sn_update_fields": fields
            }

            # Call PATCH and get response
            update_response = res_helper.sn_api_request(
                "PATCH", "/update", data=json.dumps(request_data))
            payload.sn_ref_id = update_response.get("sn_ref_id")
            payload.sn_time_updated = int(
                time.time() *
                1000)  # Get current time (*1000 as API does not accept int)

            results = payload.as_dict()
            log.debug("RESULTS: %s", results)
            log.info("Complete")

            # Produce a FunctionResult with the results
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
Exemplo n.º 9
0
    def _fn_snow_add_attachment_to_record_function(self, event, *args, **kwargs):
        """Function: Function that adds a Resilient Attachment to a ServiceNow Record."""

        log = logging.getLogger(__name__)

        try:
            # Instansiate helper (which gets appconfigs from file)
            res_helper = ResilientHelper(self.options)

            # Get the function inputs:
            inputs = {
                "attachment_id": res_helper.get_function_input(kwargs, "attachment_id"),  # number (required)
                "incident_id": res_helper.get_function_input(kwargs, "incident_id"),  # number (required)
                "task_id": res_helper.get_function_input(kwargs, "task_id", True)  # number (optional)
            }

            # Create payload dict with inputs
            payload = FunctionPayload(inputs)

            yield StatusMessage("Function Inputs OK")

            # Instansiate new Resilient API object
            res_client = self.rest_client()

            yield StatusMessage("Getting attachment data. ID: {0}".format(payload.inputs["attachment_id"]))

            # Get the attachment
            attachment = res_helper.get_attachment(res_client,
                                                   payload.inputs["attachment_id"],
                                                   payload.inputs["incident_id"],
                                                   payload.inputs["task_id"])

            # Get the datatable
            datatable = ServiceNowRecordsDataTable(res_client, payload.inputs["incident_id"])

            # Generate res_id using incident and task id
            res_id = res_helper.generate_res_id(payload.inputs["incident_id"], payload.inputs["task_id"])

            # Get the sn_ref_id from the datatable
            sn_ref_id = datatable.get_sn_ref_id(res_id)

            if not sn_ref_id:
                payload.success = False
                err_msg = "Failed to add Attachment to ServiceNow. This {0} has not been created in ServiceNow yet. {0} ID: {1}"

                if payload.inputs["task_id"]:
                    err_msg = err_msg.format("Task", payload.inputs["task_id"])

                else:
                    err_msg = err_msg.format("Incident", payload.inputs["incident_id"])

                raise ValueError(err_msg)

            else:
                # Generate the request_data
                request_data = {
                    "sn_ref_id": sn_ref_id,
                    "sn_table_name": res_helper.SN_TABLE_NAME,
                    "type": "attachment",
                    "attachment_base64": attachment["contents"],
                    "attachment_name": attachment["name"],
                    "attachment_content_type": attachment["content_type"]
                }

                yield StatusMessage("Adding Attachment to ServiceNow Record {0}".format(sn_ref_id))

                # Call POST and get response
                add_in_sn_response = res_helper.sn_api_request("POST", "/add", data=json.dumps(request_data))
                payload.res_id = res_id
                payload.sn_ref_id = sn_ref_id
                payload.attachment_name = attachment["name"]
                payload.sn_attachment_sys_id = add_in_sn_response["attachment_id"]

            results = payload.as_dict()

            log.info("Complete")

            # Produce a FunctionResult with the results
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()