Microsoft Teams

Table of Contents


Release Notes

Version

Date

Notes

2.3.0

02/2025

Add functions: list groups, list teams, list channels. Better proxy support.

2.2.0

08/2024

New function to support MS Teams Workflows

2.1.0

05/2023

Add playbooks and removed workflows

2.0.1

12/2022

Bug fix in workflows for MS Teams: Enable Teams for Groups and MS Teams: Read messages

2.0.0

12/2022

Added support for creating and deleting MS Groups, Teams and Channels

1.0.0

10/2019

Post Incident/task information to MS Teams

2.2.0 Changes

v2.2.0 supports MS Teams Workflows and Adaptive Cards. See the section Setting up Workflows for more information needed to configure this capability.

2.1.0 Changes

In v2.1, the existing rules and workflows have been replaced with playbooks. This change is made to support the ongoing, newer capabilities of playbooks. Each playbook has the same functionality as the previous, corresponding rule/workflow.

If upgrading from a previous release, you’ll notice that the previous release’s rules/workflows remain in place. Both sets of rules and playbooks are active. For manual actions, playbooks have the same name as it’s corresponding rule, but with “(PB)” added at the end. For automatic actions, the playbooks will be disabled by default.

You can continue to use the rules/workflows. But migrating to playbooks provides greater functionality along with future app enhancements and bug fixes.


screenshot: main.png

Overview

This IBM Security QRadar SOAR application extends the meeting and collaboration functionality of Microsoft Teams. It includes the ability to create MS Teams Groups, Teams and Channels. Functionality also exists to post SOAR Incident or task information to a MS Teams Channel.

Microsoft 365 Groups are created with resources that members of the group share, including:

  • Outlook conversations

  • Outlook calendar

  • SharePoint files

  • OneNote notebook

  • SharePoint team site

  • Planner plans

  • Intune device management

Key Features

  • Create or delete Groups, Channels, and Teams, as well as archive or un-archive Teams. Incident and task members can be assigned to a Team.

  • It is now possible to create Groups, Teams, and Channels with members who are not a part of an incident or task but who have a functioning MS account that is a part of the same organization.

  • Post information about the Incident or task directly to a MS Channel.

  • Example rules/workflows are included that perform the aforementioned operations and store related information as a incident or a task note.


Requirements

This app supports the IBM Security QRadar SOAR Platform and the IBM Security QRadar SOAR for IBM Cloud Pak for Security.

SOAR platform

The SOAR platform supports two app deployment mechanisms, App Host and integration server.

If deploying to a SOAR platform with an App Host, the requirements are:

  • SOAR platform >= 51.0.0.

  • The app is in a container-based format (available from the AppExchange as a zip file).

If deploying to a SOAR platform with an integration server, the requirements are:

  • SOAR platform >= 51.0.0.

  • The app is in the older integration format (available from the AppExchange as a zip file which contains a tar.gz file).

  • Integration server is running resilient_circuits>=51.0.0.

  • If using an API key account, make sure the account provides the following minimum permissions:

    Name

    Permissions

    Org Data

    Read

    Function

    Read

    Incident

    Read, Write

    Incident Private Tasks

    Read

    Group

    Read

    Users

    Read

The following SOAR platform guides provide additional information:

  • App Host Deployment Guide: provides installation, configuration, and troubleshooting information, including proxy server settings.

  • Integration Server Guide: provides installation, configuration, and troubleshooting information, including proxy server settings.

  • System Administrator Guide: provides the procedure to install, configure and deploy apps.

The above guides are available on the IBM Documentation website at ibm.biz/soar-docs. On this web page, select your SOAR platform version. On the follow-on page, you can find the App Host Deployment Guide or Integration Server Guide by expanding Apps in the Table of Contents pane. The System Administrator Guide is available by expanding System Administrator.

Cloud Pak for Security

If you are deploying to IBM Cloud Pak for Security, the requirements are:

  • IBM Cloud Pak for Security >= 1.10.

  • Cloud Pak is configured with an App Host.

  • The app is in a container-based format (available from the AppExchange as a zip file).

The following Cloud Pak guides provide additional information:

  • App Host Deployment Guide: provides installation, configuration, and troubleshooting information, including proxy server settings. From the Table of Contents, select Case Management and Orchestration & Automation > Orchestration and Automation Apps.

  • System Administrator Guide: provides information to install, configure, and deploy apps. From the IBM Cloud Pak for Security IBM Documentation table of contents, select Case Management and Orchestration & Automation > System administrator.

These guides are available on the IBM Documentation website at ibm.biz/cp4s-docs. From this web page, select your IBM Cloud Pak for Security version. From the version-specific IBM Documentation page, select Case Management and Orchestration & Automation.

Proxy Server

The app does support a proxy server.

Python Environment

Both Python 3.9 and Python 3.11 are supported. Additional package dependencies may exist for each of these packages:

  • msal ~= 1.30

  • pymsteams ~= 0.2.3

  • resilient_circuits>=51.0.0

Endpoint Developed With MS Teams

This app has been implemented using:

Product Name

API URL

API Version

Microsoft Graph REST API

https://graph.microsoft.com/

V1.0


Installation

  • To install or uninstall an App or Integration on the SOAR platform, see the documentation at ibm.biz/soar-docs.

  • To install or uninstall an App on IBM Cloud Pak for Security, see the documentation at ibm.biz/cp4s-docs and follow the instructions above to navigate to Orchestration and Automation.

Endpoint Configuration

This application needs an access token from the Microsoft identity platform for use with Microsoft Graph APIs. The access token includes details about the application and the permission it has to use the Microsoft Graph resources and APIs. The app needs to be registered with the Microsoft identity platform and given permission to access the required Microsoft Graph resources by a user or an administrator in order to obtain an access token.

Register a new application using the Azure portal

The application must be registered with the identity provider for the identity provider to be aware that a specific app is attempting to access user information. The configuration necessary for the application to interface with the Microsoft identity platform is then made available when it registers with Microsoft Entra ID (formally Azure AD). You can learn more about this at learn.microsoft.com/application-model

  • Sign in to the Azure portal using either an enterprise account.

  • If your account gives you access to more than one tenant, select your account in the top right corner, and set your portal session to the Azure AD tenant that you want.

  • In the left-hand navigation pane, select the Microsoft Entra ID, and then select App registrations > New registration.

  • When the Register an application page appears, enter your application’s registration information:

    • Name - Enter a meaningful application name that will be displayed to users of the app.

    • Supported account types - Select which accounts you would like your application to support.

    • Redirect URI - Enter the redirect URI (or reply URL) for this application, i.e : https://localhost:8080/callback

screenshot: app_registration_overview.png

  • Note this information as this would be required later while setting up the application in the SOAR platform.

  • When finished, select Register.

  • In the left-hand navigation pane under the Manage section, select Authentication.

  • Select Yes for Enable the following mobile and desktop flows then click Save.

API Permissions

See the list permissions needed for the App associated with the Team app.

screenshot: app_scopes_permissions.png

Create a client Secret Value (Both Permissions)

  • In the left-hand navigation pane under the Manage section, select Certificate and secrets.

  • Click on the New client secret button.

  • Enter a name for the client secret and click on the Add button.

  • Note this information as this would be required later while setting up the application in the SOAR platform.

screenshot: app_certificate_secrets.png

Setting up Incoming Webhooks (Both Permissions)

Note: Webhooks are now replaced by MS Teams Workflows. Refer to MS Teams Workflows on how to start using this new capability.

Setting up Delegated permissions (Delegated permissions)

This setup process is to provide the application with delegated permissions which is required only for Read channel message feature. If you wish to use the application without this feature, you can do so by skipping this process.

To provide the application with the necessary permission, the OAuth Utilities Documentation is to be used.

  • This tool provides the user with means to login on behalf of the application and provide the application with the required permissions to act on behalf of the user.

  • The application_id, directory_id and secret_value are used to generate a unique refresh_token, which is later used in the app.conf file.

  • There are several ways to generate this Refresh token using the OAuth Utilities tool, please refer to the documentation OAuth Utilities Documentation

  • Once such method would be using the CLI. A sample command has been provided below:

oauth-utils oauth2_generate_refresh_token \
-tu https://login.microsoftonline.com/<DIRECTORY_ID>/oauth2/v2.0/token \
-au https://login.microsoftonline.com/<DIRECTORY_ID>/oauth2/v2.0/authorize \
-ci <APPLICATION_ID> \
-cs <SECRET_VALUE> \
-sc "ChannelMessage.Send ChannelMessage.Read.All offline_access"

Upon successfully completing this process, you will be presented with a refresh_token. Note this information as this is required later while setting up the application in the SOAR platform.

App Configuration (Both Permissions)

The following table provides the settings you need to configure the app. These settings are made in the app.config file. These values are used by the SOAR platform to establish a secure connection with the Microsoft Endpoint. See the documentation discussed in the Endpoint Configuration section for the procedure. If you have successfully configured that endpoint, you can find these values:

Config

Required

Example

Description

application_id

Yes

18d10049-72e3-4652-ac9f-d9b13f24303c

Overview -> Application (client) ID

directory_id

Yes

1d8a5928-8678-408e-ab06-50ca7e01766a

Overview -> Directory (tenant) ID

secret_value

Yes

oCN******************

Certificate & secrets -> Secret Value

refresh_token

Optional

eyxn******************

Required only for delegated permissions

<channel_name>

Optional

****.webhook.office.com/webhookb2/

Webhook URL for a channel

selftest

Optional

https://teams.microsoft.com/l/channel/xxx

Webhook URL for channel connectors to test posting of messages. This capability is deprecated and selftest_workflows should be used starting with V2.2

selftest_workflows

Optional

https://azure.com/workflows/xxx

Webhook URL for Teams Workflows to test posting of messages.

*<webhook_label>

Optional

https://azure.com/workflows/xxx

Additional webhook URLs. Reference the webhook label used when post messaging via the functions which use the teams_channel input field.

MS Teams Workflows

In V2.2, messages can be sent via the MS Teams Workflows capability. Although a powerful way to set up complex capabilities, the basic enablement can be performed via these steps:

  1. Within the MS Teams app (either Windows or Mac), select the Workflows app screenshot: Workflows app

  2. Add a workflow for Post to a channel when a webhook request is received screenshot: Adding a Workflow

  3. At the minimum, the workflow will have steps to send Adaptive Cards to a channel screenshot: Workflow Steps screenshot: Workflow Detail

The webhook request step contains the URL required to send Adaptive Cards to a teams channel. Copy this value and add it your app.config settings using a label of your choosing. This label will then be referred to in any message sent to the MS Teams channel via this Workflow.


Function - MS Teams: Archive Team

This function allows for archiving or unarchiving a Microsoft Team. The archive_operation input parameter specifies if the team is to be archived or unarchived. Archiving does not delete the MS Team. To identify the team for archival or unarchival, one of the following inputs can be used:

  • ms_groupteam_id

  • ms_group_mail_nickname

  • ms_groupteam_name

Note: When multiple options are provided to locate the Graph object (Group or a Team), the ms_group_mail_nickname parameter will take precedence over ms_groupteam_name, and the ms_groupteam_id parameter will take precedence over the other two options.

screenshot: action_archive_team.png

Inputs:

Name

Type

Required

Example

Tooltip

archive_operation

select

No

archive

Specify the operation to be performed

ms_group_mail_nickname

text

No

Engineering

Unique value, as no two MS Objects can have the same email ID. The mail address need not include the domain suffix (i.e. @example.com)

ms_groupteam_id

text

No

4dfde5ae-4c27-4461

Unique id assigned to a MS Group or Team while being created

ms_groupteam_name

text

No

Engineering Team

Name assigned to the MS Group or Team while being created

Outputs:

NOTE: This example might be in JSON format, but results is a Python Dictionary on the SOAR platform.

results = {
  "version": 2.0,
  "success": true,
  "reason": null,
  "content": {
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups/$entity",
    "id": "87fd1ea0-6a3f-4078-b271-e4f8e6f8469e",
    "deletedDateTime": null,
    "classification": null,
    "createdDateTime": "2023-05-15T17:39:58Z",
    "creationOptions": [
      "Team",
      "ExchangeProvisioningFlags:3552"
    ],
    "description": "task testing",
    "displayName": "task testing",
    "expirationDateTime": null,
    "groupTypes": [
      "Unified"
    ],
    "isAssignableToRole": null,
    "mail": "tasktesting@example.com",
    "mailEnabled": true,
    "mailNickname": "tasktesting",
    "membershipRule": null,
    "membershipRuleProcessingState": null,
    "onPremisesDomainName": null,
    "onPremisesLastSyncDateTime": null,
    "onPremisesNetBiosName": null,
    "onPremisesSamAccountName": null,
    "onPremisesSecurityIdentifier": null,
    "onPremisesSyncEnabled": null,
    "preferredDataLocation": null,
    "preferredLanguage": null,
    "proxyAddresses": [
      "SMTP:tasktesting@example.com"
    ],
    "renewedDateTime": "2023-05-15T17:39:58Z",
    "resourceBehaviorOptions": [
      "HideGroupInOutlook",
      "SubscribeMembersToCalendarEventsDisabled",
      "WelcomeEmailDisabled"
    ],
    "resourceProvisioningOptions": [
      "Team"
    ],
    "securityEnabled": false,
    "securityIdentifier": "S-1-12-1-2281512608-1081633343-4175720882-2655451366",
    "theme": null,
    "visibility": "Private",
    "onPremisesProvisioningErrors": [],
    "status_code": 200,
    "message": "Successfully archived Team: task testing",
    "teamsEnabled": "Archived"
  },
  "raw": null,
  "inputs": {
    "ms_groupteam_id": "87fd1ea0-6a3f-4078-b271-e4f8e6f8469e",
    "archive_operation": "Archive",
    "ms_group_mail_nickname": "",
    "ms_groupteam_name": ""
  },
  "metrics": {
    "version": "1.0",
    "package": "fn-teams",
    "package_version": "2.1.0",
    "host": "local",
    "execution_time_ms": 2030,
    "timestamp": "2023-05-15 13:47:58"
  }
}

Example Pre-Process Script:

inputs.archive_operation = "Archive"
if playbook.inputs.archive_operation:
  inputs.archive_operation = playbook.inputs.archive_operation

if playbook.inputs.ms_groupteam_id:
  inputs.ms_groupteam_id = playbook.inputs.ms_groupteam_id

elif playbook.inputs.ms_group_mail_nickname:
  inputs.ms_group_mail_nickname = playbook.inputs.ms_group_mail_nickname

elif playbook.inputs.ms_groupteam_name:
  inputs.ms_groupteam_name = playbook.inputs.ms_groupteam_name

else:
  helper.fail("No input was provided")

Example Post-Process Script:

FN_NAME = "ms_teams_archive_team"
WF_NAME = "MS Teams: Archive Team (PB)"

results = playbook.functions.results.archive_results
content = results.get("content", {})

if not results.get("success"):
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Failed for SOAR function <b>{FN_NAME}</b><br>"
  text += "Unable to archive the Microsoft Team"
  fail_reason = results.get("reason")
  if fail_reason:
    text = f"{text}:\n\tFailure reason: {fail_reason}"
    text += f"\n\tInputs: {results.get('inputs')}"

else:
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Successfully Executed for SOAR function <b>{FN_NAME}</b><br>"
  text += f"""<b>Microsoft Team Details:</b><br />
  <br />The Team associated with this Group has now been {content.get('teamsEnabled')}.<br />
  <br />Name: {content.get('displayName')}
  <br />Description: {content.get('description')}
  <br />Teams Enabled: {content.get('teamsEnabled')}
  <br />ID: {content.get('id')}
  <br />Mail: {content.get('mail')}
  <br />Visibility: {content.get('visibility')}
  <br />Group Types: {content.get('groupTypes')}
  <br />Created date and time: {content.get('createdDateTime')}"""
  if content.get("unfoundUsers"):
    text += f"<br />*Note the following users were unable to be added to the group: {content.get('unfoundUsers')}"

incident.addNote(helper.createRichText(text))


Function - MS Teams: Create Channel

This function creates a Microsoft Channel for a MS Team. A MS Team can have multiple channels.

To create a Channel for an MS Team, 3 key attributes are required, namely: teamId, displayName, and description. Out of these attributes, teamId is crucial as the MS Team must be properly identified before channel addition operation can take place. This function has the ability to find the required teamId using anyone of the below mentioned options:

  • ms_groupteam_id

  • ms_group_mail_nickname

  • ms_groupteam_name

Note: When multiple options are provided to locate the Graph object (Group or a Team), the ms_group_mail_nickname parameter will take precedence over ms_groupteam_name, and the ms_groupteam_id parameter will take precedence over the other two parameters.

screenshot: action_create_channel.png

Inputs:

Name

Type

Required

Example

Tooltip

ms_channel_name

text

No

Team 2

Name of the Microsoft Teams channel

ms_description

text

No

Engineering SubTeam2

Description for the MS Graph Object (Group / Team / Channel) that is being created

ms_group_mail_nickname

text

No

Engineering2

Unique value, as no two MS Objects can have the same email ID. The mail address need not include the domain suffix (i.e. @example.com)

ms_groupteam_id

text

No

4dfde5ae-4c27-4461

Unique id assigned to the MS Group or Team

ms_groupteam_name

text

No

Engineering

Name assigned to the MS Group or Team

Outputs:

NOTE: This example might be in JSON format, but results is a Python Dictionary on the SOAR platform.

results = {
  "version": 2.0,
  "success": true,
  "reason": null,
  "content": {
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#teams('33333-450e-46d6-9338-f7e3b66a064c')/channels/$entity",
    "id": "19:c875f7333fb843aeacb01d1cbfa52ae5@thread.tacv2",
    "createdDateTime": "2023-05-15T18:26:57.6422279Z",
    "displayName": "test 1234",
    "description": "test 1234",
    "isFavoriteByDefault": false,
    "email": "",
    "webUrl": "https://teams.microsoft.com/l/channel/19%3ac875f7333fb843aeacb01d1cbfa52ae5%40thread.tacv2/test+1234?groupId=33333-450e-46d6-9338-f7e3b66a064c&tenantId=55555-b889-434d-802d-13b87c68047b",
    "membershipType": "standard",
    "status_code": 201,
    "message": "Successfully created channel: test 1234"
  },
  "raw": null,
  "inputs": {
    "ms_groupteam_id": "33333-450e-46d6-9338-f7e3b66a064c",
    "ms_channel_name": "test 1234",
    "ms_description": "test 1234"
  },
  "metrics": {
    "version": "1.0",
    "package": "fn-teams",
    "package_version": "2.1.0",
    "host": "local",
    "execution_time_ms": 4593,
    "timestamp": "2023-05-15 14:26:57"
  }
}

Example Pre-Process Script:

inputs.ms_channel_name = playbook.inputs.ms_channel_name if playbook.inputs.ms_channel_name else f"Incident {incident.id} {incident.name}"

if playbook.inputs.ms_description:
    inputs.ms_description = playbook.inputs.ms_description
else:
    description = incident.description.content if incident.description else ""
    inputs.ms_description = f"Incident {incident.id}: {incident.name} {description}"

if playbook.inputs.ms_groupteam_id:
  inputs.ms_groupteam_id = playbook.inputs.ms_groupteam_id
elif playbook.inputs.ms_group_mail_nickname:
  inputs.ms_group_mail_nickname = playbook.inputs.ms_group_mail_nickname
elif playbook.inputs.ms_groupteam_name:
  inputs.ms_groupteam_name = playbook.inputs.ms_groupteam_name
else:
  helper.fail("No input was provided")

Example Post-Process Script:

FN_NAME = "ms_teams_create_channel"
WF_NAME = "MS Teams: Create Channel (PB)"

results = playbook.functions.results.create_channel_results
content = results.get("content", {})
team = playbook.inputs.ms_groupteam_name or playbook.inputs.ms_group_mail_nickname or playbook.inputs.ms_groupteam_id

if not results.get("success"):
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Failed for SOAR function <b>{FN_NAME}</b><br>"
  text += f"Unable to create Microsoft Group: {playbook.inputs.ms_channel_name}"
  fail_reason = results.get("reason")
  if fail_reason:
    text = f"{text}:\n\tFailure reason: {fail_reason}"
    text += f"\n\tInputs: {results.get('inputs')}"

else:
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Successfully Executed for SOAR function <b>{FN_NAME}</b><br>"
  text += f'''<a href="{content.get("webUrl", "")}">Click here to go to channel</a>
  <b>Microsoft Channel Details:</b><br />
  Name: {content.get("displayName", "")}
  Team: {team}
  Web URL: {content.get("webUrl", "")}
  Description: {content.get("description", "")}
  Teams Enabled: {True}
  ID: {content.get("id")}
  Mail: {content.get("email", "")}
  Membership Type: {content.get("membershipType")}'''

incident.addNote(helper.createRichText(text))


Function - MS Teams: Create group

This function creates a Microsoft Group with the ability to add multiple owners by specifying their email addresses in a comma-separated list. At least one owner must be mentioned for group creation.

The function is developed to automatically add all members of an incident or a task to the MS Group. If the function is executed from within a task, in addition to task members, all incident members can also be automatically added if that option is selected. Apart from automatic member addition, individual members can be added by directly specifying their email addresses.

screenshot: action_create_group.png

Inputs:

Name

Type

Required

Example

Tooltip

add_members_from

select

Yes

Incident/Task

Add incident or task members to Team/Group

additional_members

text

No

user1@example.com, user2@example.com

Add additional Team members who are not members of this incident or task

incident_id

number

No

1098

Incident id of the current incident

ms_description

text

No

-

Description for the MS Group

ms_group_mail_nickname

text

No

SOAR

Unique value, as no two MS Objects can have the same email ID. The mail address need not include the domain suffix (i.e. @example.com)

ms_group_name

text

No

SOAR

Name of the MS Group

ms_owners_list

text

No

AdminSoarMS@.onmicrosoft.com

A list of owners for the Group

task_id

number

No

-

Incident task id if creating a Group from a task.

Outputs:

NOTE: This example might be in JSON format, but results is a Python Dictionary on the SOAR platform.

results = {
  "version": 2.0,
  "success": true,
  "reason": null,
  "content": {
    "id": "3cde21c1-7e9c-4f6d-b986-06d879c43dad",
    "deletedDateTime": null,
    "classification": null,
    "createdDateTime": "2023-05-15T18:13:38Z",
    "creationOptions": [],
    "description": "test1",
    "displayName": "test1",
    "expirationDateTime": null,
    "groupTypes": [
      "Unified"
    ],
    "isAssignableToRole": null,
    "mail": "test1@example.com",
    "mailEnabled": true,
    "mailNickname": "test1",
    "membershipRule": null,
    "membershipRuleProcessingState": null,
    "onPremisesDomainName": null,
    "onPremisesLastSyncDateTime": null,
    "onPremisesNetBiosName": null,
    "onPremisesSamAccountName": null,
    "onPremisesSecurityIdentifier": null,
    "onPremisesSyncEnabled": null,
    "preferredDataLocation": null,
    "preferredLanguage": null,
    "proxyAddresses": [
      "SMTP:test1@example.com"
    ],
    "renewedDateTime": "2023-05-15T18:13:38Z",
    "resourceBehaviorOptions": [],
    "resourceProvisioningOptions": [],
    "securityEnabled": false,
    "securityIdentifier": "S-1-12-1-1021190593-1332575900-3624306361-2906506361",
    "theme": null,
    "visibility": "Private",
    "onPremisesProvisioningErrors": [],
    "teamsEnabled": false,
    "unfoundUsers": []
  },
  "raw": null,
  "inputs": {
    "incident_id": 2122,
    "additional_members": "",
    "ms_owners_list": "markscherfling@example.com",
    "ms_group_name": "test1",
    "ms_group_mail_nickname": "test1",
    "add_members_from": "None",
    "task_id": null,
    "ms_description": "test1"
  },
  "metrics": {
    "version": "1.0",
    "package": "fn-teams",
    "package_version": "2.1.0",
    "host": "local",
    "execution_time_ms": 1560,
    "timestamp": "2023-05-15 14:13:39"
  }
}

Example Pre-Process Script:

if task:
  inputs.task_id = task.id

inputs.incident_id = str(incident.id)
inputs.ms_group_name = f"Incident {incident.id}: {incident.name}" if playbook.inputs.ms_group_name is None else playbook.inputs.ms_group_name

if playbook.inputs.ms_owners_list:
  inputs.ms_owners_list = playbook.inputs.ms_owners_list

if playbook.inputs.add_members_incident:
  _value = playbook.inputs.add_members_incident.lower().strip()
  if _value == "all incident members":
    inputs.add_members_from = "Incident"
  else:
    inputs.add_members_from = "None"

if playbook.inputs.additional_members.content:
  inputs.additional_members = playbook.inputs.additional_members.content

if playbook.inputs.ms_description:
  inputs.ms_description = playbook.inputs.ms_description
else:
  description = incident.description.content if incident.description else ""
  inputs.ms_description = f"Incident {incident.id}: {incident.name} {description}"

if playbook.inputs.ms_group_mail_nickname:
  inputs.ms_group_mail_nickname = playbook.inputs.ms_group_mail_nickname

Example Post-Process Script:

FN_NAME = "ms_teams_create_group"
WF_NAME = "MS Teams: Create Group (PB)"

results = playbook.functions.results.create_group_results
content = results.get("content", {})

if not results.get("success"):
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Failed for SOAR function <b>{FN_NAME}</b><br>"
  text += f"Unable to create Microsoft Group: {playbook.inputs.ms_group_name}"
  fail_reason = results.get("reason")
  if fail_reason:
    text = f"""{text}:\n\tFailure reason: {fail_reason}
    Inputs: {str(playbook.inputs)}"""

else:
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Successfully Executed for SOAR function <b>{FN_NAME}</b><br>"
  text += f'''<b>Microsoft Group Details:</b><br />
  Name: {content.get("displayName")}
  Description: {content.get("description")}
  Nickname: {content.get("mailNickname")}
  Teams Enabled: {content.get("teamsEnabled")}
  ID: {content.get("id")}
  Mail: {content.get("mail")}
  Visibility: {content.get("visibility")}
  Group Types: {content.get("groupTypes")}
  Created date and time: {content.get("createdDateTime")}'''
  if content.get("unfoundUsers"):
    text += f'<br />*Note the following users were unable to be added to the group: {content.get("unfoundUsers")}'

incident.addNote(helper.createRichText(text))


Function - MS Teams: Create team

This function creates a Microsoft Team with the ability to add multiple owners by specifying their email addresses in a comma-separated list. At least one owner must be mentioned for group creation.

The function is developed to automatically add all members of an incident or a task to the MS Team. If the function is executed from within a task, in addition to task members, all incident members can also be automatically added if that option is selected. Apart from automatic member addition, individual members can be added by directly specifying their email addresses.

screenshot: action_create_team.png

Inputs:

Name

Type

Required

Example

Tooltip

add_members_from

select

Yes

Incident/Task

Allows for adding incident or task members to Team

additional_members

text

No

user1@example.com, user2@example.com

Add members who are not members of this incident or task

incident_id

number

No

1098

Incident Id associate with this workflow/playbook

ms_description

text

No

-

Description for the MS Graph Team that is being created

ms_owners_list

text

No

AdminSoarMS@.onmicrosoft.com

A comma separated list of owners for the group or team

ms_team_name

text

No

SoarTeam

Name of the Microsoft Team

task_id

number

No

-

Incident task id if this Team is associated with a task

Outputs:

NOTE: This example might be in JSON format, but results is a Python Dictionary on the SOAR platform.

results = {
  "version": 2.0,
  "success": true,
  "reason": null,
  "content": {
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups/$entity",
    "id": "87fd1ea0-6a3f-4078-b271-e4f8e6f8469e",
    "deletedDateTime": null,
    "classification": null,
    "createdDateTime": "2023-05-15T17:39:58Z",
    "creationOptions": [
      "Team",
      "ExchangeProvisioningFlags:3552"
    ],
    "description": "task testing",
    "displayName": "task testing",
    "expirationDateTime": null,
    "groupTypes": [
      "Unified"
    ],
    "isAssignableToRole": null,
    "mail": "tasktesting@example.com",
    "mailEnabled": true,
    "mailNickname": "tasktesting",
    "membershipRule": null,
    "membershipRuleProcessingState": null,
    "onPremisesDomainName": null,
    "onPremisesLastSyncDateTime": null,
    "onPremisesNetBiosName": null,
    "onPremisesSamAccountName": null,
    "onPremisesSecurityIdentifier": null,
    "onPremisesSyncEnabled": null,
    "preferredDataLocation": null,
    "preferredLanguage": null,
    "proxyAddresses": [
      "SMTP:tasktesting@example.com"
    ],
    "renewedDateTime": "2023-05-15T17:39:58Z",
    "resourceBehaviorOptions": [
      "HideGroupInOutlook",
      "SubscribeMembersToCalendarEventsDisabled",
      "WelcomeEmailDisabled"
    ],
    "resourceProvisioningOptions": [
      "Team"
    ],
    "securityEnabled": false,
    "securityIdentifier": "S-1-12-1-2281512608-1081633343-4175720882-2655451366",
    "theme": null,
    "visibility": "Private",
    "onPremisesProvisioningErrors": [],
    "status_code": 200,
    "teamsEnabled": true,
    "unfoundUsers": null
  },
  "raw": null,
  "inputs": {
    "ms_team_name": "task testing",
    "incident_id": 2120,
    "additional_members": "",
    "ms_owners_list": "markscherfling@example.com",
    "add_members_from": "None",
    "task_id": 126,
    "ms_description": "task testing"
  },
  "metrics": {
    "version": "1.0",
    "package": "fn-teams",
    "package_version": "2.1.0",
    "host": "local",
    "execution_time_ms": 50594,
    "timestamp": "2023-05-15 13:40:47"
  }
}

Example Pre-Process Script:

if task:
  inputs.task_id = task.id

inputs.incident_id = str(incident.id)
inputs.ms_team_name = f"Incident {incident.id}: {incident.name}" if playbook.inputs.ms_team_name is None else playbook.inputs.ms_team_name

if playbook.inputs.ms_owners_list:
  inputs.ms_owners_list = playbook.inputs.ms_owners_list

if playbook.inputs.add_members_incident:
  _value = playbook.inputs.add_members_incident.lower().strip()
  if _value == "all incident members":
    inputs.add_members_from = "Incident"
  else:
    inputs.add_members_from = "None"

if playbook.inputs.additional_members.content:
  inputs.additional_members = playbook.inputs.additional_members.content

if playbook.inputs.ms_description:
  inputs.ms_description = playbook.inputs.ms_description
else:
  description = incident.description.content if incident.description else ""
  inputs.ms_description = f"Incident {incident.id}: {incident.name} {description}"

Example Post-Process Script:

FN_NAME = "ms_teams_create_team"
WF_NAME = "MS Teams: Create Team (PB)"

results = playbook.functions.results.create_team
content = results.get("content", {})

if not results.get("success"):
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Failed for SOAR function <b>{FN_NAME}</b><br>"
  text += "Unable to create Microsoft Team"
  fail_reason = results.get("reason")
  if fail_reason:
    text = f"{text}:\n\tFailure reason: {fail_reason}"
    text += f"\n\tInputs: {results.get('inputs')}"

else:
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Successfully Executed for SOAR function <b>{FN_NAME}</b><br>"
  text += f'''<b>Microsoft Team Details:</b><br />
  Name: {content.get("displayName")}
  Description: {content.get("description")}
  Teams Enabled: {content.get("teamsEnabled")}
  ID: {content.get("id")}
  Mail: {content.get("mail")}
  Visibility: {content.get("visibility")}
  Group Types: {content.get("groupTypes")}
  Created date and time: {content.get("createdDateTime")}'''
  if content.get("unfoundUsers"):
    text += f'<br />*Note the following users were unable to be added to the group: {content.get("unfoundUsers")}'

incident.addNote(helper.createRichText(text))


Function - MS Teams: Delete Channel

This function deletes a MS Channel. A MS Team can be assigned to multiple channels. However, each MS Group can have only one Team. In order to delete an Channel, it’s MS Team/Group needs to be identified. To locate this team for this operation, one of the following inputs can be used:

  • ms_groupteam_id

  • ms_group_mail_nickname

  • ms_groupteam_name

Note: When multiple options are provided to locate the Graph object (Group or a Team), the ms_group_mail_nickname parameter will take precedence over ms_groupteam_name, and the ms_groupteam_id parameter will take precedence over the other two.

screenshot: action_delete_channel.png

Inputs:

Name

Type

Required

Example

Tooltip

ms_channel_name

text

No

Engineering Channel

Name of the Microsoft Teams channel

ms_group_mail_nickname

text

No

engineeringTeam

Unique nickname for a group. The mail address need not include the domain suffix (i.e. @example.com)

ms_groupteam_id

text

No

db7350fc-b6df-4041

Unique id assigned to a MS Group or Team while being created

ms_groupteam_name

text

No

Engineering Team

Name assigned to the MS Group or Team while being created

Outputs:

NOTE: This example might be in JSON format, but results is a Python Dictionary on the SOAR platform.

results = {
  "version": 2.0,
  "success": true,
  "reason": null,
  "content": {
    "status_code": 204,
    "message": "Successfully deleted channel: Incident 2121 testing channels"
  },
  "raw": null,
  "inputs": {
    "ms_groupteam_id": "33333-450e-46d6-9338-f7e3b66a064c",
    "ms_channel_name": "Incident 2121 testing channels"
  },
  "metrics": {
    "version": "1.0",
    "package": "fn-teams",
    "package_version": "2.1.0",
    "host": "local",
    "execution_time_ms": 3172,
    "timestamp": "2023-05-15 14:09:04"
  }
}

Example Pre-Process Script:

inputs.ms_channel_name = playbook.inputs.ms_channel_name

if playbook.inputs.ms_groupteam_id:
  inputs.ms_groupteam_id = playbook.inputs.ms_groupteam_id
elif playbook.inputs.ms_group_mail_nickname:
  inputs.ms_group_mail_nickname = playbook.inputs.ms_group_mail_nickname
elif playbook.inputs.ms_groupteam_name:
  inputs.ms_groupteam_name = playbook.inputs.ms_groupteam_name
else:
  helper.fail("No input was provided")

Example Post-Process Script:

FN_NAME = "ms_teams_delete_channel"
WF_NAME = "MS Teams: Delete Channel (PB)"

results = playbook.functions.results.delete_channel
content = results.get("content", {})
channel_name = playbook.inputs.ms_channel_name

if not results.get("success"):
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Failed for SOAR function <b>{FN_NAME}</b><br>"
  text += f"Unable to delete Microsoft Channel: {channel_name}"
  fail_reason = results.get("reason")
  if fail_reason:
    text = f"{text}:\n\tFailure reason: {fail_reason}"
    text += f"\n\tInputs: {results.get('inputs')}"

else:
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Successfully Executed for SOAR function <b>{FN_NAME}</b><br>"
  text += f"""<b>Delete Microsoft Channel: {channel_name} result</b><br />
  <br />{content.get('message')}"""

incident.addNote(helper.createRichText(text))


Function - MS Teams: Delete Group

This function deletes a MS Group. To identify this Group, one of the following inputs can be used:

  • ms_group_id

  • ms_group_mail_nickname

  • ms_group_name

Note: When multiple parameters are provided to locate the Graph object (Group or a Team), the ms_group_mail_nickname parameter will take precedence over the ms_group_name, and the ms_group_id parameter will take precedence over the other two.

screenshot: action_delete_group.png

Inputs:

Name

Type

Required

Example

Tooltip

ms_group_id

text

No

db7350fc-b6df-4041

Unique id assigned to a group

ms_group_mail_nickname

text

No

ProductionTeam

Unique nickname, as no two MS Objects can have the same email ID. The mail address need not include the domain suffix (i.e. @example.com)

ms_group_name

text

No

ProductionTeam

Name of the MS Group

Outputs:

NOTE: This example might be in JSON format, but results is a Python Dictionary on the SOAR platform.

results = {
  "version": 2.0,
  "success": true,
  "reason": null,
  "content": {
    "status_code": 204,
    "message": "Successfully deleted Group: test1"
  },
  "raw": null,
  "inputs": {
    "ms_group_id": "3cde21c1-7e9c-4f6d-b986-06d879c43dad"
  },
  "metrics": {
    "version": "1.0",
    "package": "fn-teams",
    "package_version": "2.1.0",
    "host": "local",
    "execution_time_ms": 2032,
    "timestamp": "2023-05-15 14:15:02"
  }
}

Example Pre-Process Script:

if playbook.inputs.ms_group_id:
  inputs.ms_group_id = playbook.inputs.ms_group_id

elif playbook.inputs.ms_group_mail_nickname:
  inputs.ms_group_mail_nickname = playbook.inputs.ms_group_mail_nickname

elif playbook.inputs.ms_group_name:
  inputs.ms_group_name = playbook.inputs.ms_group_name

else:
  helper.fail("Atleast one option must be specified to delete a group")

Example Post-Process Script:

FN_NAME = "ms_teams_delete_group"
WF_NAME = "MS Teams: Delete Group (PB)"

results = playbook.functions.results.delete_group
content = results.get("content", {})
group = playbook.inputs.ms_group_name or playbook.inputs.ms_group_mail_nickname or playbook.inputs.ms_group_id

if not results.get("success"):
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Failed for SOAR function <b>{FN_NAME}</b><br>"
  text += f"Unable to delete Microsoft Group: {group}"
  fail_reason = results.get("reason")
  if fail_reason:
    text = f"{text}:\n\tFailure reason: {fail_reason}"
    text += f"\n\tInputs: {results.get('inputs')}"

else:
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Successfully Executed for SOAR function <b>{FN_NAME}</b><br>"
  text += content.get("message")

incident.addNote(helper.createRichText(text))


Function - MS Teams: Enable Team

This function enables an MS Team for a MS Group. When an MS Group is created, the teams’ functionality is not enabled by default. Use this function to enable this for an MS Group that was recently created or for an existing Group.

To identify the Group, one of the following inputs can be used:

  • ms_group_id

  • ms_group_mail_nickname

  • ms_group_name

Note: When multiple parameters are provided to locate the Graph Group, the ms_group_mail_nickname parameter will take precedence over ms_group_name and the ms_group_id parameter will take precedence over the other two.

screenshot: action_enable_teams.png

Inputs:

Name

Type

Required

Example

Tooltip

ms_group_id

text

No

764a62f2-b759-4dd9

Group Unique id

ms_group_mail_nickname

text

No

engineeringTeam

Unique nickname, as no two MS Objects can have the same email ID. The mail address need not include the domain suffix (i.e. @example.com)

ms_group_name

text

No

Engineering Team

Name of the MS Group

Outputs:

NOTE: This example might be in JSON format, but results is a Python Dictionary on the SOAR platform.

results = {
  "version": 2.0,
  "success": true,
  "reason": null,
  "content": {
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups/$entity",
    "id": "3cde21c1-7e9c-4f6d-b986-06d879c43dad",
    "deletedDateTime": null,
    "classification": null,
    "createdDateTime": "2023-05-15T18:13:38Z",
    "creationOptions": [],
    "description": "test1",
    "displayName": "test1",
    "expirationDateTime": null,
    "groupTypes": [
      "Unified"
    ],
    "isAssignableToRole": null,
    "mail": "test1@example.com",
    "mailEnabled": true,
    "mailNickname": "test1",
    "membershipRule": null,
    "membershipRuleProcessingState": null,
    "onPremisesDomainName": null,
    "onPremisesLastSyncDateTime": null,
    "onPremisesNetBiosName": null,
    "onPremisesSamAccountName": null,
    "onPremisesSecurityIdentifier": null,
    "onPremisesSyncEnabled": null,
    "preferredDataLocation": null,
    "preferredLanguage": null,
    "proxyAddresses": [
      "SMTP:test1@example.com"
    ],
    "renewedDateTime": "2023-05-15T18:13:38Z",
    "resourceBehaviorOptions": [],
    "resourceProvisioningOptions": [],
    "securityEnabled": false,
    "securityIdentifier": "S-1-12-1-1021190593-1332575900-3624306361-2906506361",
    "theme": null,
    "visibility": "Private",
    "onPremisesProvisioningErrors": [],
    "status_code": 200,
    "message": "Successfully enabled Teams for Group: test1",
    "teamsEnabled": true
  },
  "raw": null,
  "inputs": {
    "ms_group_id": "3cde21c1-7e9c-4f6d-b986-06d879c43dad"
  },
  "metrics": {
    "version": "1.0",
    "package": "fn-teams",
    "package_version": "2.1.0",
    "host": "local",
    "execution_time_ms": 4333,
    "timestamp": "2023-05-15 14:14:23"
  }
}

Example Pre-Process Script:

if playbook.inputs.ms_group_id:
  inputs.ms_group_id = playbook.inputs.ms_group_id
elif playbook.inputs.ms_group_mail_nickname:
  inputs.ms_group_mail_nickname = playbook.inputs.ms_group_mail_nickname
elif playbook.inputs.ms_group_name:
  inputs.ms_group_name = playbook.inputs.ms_group_name
else:
  helper.fail("No input was provided.")

Example Post-Process Script:

FN_NAME = "ms_teams_enable_team"
WF_NAME = "MS Teams: Enable Teams for Group (PB)"

results = playbook.functions.results.enable_team
content = results.get("content", {})

if not results.get("success"):
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Failed for SOAR function <b>{FN_NAME}</b><br>"
  text += "Could not enable Teams for this MS Group"
  fail_reason = results.get("reason")
  if fail_reason:
    text = f"{text}:\n\tFailure reason: {fail_reason}"
    text += f"\n\tInputs: {results.get('inputs')}"

else:
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Successfully Executed for SOAR function <b>{FN_NAME}</b><br>"
  text += f'''<b>Enabled Microsoft Group Details:</b><br />
  Name: {content.get("displayName")}
  Description: {content.get("description")}
  Teams Enabled: {content.get("teamsEnabled")}
  ID: {content.get("id")}
  Mail: {content.get("mail")}
  Visibility: {content.get("visibility")}
  Group Types: {content.get("groupTypes")}
  Created date and time: {content.get("createdDateTime")}'''
  if content.get("unfoundUsers"):
    text += f'<br />*Note the following users were unable to be added to the group: {content.get("unfoundUsers")}'

incident.addNote(helper.createRichText(text))


Function - MS Teams: List Groups

List available groups

Inputs:

Name

Type

Required

Example

Tooltip

teams_filter

text

No

displayName eq 'Incident 2026'

optional filter values

Outputs:

NOTE: This example might be in JSON format, but results is a Python Dictionary on the SOAR platform.

results = {
  "content": [
    {
      "classification": "None",
      "createdDateTime": "2024-10-16T09:39:39Z",
      "creationOptions": [
        "Team",
        "ExchangeProvisioningFlags:3552"
      ],
      "deletedDateTime": "None",
      "description": "Incident 2386: DOS 11102004",
      "displayName": "msjq01",
      "expirationDateTime": "None",
      "groupTypes": [
        "Unified"
      ],
      "id": "08dcc0f1-80f8-41c5-98b9*****",
      "isAssignableToRole": "None",
      "mail": "msjq01@example.com",
      "mailEnabled": true,
      "mailNickname": "msjq01",
      "membershipRule": "None",
      "membershipRuleProcessingState": "None",
      "onPremisesDomainName": "None",
      "onPremisesLastSyncDateTime": "None",
      "onPremisesNetBiosName": "None",
      "onPremisesProvisioningErrors": [],
      "onPremisesSamAccountName": "None",
      "onPremisesSecurityIdentifier": "None",
      "onPremisesSyncEnabled": "None",
      "preferredDataLocation": "None",
      "preferredLanguage": "None",
      "proxyAddresses": [
        "SPO:SPO_78794e5c-da33-46e1-abdf-6d2c28c2a6c6@SPO_50ad7d3e-b889-434d-802d*****",
        "SMTP:msjq01@example.com"
      ],
      "renewedDateTime": "2024-10-16T09:39:39Z",
      "resourceBehaviorOptions": [
        "HideGroupInOutlook",
        "SubscribeMembersToCalendarEventsDisabled",
        "WelcomeEmailDisabled"
      ],
      "resourceProvisioningOptions": [
        "Team"
      ],
      "securityEnabled": false,
      "securityIdentifier": "S-1-12-1-148685041-1103462648-1283570072*****",
      "serviceProvisioningErrors": [],
      "theme": "None",
      "uniqueName": "None",
      "visibility": "Private"
    }
  ],
  "inputs": {},
  "metrics": {
    "execution_time_ms": 2725,
    "host": "localhost",
    "package": "fn-teams",
    "package_version": "2.3.0",
    "timestamp": "2025-02-20 15:40:15",
    "version": "1.0"
  },
  "raw": "None",
  "reason": "None",
  "success": true,
  "version": 2.0
}

Example Function Input Script:

if hasattr(playbook.inputs, "teams_filter"):
  inputs.teams_filter = playbook.inputs.teams_filter

Example Function Post Process Script:

FN_NAME = "ms_teams_list_groups"
WF_NAME = "MS Teams: List Group (PB)"

results = playbook.functions.results.list_group_result
content = results.get("content", {})

if not results.get("success"):
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Failed for SOAR function <b>{FN_NAME}</b><br>"
  text += f"Unable to List Microsoft Groups"
  fail_reason = results.get("reason")
  if fail_reason:
    text = f"""{text}:\n\tFailure reason: {fail_reason}
    Inputs: {str(playbook.inputs)}"""

else:
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Successfully Executed for SOAR function <b>{FN_NAME}</b><br>"
  for ms_group in content:
    row = incident.addRow("ms_teams_list_groups_teams_channels_datatable")
    row["group_id"] = ms_group.get("id")
    row["group_name"] = ms_group.get("displayName")
    row["group_type"] = ms_group.get("groupTypes")[0]
incident.addNote(helper.createRichText(text))


Function - MS Teams: List Team Channels

List all the channels associated with a Team. A filter can be used to narrow the results

Inputs:

Name

Type

Required

Example

Tooltip

ms_groupteam_id

text

No

-

Unique id assigned to a MS Group or Team while being created

teams_filter

text

No

displayName eq 'General'

optional filter values

Outputs:

NOTE: This example might be in JSON format, but results is a Python Dictionary on the SOAR platform.

results = {
  "content": {
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(microsoft.graph.channel)",
    "@odata.count": 2,
    "value": [
      {
        "@odata.id": "https://graph.microsoft.com/v1.0/tenants/ddd-eee-fff/teams/aaa-bbb-ccc/channels/19:cd40beb34fca44de97d1c01c3839c425@thread.tacv2",
        "createdDateTime": "2025-02-20T19:37:05.225Z",
        "description": null,
        "displayName": "Chat",
        "email": null,
        "id": "19:cd40beb34fca44de97d1c01c3839c425@thread.tacv2",
        "isArchived": false,
        "isFavoriteByDefault": null,
        "membershipType": "standard",
        "tenantId": "ddd-eee-fff",
        "webUrl": "https://teams.microsoft.com/l/channel/11-555-77/Chat?groupId=aaa-bbb-ccc\u0026tenantId=ddd-eee-fff\u0026allowXTenantAccess=True\u0026ngc=True"
      },
      {
        "@odata.id": "https://graph.microsoft.com/v1.0/tenants/11111-2222-333333/teams/aaa-bbb-ccc/channels/abc-def",
        "createdDateTime": "2025-02-19T17:09:46.05Z",
        "description": "some description",
        "displayName": "General",
        "email": "Incident2106@example.com",
        "id": "abc-def",
        "isArchived": false,
        "isFavoriteByDefault": null,
        "membershipType": "standard",
        "tenantId": "ddd-eee-fff",
        "webUrl": "https://teams.microsoft.com/l/channel/abc-def-ghi/Incident%3A%202106?groupId=aaa-bbb-ccc\u0026tenantId=ddd-eee-fff\u0026allowXTenantAccess=True\u0026ngc=True"
      }
    ]
  },
  "reason": "success",
  "success": true
}

Example Function Input Script:

if hasattr(playbook.inputs, "teams_filter"):
  inputs.teams_filter = playbook.inputs.teams_filter
inputs.ms_channel_team_id = playbook.inputs.ms_channel_team_id

Example Function Post Process Script:

FN_NAME = "ms_teams_list_team_channels"
WF_NAME = "MS Teams: List Team Channels (PB)"

results = playbook.functions.results.list_channel_result
content = results.get("content", {})

if not results.get("success"):
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Failed for SOAR function <b>{FN_NAME}</b><br>"
  text += f"Unable to List Microsoft Team Channels"
  fail_reason = results.get("reason")
  if fail_reason:
    text = f"""{text}:\n\tFailure reason: {fail_reason}
    Inputs: {str(playbook.inputs)}"""

else:
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Successfully Executed for SOAR function <b>{FN_NAME}</b><br>"
  for ms_group in content:
    row = incident.addRow("ms_teams_list_groups_teams_channels_datatable")
    row["group_id"] = ms_group.get("id")
    row["group_name"] = ms_group.get("displayName")
    if "groupTypes" in ms_group.keys():
      row["group_type"] = ms_group.get("groupTypes")[0]
incident.addNote(helper.createRichText(text))


Function - MS Teams: List Teams

List available teams

Inputs:

Name

Type

Required

Example

Tooltip

teams_filter

text

No

displayName eq 'Incident 2026'

optional filter values

Outputs:

NOTE: This example might be in JSON format, but results is a Python Dictionary on the SOAR platform.

results = {
  "content": [
    {
      "classification": null,
      "createdDateTime": null,
      "description": "example",
      "discoverySettings": null,
      "displayName": "example",
      "funSettings": null,
      "guestSettings": null,
      "id": "322d20bf-450e-46d6-9338-*****",
      "internalId": null,
      "isArchived": null,
      "isMembershipLimitedToOwners": null,
      "memberSettings": null,
      "messagingSettings": null,
      "specialization": null,
      "summary": null,
      "tagSettings": null,
      "visibility": "public",
      "webUrl": null
    }
  ],
  "reason": "success",
  "success": true
}

Example Function Input Script:

if hasattr(playbook.inputs, "teams_filter"):
  inputs.teams_filter = playbook.inputs.teams_filter

Example Function Post Process Script:

FN_NAME = "ms_teams_list_teams"
WF_NAME = "MS Teams: List Teams (PB)"

results = playbook.functions.results.list_teams_result
content = results.get("content", {})

if not results.get("success"):
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Failed for SOAR function <b>{FN_NAME}</b><br>"
  text += f"Unable to List Microsoft Teams"
  fail_reason = results.get("reason")
  if fail_reason:
    text = f"""{text}:\n\tFailure reason: {fail_reason}
    Inputs: {str(playbook.inputs)}"""

else:
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Successfully Executed for SOAR function <b>{FN_NAME}</b><br>"
  for ms_group in content:
    row = incident.addRow("ms_teams_list_groups_teams_channels_datatable")
    row["group_id"] = ms_group.get("id")
    row["group_name"] = ms_group.get("displayName")
    if "group_type" in ms_group.keys():
      row["group_type"] = ms_group.get("groupTypes")[0]
incident.addNote(helper.createRichText(text))


Function - MS Teams: Post Message

This application posts Incident or Task details to a MS Teams channel. The channel name, specified by the teams_channel parameter, is used to lookup the appropriate channel webhook url maintained in app.config.

screenshot: output_post_teams.png

Inputs:

Name

Type

Required

Example

Tooltip

incident_id

number

No

-

Unique id for the associated Incident

task_id

number

No

-

Unique id for a task if task information will be posted

teams_channel

text

Yes

-

Lookup value of channel to post a message

teams_mrkdown

boolean

Yes

-

-

teams_payload

text

Yes

-

String encoded JSON including incident or task information: sections, title, text, facts

Outputs:

NOTE: This example might be in JSON format, but results is a Python Dictionary on the SOAR platform.

results = {
  "version": 2.0,
  "success": true,
  "reason": null,
  "content": {
    "message": "Information successfully posted in channel General"
  },
  "raw": null,
  "inputs": {
    "teams_channel": "General",
    "incident_id": 2121,
    "teams_mrkdown": true,
    "teams_payload": "{ \"summary\": \"SOAR Incident\", \"sections\": [ \n  { \"facts\": [ \n    { \"name\": \"Name\", \"value\": \"testing channels\" }, \n    { \"name\": \"Description\", \"value\": \"-\" }, \n    { \"name\": \"Id\", \"value\": \"2121\" }, \n    { \"name\": \"Owner\", \"value\": \"i@example.com\" }, \n    { \"name\": \"Types\", \"value\": \"\" }, \n    { \"name\": \"NIST Attack Vectors\", \"value\": \"\" }, \n    { \"name\": \"Create Date\", \"value\": \"1684173230796\" }, \n    { \"name\": \"Date Occurred\", \"value\": \"-\" }, \n    { \"name\": \"Discovered Date\", \"value\": \"1684173210301\" }, \n    { \"name\": \"Confirmed\", \"value\": \"True\" }, \n    { \"name\": \"Severity\", \"value\": \"Low\" } \n   ]\n  }\n ] \n} \n"
  },
  "metrics": {
    "version": "1.0",
    "package": "fn-teams",
    "package_version": "2.1.0",
    "host": "local",
    "execution_time_ms": 2038,
    "timestamp": "2023-05-15 14:21:57"
  }
}

Example Pre-Process Script:

inputs.incident_id = incident.id

"""
format of a payload. * = optional
{ "title"*: xx,
  "summary": xx,
  "sections": [{ "title"*: yy, "text"*: yy,
                        "facts"*: [{"name": zz, "value": zz}]
              }]
}
"""

payload = '''{{ "summary": "SOAR Incident", "sections": [
  {{ "facts": [
    {{ "name": "Name", "value": "{}" }},
    {{ "name": "Description", "value": "{}" }},
    {{ "name": "Id", "value": "{}" }},
    {{ "name": "Owner", "value": "{}" }},
    {{ "name": "Types", "value": "{}" }},
    {{ "name": "NIST Attack Vectors", "value": "{}" }},
    {{ "name": "Create Date", "value": "{}" }},
    {{ "name": "Date Occurred", "value": "{}" }},
    {{ "name": "Discovered Date", "value": "{}" }},
    {{ "name": "Confirmed", "value": "{}" }},
    {{ "name": "Severity", "value": "{}" }}
   ]
  }}
 ]
}}
'''.format(incident.name, incident.description.content.replace('"', '\\"') if incident.description else "-", incident.id,
  incident.owner_id if incident.owner_id else "-",
  ", ".join(str(x) for x in incident.incident_type_ids), ", ".join(str(x) for x in incident.nist_attack_vectors),
  incident.create_date, incident.start_date if incident.start_date else "-", incident.discovered_date,
  "True" if incident.confirmed else "False",
  "-" if not incident.severity_code else incident.severity_code)

inputs.teams_payload = payload
inputs.teams_channel = "General"
inputs.teams_mrkdown = True

Example Post-Process Script:

FN_NAME = "ms_teams_post_message"
WF_NAME = "MS Teams: Post Incident Information (PB)"

results = playbook.functions.results.post_message

if not results.get("success"):
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Failed for SOAR function <b>{FN_NAME}</b><br>"
  text += "Unable to Post message"
  fail_reason = results.get("reason")
  if fail_reason:
    text = f"{text}:\n\tFailure reason: {fail_reason}"
    text += f"\n\tInputs: {results.get('inputs')}"

  incident.addNote(helper.createRichText(text))
else:
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Successfully Executed for SOAR function <b>{FN_NAME}</b><br>"
  text += f"Successfully posted a MS Teams message to channel: {results.get('inputs', {}).get('teams_channel')}"
  incident.addNote(text)


Function - MS Teams: Read Message

Read messages from a Teams Channel

screenshot: action_read_message.png

Inputs:

Name

Type

Required

Example

Tooltip

ms_channel_id

text

No

abs7350fc-b6df-4041

Unique Id assigned to a channel while being created

ms_channel_name

text

No

soarmessages

Name of the Microsoft Teams channel

ms_group_mail_nickname

text

No

soarmailbox

Unique value, as no two MS Objects can have the same email ID. The mail address need not include the domain suffix (i.e. @example.com)

ms_groupteam_id

text

No

db7350fc-b6df-4041

Unique id assigned to a MS Group or Team while being created

ms_groupteam_name

text

No

soar

Name of the MS Team or Group

ms_message_id

text

No

111233344

Each message has a unique ID

Outputs:

NOTE: This example might be in JSON format, but results is a Python Dictionary on the SOAR platform.

results = {
  "version": 2.0,
  "success": true,
  "reason": null,
  "content": [
    {
      "id": "1684173756348",
      "replyToId": null,
      "etag": "1684173756348",
      "messageType": "message",
      "createdDateTime": "2023-05-15T18:02:36.348Z",
      "lastModifiedDateTime": "2023-05-15T18:02:36.348Z",
      "lastEditedDateTime": null,
      "deletedDateTime": null,
      "subject": null,
      "summary": null,
      "chatId": null,
      "importance": "normal",
      "locale": "en-us",
      "webUrl": "https://teams.microsoft.com/l/message/19%3Aa62cab990d8648b6a9047787e030fa7e%40thread.tacv2/1684173756348?groupId=33333-450e-46d6-9338-f7e3b66a064c&tenantId=55555-b889-434d-802d-13b87c68047b&createdTime=1684173756348&parentMessageId=1684173756348",
      "policyViolation": null,
      "eventDetail": null,
      "from": {
        "application": null,
        "device": null,
        "user": {
          "@odata.type": "#microsoft.graph.teamworkUserIdentity",
          "id": "43dd7b73-6a70-475e-88c3-609a9f30b514",
          "displayName": "Mark Scherfling",
          "userIdentityType": "aadUser",
          "tenantId": "55555-b889-434d-802d-13b87c68047b"
        }
      },
      "body": {
        "contentType": "text",
        "content": "hello world"
      },
      "channelIdentity": {
        "teamId": "33333-450e-46d6-9338-f7e3b66a064c",
        "channelId": "19:a62cab990d8648b6a9047787e030fa7e@thread.tacv2"
      },
      "attachments": [],
      "mentions": [],
      "reactions": []
    }
  ],
  "raw": null,
  "inputs": {
    "ms_groupteam_id": "33333-450e-46d6-9338-f7e3b66a064c",
    "ms_channel_name": "Incident 2121 testing channels"
  },
  "metrics": {
    "version": "1.0",
    "package": "fn-teams",
    "package_version": "2.1.0",
    "host": "local",
    "execution_time_ms": 3707,
    "timestamp": "2023-05-15 14:06:30"
  }
}

Example Pre-Process Script:

if playbook.inputs.ms_message_id:
  inputs.ms_message_id = playbook.inputs.ms_message_id
if playbook.inputs.ms_channel_id:
  inputs.ms_channel_id = playbook.inputs.ms_channel_id
if playbook.inputs.ms_groupteam_id:
  inputs.ms_groupteam_id = playbook.inputs.ms_groupteam_id
if playbook.inputs.ms_channel_name:
  inputs.ms_channel_name = playbook.inputs.ms_channel_name
if playbook.inputs.ms_groupteam_id:
  inputs.ms_groupteam_id = playbook.inputs.ms_groupteam_id
if playbook.inputs.ms_group_mail_nickname:
  inputs.ms_group_mail_nickname = playbook.inputs.ms_group_mail_nickname
if playbook.inputs.ms_groupteam_name:
  inputs.ms_groupteam_name = playbook.inputs.ms_groupteam_name

Example Post-Process Script:

FN_NAME = "ms_teams_read_message"
WF_NAME = "MS Teams: Read Channel Messages (PB)"

results = playbook.functions.results.read_message
content = results.get("content", {})
channel = playbook.inputs.ms_channel_name or playbook.inputs.ms_channel_id
group = playbook.inputs.ms_group_mail_nickname or playbook.inputs.ms_groupteam_id or playbook.inputs.ms_groupteam_name

if not results.get("success"):
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Failed for SOAR function <b>{FN_NAME}</b><br>"
  text += f"Unable to read messages from Microsoft Channel: {channel}"
  fail_reason = results.get("reason")
  if fail_reason:
    text = f"{text}:\n\tFailure reason: {fail_reason}"
    text += f"\n\tInputs: {results.get('inputs')}"

else:
  text = f"MS Teams Playbook: <b>{WF_NAME}</b><br>Successfully Executed for SOAR function <b>{FN_NAME}</b><br>"
  text += f"""Successfully retrieved <b>{len(content)}</b> messages from:<br />
  Channel's Group Name - Mail Nickname - Group/Team ID: {group}
  Channel: {channel}<br />"""

  for message in content:
    url = f'''<a href="{message.get("webUrl")}">Click here</a>
    Message Id: {message.get("id")}'''

    text += f'''<br />Message Id : {message.get("id")}
    Created Time: {message.get("createdDateTime")}
    Web URL: <a href="{message.get("webUrl")}">Click here</a>
    '''
    
    if message.get("from"):
      user_messages = message.get("from", {}).get("user", {})
      if user_messages:
        text += f'From User: {user_messages.get("displayName")} <br />'
      else:
        text += f'From Application: {message.get("from", {}).get("application", {}).get("displayName")} <br />'

    if message.get("subject"):
      text += f'''    Subject: {message.get("subject")}<br>{message.get("body", {}).get("content")}'''

incident.addNote(helper.createRichText(text))


Playbooks

Playbook Name

Description

Activation Type

Object

Status

Condition

MS Teams: Archive Team From Task (PB)

None

Manual

task

enabled

-

MS Teams: Archive Team (PB)

None

Manual

incident

enabled

-

MS Teams: Create Channel From Task (PB)

None

Manual

task

enabled

-

MS Teams: Create Channel (PB)

None

Manual

incident

enabled

-

MS Teams: Create Group From Task (PB)

None

Manual

task

enabled

-

MS Teams: Create Group (PB)

None

Manual

incident

enabled

-

MS Teams: Create Team From Task (PB)

None

Manual

task

enabled

-

MS Teams: Create Team (PB)

None

Manual

incident

enabled

-

MS Teams: Delete Channel From Task (PB)

None

Manual

task

enabled

-

MS Teams: Delete Channel (PB)

None

Manual

incident

enabled

-

MS Teams: Delete Group From Task (PB)

None

Manual

task

enabled

-

MS Teams: Delete Group (PB)

None

Manual

incident

enabled

-

MS Teams: Enable Teams for Group From Task (PB)

None

Manual

task

enabled

-

MS Teams: Enable Teams for Group (PB)

None

Manual

incident

enabled

-

MS Teams: Post Incident Information (PB)

None

Manual

incident

enabled

-

MS Teams: Post Message for Workflows

Post a message to a Teams channel using the new Workflows model

Manual

incident

enabled

-

MS Teams: Post Chat Message (using Workflows)

incident

enabled

-

MS Teams: Post Chat Task Message (using Workflows)

task

enabled

-

MS Teams: Post Task Information (PB)

None

Manual

task

enabled

-

MS Teams: Read Channel Messages From task (PB)

None

Manual

task

enabled

-

MS Teams: Read Channel Messages (PB)

None

Manual

incident

enabled

-

MS Teams: List Group (PB)

None

Manual

incident

enabled

-

MS Teams: List Team Channels (PB)

None

Manual

incident

enabled

-

MS Teams: List Teams (PB)

None

Manual

incident

enabled

-



Troubleshooting & Support

Refer to the documentation listed in the Requirements section for troubleshooting information.

For Support

This is a IBM Community provided App. Please search the Community ibm.biz/soarcommunity for assistance.