MicrosoftTeamsAsk

Send a team member or channel a question with predefined response options on Microsoft Teams. The response can be used to close a task (might be conditional) in a playbook.

python · Microsoft Teams

Details

IDMicrosoftTeamsAsk
Languagepython
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Tagsmicrosoftteams

README

Send a team member or channel a question with predefined response options on Microsoft Teams. The response can be used to close a task (might be conditional) in a playbook.

Script Data


Name Description
Script Type python3
Tags microsoftteams
Cortex XSOAR Version 5.0.0

Dependencies


This script uses the following commands and scripts.

  • send-notification

Inputs


Argument Name Description
message Question (message) to send to the specified team member or channel.
persistent Indicates whether to use one-time entitlement or persistent entitlement.
option1 First reply option. Options cannot contain whitespaces.
option2 Second reply option. Options cannot contain whitespaces.
additional_options A CSV list of additional options (in case more than 2 options are required). Options cannot contain whitespaces.
team_member The question recipient. Accepts the team member’s email address or username.
task_id Task ID of the playbook task to close with the reply. If not provided, no playbook task will be closed.
channel Channel to which to send the question.
team The team in which to mirror the Cortex XSOAR investigation. If not specified, the default team configured in the integration parameters will be used.

Outputs


There are no outputs for this script.

Usage


The MicrosoftTeamsAsk script sends a message, such as operation approval or information retrieval, in a question format from Cortex XSOAR to Microsoft Teams. The message must have at least two options. For example, “yes” and “no”.

After the question is answered in Microsoft Teams, the response is sent to the Cortex XSOAR server, which appears as a conditional task in a playbook with response options as conditions. Depending on the response, the workflow may continue. For example, you define the following arguments:

  • option1: yes
  • option2: no

If a team member responds “yes” the playbook continues running the “yes” branch.

If a task ID is included in the script, and the task condition is met, the playbook closes as soon as a response is received.

MicrosoftTeamsAsk

To use MicrosoftTeamsAsk via playbook:

  1. Add the MicrosoftTeamsAsk script to a playbook as a task.
  2. In the message argument, specify the message to be sent.
  3. Configure the response options by filling out the option1 and option2 arguments (default values are ‘Yes’ and ‘No’).
  4. Either a team_member or a channel must be specified.
  5. In the MicrosoftTeamsAsk task, pass a tag value to the task_id argument.

All other inputs are optional.
At some point at the playbook, after running MicrosoftTeamsAsk, add a manual conditional task, which holds up the playbook execution until the response is received from Teams.
The condition names must match the response options you passed in to MicrosoftTeamsAsk.
In order to tie the conditional task back to MicrosoftTeamsAsk, add the same tag from the fifth step to the conditional task (under the “Details” tab of the task). The conditional task will be marked as completed when a user responds to the MicrosoftTeamsAsk form.

Notes


  • MicrosoftTeamsAsk will not work when run in the playbook debugger. This is because the debugger does not generate entitlements, since they must be tied to an investigation. Entitlements are needed to track the response.
  • Whitespaces are not supported in custom options and will not work. (i.e. setting a button to I Agree)
import json

import demistomock as demisto
import pytest
from CommonServerPython import entryTypes
from MicrosoftTeamsAsk import main

MS_TEAMS_ASK_MESSAGE_KEYS = {"message_text", "options", "entitlement", "investigation_id", "task_id", "form_type"}

MS_TEAMS_ASK_AC_KEYS = {
    "adaptive_card",
    "entitlement",
    "investigation_id",
    "task_id",
}  # must be synced with ones in MicrosoftTeams.py


def execute_command(name, args=None):
    """
    if assert MS_TEAMS_ASK_MESSAGE_KEYS == json_message.keys() test fails, update the MS_TEAMS_ASK_MESSAGE_KEYS constant
     to have the same keys as the message keys in the MsTeamsAsk script and the test.
    """
    if name == "addEntitlement":
        return [{"Type": entryTypes["note"], "Contents": "4404dae8-2d45-46bd-85fa-64779c12abe8"}]
    elif name == "send-notification":
        expected_script_arguments: dict = {}
        json_message = {
            "message_text": "How are you today?",
            "options": ["Great", "Wonderful", "SSDD", "Wooah"],
            "entitlement": "4404dae8-2d45-46bd-85fa-64779c12abe8",
            "investigation_id": "32",
            "task_id": "44",
            "form_type": "predefined-options",
        }

        if "message" in args:
            json_message["message_text"] = "How are you today?"
            expected_message: str = json.dumps(json_message)
            assert json_message.keys() == MS_TEAMS_ASK_MESSAGE_KEYS
            expected_script_arguments["message"] = expected_message

        if "adaptive_card" in args:
            json_message["adaptive_card"] = {
                "contentType": "application/vnd.microsoft.card.adaptive",
                "content": {
                    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
                    "type": "AdaptiveCard",
                    "version": "1.0",
                    "body": [{"type": "Container", "items": [{"type": "TextBlock", "text": "What a pretty adaptive card"}]}],
                },
            }
            json_message.pop("options")
            json_message.pop("message_text")
            json_message.pop("form_type")
            expected_message: str = json.dumps(json_message)
            assert json_message.keys() == MS_TEAMS_ASK_AC_KEYS
            expected_script_arguments["adaptive_card"] = expected_message

        expected_script_arguments["using-brand"] = "Microsoft Teams"
        expected_script_arguments["using"] = ""

        if "team_member" in args:
            expected_script_arguments["team_member"] = "Shaq"
        elif "channel" in args:
            expected_script_arguments["channel"] = "WhatAchannel"
            if "team" in args:
                expected_script_arguments["team"] = "TestTeam"
        assert args == expected_script_arguments
        return None
    else:
        raise ValueError(f"Unimplemented command called: {name}")


def test_microsoft_teams_ask(mocker):
    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)
    mocker.patch.object(demisto, "investigation", return_value={"id": "32"})
    script_arguments: dict = {
        "message": "How are you today?",
        "option1": "Great",
        "option2": "Wonderful",
        "additional_options": "SSDD,Wooah",
        "task_id": "44",
        "form_type": "predefined-options",
    }

    mocker.patch.object(demisto, "args", return_value=script_arguments)
    with pytest.raises(ValueError) as e:
        main()
    assert str(e.value) == "Either team member, channel or user id must be provided."

    script_arguments["team_member"] = "Shaq"
    script_arguments["channel"] = "WhatAchannel"
    script_arguments["team"] = "TestTeam"
    mocker.patch.object(demisto, "args", return_value=script_arguments)
    with pytest.raises(ValueError) as e:
        main()
    assert str(e.value) == "Either team member or channel should be provided, not both."
    script_arguments.pop("team_member")

    mocker.patch.object(demisto, "args", return_value=script_arguments)
    main()
    assert demisto.executeCommand.call_count == 2
    script_arguments["adaptive_card"] = {
        "contentType": "application/vnd.microsoft.card.adaptive",
        "content": {
            "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
            "type": "AdaptiveCard",
            "version": "1.0",
            "body": [{"type": "Container", "items": [{"type": "TextBlock", "text": "What a pretty adaptive card"}]}],
        },
    }
    mocker.patch.object(demisto, "args", return_value=script_arguments)
    with pytest.raises(ValueError) as e:
        main()
    assert str(e.value) == "Provide either message or adaptive card to send, not both."


def test_microsoft_teams_ask_persistent(mocker):
    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)
    mocker.patch.object(demisto, "investigation", return_value={"id": "32"})
    script_arguments = {
        "message": "How are you today?",
        "option1": "Great",
        "option2": "Wonderful",
        "additional_options": "SSDD,Wooah",
        "task_id": "44",
        "team_member": "Shaq",
        "persistent": "true",
    }
    mocker.patch.object(demisto, "args", return_value=script_arguments)
    main()
    assert demisto.executeCommand.call_count == 2


def test_microsoft_teams_ask_error_response(mocker):
    def error_execute_command(name, args=None):
        if name == "addEntitlement":
            return [{"Type": entryTypes["error"], "Contents": "Error adding entitlement"}]
        return None

    mocker.patch.object(demisto, "executeCommand", side_effect=error_execute_command)
    mocker.patch.object(demisto, "investigation", return_value={"id": "32"})
    script_arguments = {"message": "Test message", "team_member": "Charlie"}
    mocker.patch.object(demisto, "args", return_value=script_arguments)
    mocker.patch.object(demisto, "results")
    main()
    demisto.results.assert_called_once()