SecurityAndCompliance Deprecated

This integration allows you to manage and interact with Microsoft security and compliance content search.

Email · Microsoft Exchange Online

Details

IDSecurityAndCompliance
ProviderMicrosoft
CategoryEmail
From Version5.5.0
Docker Imagedemisto/powershell-ubuntu:7.3.0.49844
Supported ModulesAgentix Cloud Runtime Security Cloud Posture Security XSIAM EDR Cortex Cloud

README

This integration enables you to manage and interact with Microsoft security and compliance content search. You can manage the security of all your organization’s emails, SharePoint sites, OneDrives, etc., by searching for text strings or queries based on attributes of a malicious email. However, you can only perform actions (preview and delete) on emails.
This integration was integrated and tested with Security & Compliance Center.

Important Note: Microsoft Office 365 no longer supports Basic Authentication. This version of the Security and Compliance integration will not function correctly beginning January 1st, 2023. Please use O365 Security and Compliance v2 instead.

Use Cases

  • Create / Modify / Get / List / Remove / Trigger a search in the Security & Compliance Center - Content search service.
  • Create / Get / List / Remove search actions in the Security & Compliance Center - Content search service. Supported actions for emails only are Purge (Delete) and Preview.

Playbooks

  • O365 - Security And Compliance - Search And Delete: Creates and starts a compliance search in the Security and Compliance Center to identify emails with similar attributes of a malicious email. If configured, will preview or delete the emails that were located. This playbook uses the following playbooks as sub-playbooks in its workflow.
  • O365 - Security And Compliance - Search: Creates and starts a compliance search in the Security and Compliance Center to identify emails with attributes similar to a malicious email.
  • O365 - Security And Compliance - Search Action - Delete: Deletes emails located by the O365 SecurityAndCompliance Search sub-playbook.
  • O365 - Security And Compliance - Search Action - Preview: Provides a preview of the results of emails located by the O365 SecurityAndCompliance Search sub-playbook.

Permissions in the Security & Compliance Center

To access the Security & Compliance Center, the user account needs to be a global administrator or needs to be assigned the Role Management role (a role is assigned only to the Organization Management role group). The Role Management role allows users to view, create, and modify role groups.

  1. Login into the Security & Compliance Center:

  2. From the side menu, click Permissions.

    side-menu

  3. Search for and select the Data Investigator role.

  4. Click Edit role group.

    roles-edit-1

  5. Click Choose Members and click Edit. Add the user you intend to be used in the integration:

    roles-edit-2

  6. Click Add.
    roles-edit-3
  7. Choose which members to add from the displayed list and click Add.
  8. Click Done.

Enabling Client Side Basic Authentication

Client side basic authentication is necessary for O365 - Security And Compliance and is necessary for Powershell remoting.

Per Microsoft’s documentation, Security & Compliance PowerShell still requires Basic authentication in WinRM as described Prerequisites for the Exchange Online PowerShell module. REST API cmdlets that allow you to turn off Basic authentication in WinRM are not yet available for the Connect-IPPSSession cmdlet. For more information, see Updates for the EXO V3 module).

It is important to note that there are two types of basic authentication. The one that was deprecated which is the server side, and the other version which is the client side basic auth.
What the exchange library does when client side basic auth is enabled is to instead of sending “user:pass” in the headers, it sends “Bearer xyz”. The Exchange client itself, does not know the difference between the headers so when client basic auth is disabled, the bearer token can’t be sent.

Please note: The use of Username and Password is not indicative of the use of basic authentication. The PowerShell session uses modern authentication as noted here.

  1. Create a Group in Active Directory called “Enable Client Basic Auth” and add the user you will use for the integration to the group.
  2. Create a Policy in the Microsoft Endpoint Manager for - This can be found here
  3. Search for “Basic” and you will see the Remote Management dropdown. Under this option, please enable “Client basic authentication”
    cba-role-1
  4. Add the “Enable Client Basic Auth” group to the policy
    cba-role-2
  5. In the Instance Configuration, click the Test button. This will likely return an error with a correlation ID.
  6. Copy this ID and search for the correlation in the Risky Sign-Ins portal found here.
  7. Since we generated this alert, we can confirm that the sign-in is safe. by clicking the option found below.
    cba-role-3
  8. Finally return to the Instance Configuration and click the Test button to confirm the integration works.

Please Note: Microsoft requires that this connection be made from a secure connection. Disabling certificate verification is not supported at this time.

Additional Configuration/Debugging Options

In the event that the above does not result in a successful connection to Security and Compliance, we recommend the following:

  1. Adding the IP address of the XSOAR server to the list of Named Locations.
  2. Configuring the IP address of the XSOAR server as an exemption for multi-factor authentication. https://account.activedirectory.windowsazure.com/usermanagement/mfasettings.aspx?tenantid={YOUR-TENANT-ID}
  3. Dismissing the user’s risk level and state in the Risky Users Portal

Configure SecurityAndCompliance on Cortex XSOAR

  1. Navigate to Settings > Integrations > Servers & Services.

  2. Search for O365 - Security And Compliance - Content Search.

  3. Authentication / Authorization methods:

    1. OAuth2.0 authorization (recommended):

      1. Click Add instance to create and configure a new integration instance.

        Parameter Description Required
        url Search and Compliance URL True
        credentials Fill only Email (aka UPN), Password should be empty. False
        insecure Trust any certificate (not secure) False
      2. Open playground - War-room:

        1. Run the !o365-sc-auth-start command and follow the instructions. Expected output is:

        Security And Compliance - Authorize instructions

        1. To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code XXXXXXX to authenticate.
        2. Run the command !o365-sc-auth-complete command in the War Room.
        1. Test - OAuth2.0 authorization, Run the !o365-sc-auth-test command.
    2. Basic authentication (Not recommended):

      1. Click Add instance to create and configure a new integration instance.

        Parameter Description Required
        url Search and Compliance URL True
        credentials Fill Email (aka UPN) and password False
        insecure Trust any certificate (not secure) False
      2. Click Test to validate the URLs, token, and connection.

Commands

You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

o365-sc-auth-start


OAuth2.0 - Start authorization.

Base Command

o365-sc-auth-start

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

Command Example


#### Human Readable Output

>## Security And Compliance - Authorize instructions
>
>1. To sign in, use a web browser to open the page [https://microsoft.com/devicelogin](https://microsoft.com/devicelogin) and enter the code **XXXXXXX** to authenticate.
>2. Run the ***!o365-sc-auth-complete*** command in the War Room.

### o365-sc-auth-complete

***
OAuth2.0 - Complete authorization.

#### Base Command

`o365-sc-auth-complete`

#### Input

There are no input arguments for this command.

#### Context Output

There is no context output for this command.

#### Command Example

```!o365-sc-auth-complete```

#### Human Readable Output

>Your account **successfully** authorized!

### o365-sc-auth-test

***
OAuth2.0 - Test authorization.

#### Base Command

`o365-sc-auth-test`

#### Input

There are no input arguments for this command.

#### Context Output

There is no context output for this command.

#### Command Example

```!o365-sc-auth-test```

#### Human Readable Output

>**Test ok!**

### o365-sc-new-search

***
Create compliance search in the Security & Compliance Center.

#### Base Command

`o365-sc-new-search`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| search_name | The name of the compliance search. If not specified, will have the prefix "XSOAR-" followed by the GUID e.g., XSOAR-d6228fd0-756b-4e4b-8721-76776df91526. | Required |
| case | The name of a Core eDiscovery case to associate with the new compliance search. | Optional |
| kql | Text search string or a query that is formatted using the Keyword Query Language (KQL). [Tips for finding messages to remove using KQL](#tips-for-finding-messages-to-remove)
| Optional |
| description | Description of the compliance search. | Optional |
| allow_not_found_exchange_locations | Whether to include mailboxes other than regular user mailboxes in the compliance search. Default is "false". | Optional |
| exchange_location | Comma-separated list of mailboxes/distribution groups to include, or you can use the value "All" to include all. | Optional |
| exchange_location_exclusion | Comma-separated list of mailboxes/distribution groups to exclude when you use the value "All" for the exchange_location parameter. | Optional |
| public_folder_location | Comma-separated list of public folders to include, or you can use the value "All" to include all. | Optional |
| share_point_location | Comma-separated list of SharePoint online sites to include. You can identify the sites by their URL value, or you can use the value "All" to include all sites. | Optional |
| share_point_location_exclusion | Comma-separated list of SharePoint online sites to exclude when you use the value "All" for the share_point_location argument. You can identify the sites by their URL value. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| O365.SecurityAndCompliance.ContentSearch.Search.AllowNotFoundExchangeLocationsEnabled | Boolean | Whether to include mailboxes other than regular user mailboxes in the compliance search. |
| O365.SecurityAndCompliance.ContentSearch.Search.AzureBatchFrameworkEnabled | Boolean | Whether the Azure Batch Framework is enabled for job processing. |
| O365.SecurityAndCompliance.ContentSearch.Search.CaseId | String | Identity of a Core eDiscovery case which is associated with the compliance search. |
| O365.SecurityAndCompliance.ContentSearch.Search.CaseName | String | Name of a Core eDiscovery case which is associated with the compliance search. |
| O365.SecurityAndCompliance.ContentSearch.Search.ContentMatchQuery | String | Compliance text search string or a query that is formatted using the Keyword Query Language \(KQL\). |
| O365.SecurityAndCompliance.ContentSearch.Search.CreatedBy | String | Security and compliance search creator. |
| O365.SecurityAndCompliance.ContentSearch.Search.CreatedTime | Date | Security and compliance search creation time. |
| O365.SecurityAndCompliance.ContentSearch.Search.Description | String | Security and compliance search description. |
| O365.SecurityAndCompliance.ContentSearch.Search.Errors | String | Security and compliance search errors. |
| O365.SecurityAndCompliance.ContentSearch.Search.ExchangeLocation | String | Security and compliance search exchange locations to include. |
| O365.SecurityAndCompliance.ContentSearch.Search.Identity | String | Security and compliance search identity. |
| O365.SecurityAndCompliance.ContentSearch.Search.IsValid | Boolean | Whether the security and compliance search is valid. |
| O365.SecurityAndCompliance.ContentSearch.Search.Items | Number | The number of security and compliance search scanned items. |
| O365.SecurityAndCompliance.ContentSearch.Search.JobEndTime | Date | Security and compliance search job end time. |
| O365.SecurityAndCompliance.ContentSearch.Search.JobId | String | Security and compliance search job ID. |
| O365.SecurityAndCompliance.ContentSearch.Search.JobRunId | String | Security and compliance search job run ID. |
| O365.SecurityAndCompliance.ContentSearch.Search.JobStartTime | Date | Security and compliance search job run start time. |
| O365.SecurityAndCompliance.ContentSearch.Search.LastModifiedTime | Date | Security and compliance search last modification time. |
| O365.SecurityAndCompliance.ContentSearch.Search.LogLevel | String | Security and compliance search Azure log level. |
| O365.SecurityAndCompliance.ContentSearch.Search.Name | String | Security and compliance search name. |
| O365.SecurityAndCompliance.ContentSearch.Search.OneDriveLocation | String | Security and compliance search OneDrive locations to include. |
| O365.SecurityAndCompliance.ContentSearch.Search.OneDriveLocationExclusion | String | Security and compliance search OneDrive locations to exclude. |
| O365.SecurityAndCompliance.ContentSearch.Search.PublicFolderLocation | String | Security and compliance search public folder locations to include. |
| O365.SecurityAndCompliance.ContentSearch.Search.PublicFolderLocationExclusion | String | Security and compliance search public folder locations to exclude. |
| O365.SecurityAndCompliance.ContentSearch.Search.RunBy | String | Security and compliance search last run by UPN \(Email representation\). |
| O365.SecurityAndCompliance.ContentSearch.Search.RunspaceId | String | Security and compliance search run space ID. |
| O365.SecurityAndCompliance.ContentSearch.Search.SharePointLocation | String | Security and compliance search SharePoint locations to include. |
| O365.SecurityAndCompliance.ContentSearch.Search.Size | Number | Security and compliance search bytes results size. |
| O365.SecurityAndCompliance.ContentSearch.Search.Status | String | Security and compliance search status. |
| O365.SecurityAndCompliance.ContentSearch.Search.TenantId | String | Security and compliance search Tenant ID. |

#### Command Example

```!o365-sc-new-search search_name="example" exchange_location="user1@demistodev.onmicrosoft.com,user2@demistodev.onmicrosoft.com" allow_not_found_exchange_locations=true kql="Rodrigo"```

#### Context Example

```json
{
    "O365": {
        "SecurityAndCompliance": {
            "ContentSearch": {
                "Search": {
                    "AllowNotFoundExchangeLocationsEnabled": true,
                    "AzureBatchFrameworkEnabled": false,
                    "CaseId": null,
                    "CaseName": "",
                    "ContentMatchQuery": "Rodrigo",
                    "CreatedBy": "XSOAR-user",
                    "CreatedTime": "2020-11-29T07:12:46.5943533Z",
                    "Description": "Short description",
                    "Errors": null,
                    "ExchangeLocation": [
                        "test1@onmicrosoft.com",
                        "test2@onmicrosoft.com"
                    ],
                    "ExchangeLocationExclusion": [],
                    "Identity": "xxxxx",
                    "IsValid": true,
                    "Items": 0,
                    "JobEndTime": null,
                    "JobId": "xxxxx",
                    "JobRunId": null,
                    "JobStartTime": null,
                    "LastModifiedTime": "2020-11-29T07:12:46.5943533Z",
                    "LogLevel": "Suppressed",
                    "Name": "example",
                    "OneDriveLocation": null,
                    "OneDriveLocationExclusion": null,
                    "PublicFolderLocation": null,
                    "PublicFolderLocationExclusion": null,
                    "RunBy": "",
                    "RunspaceId": null,
                    "SharePointLocation": null,
                    "SharePointLocationExclusion": null,
                    "Size": 0,
                    "Status": "NotStarted",
                    "SuccessResults": null,
                    "TenantId": "xxxxx"
                }
            }
        }
    }
}

Human Readable Output

Security And Compliance - New search ‘example’ created

ContentMatchQuery CreatedBy Description LastModifiedTime Name
Rodrigo XSOAR-user Short description 11/29/2020 7:12:46 AM example

o365-sc-set-search


Modifies non-running compliance searches in the Security & Compliance Center.

Base Command

o365-sc-set-search

Input

Argument Name Description Required
search_name The name of the compliance search. Required
kql Modify the text search string or a query that is formatted using the Keyword Query Language (KQL). Optional
description Modify the description for the compliance search. Optional
allow_not_found_exchange_locations Whether to include mailboxes other than regular user mailboxes in the compliance search. Optional
add_exchange_location Comma-separated list of added mailboxes/distribution groups to include, or you can use the value “All” to include all mailboxes. Optional
add_exchange_location_exclusion Comma-separated list of added mailboxes/distribution groups to exclude when you use the value “All” for the exchange_location (used in create new compliance search) or the add_exchange_location argument. Optional
add_public_folder_location Comma-separated list of added public folders to include, or you can use the value “All” to include all. Optional
add_share_point_location Comma-separated list of added SharePoint online sites to include. You identify the sites by their URL value, or you can use the value “All” to include all sites. Optional
add_share_point_location_exclusion Comma-separated list of added SharePoint online sites to exclude when you use the value “All” for the exchange_location (used in create new compliance search) argument or the share_point_location argument. You can identify the sites by their URL value. Optional
remove_exchange_location Comma-separated list of removed mailboxes/distribution group to include. Optional
remove_exchange_location_exclusion Comma-separated list of removed mailboxes/distribution group to exclude when you use the value “All” for the exchange_location (Used in create new compliance search) or the add_exchange_location argument. Optional
remove_public_folder_location Comma-separated list of removed public folders to include. Optional
remove_share_point_location Comma-separated list of removed SharePoint online sites to include. You can identify the sites by their URL value. Optional
remove_share_point_location_exclusion Comma-separated list of removed SharePoint online sites to exclude when you use the value “All” for the exchange_location (Used in create new compliance search) argument or the share_point_location argument. You can identify the sites by their URL value. Optional

Context Output

There is no context output for this command.

Command Example

!o365-sc-set-search search_name="example" remove_exchange_location="test2@demistodev.onmicrosoft.com"

Human Readable Output

Security And Compliance - Search example modified!

o365-sc-remove-search


Remove compliance search by name from the Security & Compliance Center.

Base Command

o365-sc-remove-search

Input

Argument Name Description Required
search_name The name of the compliance search. Required

Context Output

There is no context output for this command.

Command Example

!o365-sc-remove-search search_name="example"

Human Readable Output

Security And Compliance - Search example removed!

o365-sc-list-search


List compliance searches in the Security & Compliance Center.

Base Command

o365-sc-list-search

Input

There are no input arguments for this command.

Context Output

Path Type Description
O365.SecurityAndCompliance.ContentSearch.Search.AllowNotFoundExchangeLocationsEnabled Boolean Whether to include mailboxes other than regular user mailboxes in the compliance search.
O365.SecurityAndCompliance.ContentSearch.Search.AzureBatchFrameworkEnabled Boolean Whether the Azure Batch Framework is enabled for job processing.
O365.SecurityAndCompliance.ContentSearch.Search.CaseId String Identity of a Core eDiscovery case which is associated with the compliance search.
O365.SecurityAndCompliance.ContentSearch.Search.CaseName String Name of a Core eDiscovery case which is associated with the compliance search.
O365.SecurityAndCompliance.ContentSearch.Search.ContentMatchQuery String Compliance text search string or a query that is formatted using the Keyword Query Language (KQL).
O365.SecurityAndCompliance.ContentSearch.Search.CreatedBy String Security and compliance search creator.
O365.SecurityAndCompliance.ContentSearch.Search.CreatedTime Date Security and compliance search creation time.
O365.SecurityAndCompliance.ContentSearch.Search.Description String Security and compliance search description.
O365.SecurityAndCompliance.ContentSearch.Search.Errors String Security and compliance search errors.
O365.SecurityAndCompliance.ContentSearch.Search.ExchangeLocation String Security and compliance search exchange locations to include.
O365.SecurityAndCompliance.ContentSearch.Search.Identity String Security and compliance search identity.
O365.SecurityAndCompliance.ContentSearch.Search.IsValid Boolean Whether the security and compliance search is valid.
O365.SecurityAndCompliance.ContentSearch.Search.Items Number The number of security and compliance search scanned items.
O365.SecurityAndCompliance.ContentSearch.Search.JobEndTime Date Security and compliance search job end time.
O365.SecurityAndCompliance.ContentSearch.Search.JobId String Security and compliance search job ID.
O365.SecurityAndCompliance.ContentSearch.Search.JobRunId String Security and compliance search job run ID.
O365.SecurityAndCompliance.ContentSearch.Search.JobStartTime Date Security and compliance search job run start time.
O365.SecurityAndCompliance.ContentSearch.Search.LastModifiedTime Date Security and compliance search last modification time.
O365.SecurityAndCompliance.ContentSearch.Search.LogLevel String Security and compliance search Azure log level.
O365.SecurityAndCompliance.ContentSearch.Search.Name String Security and compliance search name.
O365.SecurityAndCompliance.ContentSearch.Search.OneDriveLocation String Security and compliance search OneDrive locations to include.
O365.SecurityAndCompliance.ContentSearch.Search.OneDriveLocationExclusion String Security and compliance search OneDrive locations to exclude.
O365.SecurityAndCompliance.ContentSearch.Search.PublicFolderLocation String Security and compliance search public folder locations to include.
O365.SecurityAndCompliance.ContentSearch.Search.PublicFolderLocationExclusion String Security and compliance search public folder locations to exclude.
O365.SecurityAndCompliance.ContentSearch.Search.RunBy String Security and compliance search last run by UPN (Email representation).
O365.SecurityAndCompliance.ContentSearch.Search.RunspaceId String Security and compliance search run space ID.
O365.SecurityAndCompliance.ContentSearch.Search.SharePointLocation String Security and compliance search SharePoint locations to include.
O365.SecurityAndCompliance.ContentSearch.Search.Size Number Security and compliance search bytes results size.
O365.SecurityAndCompliance.ContentSearch.Search.Status String Security and compliance search status.
O365.SecurityAndCompliance.ContentSearch.Search.TenantId String Security and compliance search Tenant ID.

Command Example


#### Context Example

```json
{
    "O365": {
        "SecurityAndCompliance": {
            "ContentSearch": {
                "Search": [
                    {
                        "AllowNotFoundExchangeLocationsEnabled": false,
                        "AzureBatchFrameworkEnabled": false,
                        "CaseId": null,
                        "CaseName": "",
                        "ContentMatchQuery": "subject:test",
                        "CreatedBy": "XSOAR-user1",
                        "CreatedTime": "2019-08-22T06:43:48.747",
                        "Description": "Short description",
                        "Errors": null,
                        "ExchangeLocation": null,
                        "ExchangeLocationExclusion": null,
                        "Identity": "xxxxx",
                        "IsValid": true,
                        "Items": 0,
                        "JobEndTime": "2019-09-05T13:21:11.563",
                        "JobId": "xxxx",
                        "JobRunId": null,
                        "JobStartTime": "2019-09-05T13:20:34.633",
                        "LastModifiedTime": "2019-08-22T06:43:48.747",
                        "LogLevel": "Suppressed",
                        "Name": "example1",
                        "OneDriveLocation": null,
                        "OneDriveLocationExclusion": null,
                        "PublicFolderLocation": null,
                        "PublicFolderLocationExclusion": null,
                        "RunBy": "XSOAR-user1",
                        "RunspaceId": null,
                        "SharePointLocation": null,
                        "SharePointLocationExclusion": null,
                        "Size": 0,
                        "Status": "Completed",
                        "SuccessResults": null,
                        "TenantId": "xxxx"
                    },
                    {
                        "AllowNotFoundExchangeLocationsEnabled": false,
                        "AzureBatchFrameworkEnabled": false,
                        "CaseId": null,
                        "CaseName": "",
                        "ContentMatchQuery": "subject:Incident Summary Report",
                        "CreatedBy": "XSOAR-user2",
                        "CreatedTime": "2020-01-08T00:44:30.94",
                        "Description": "Short description",
                        "Errors": null,
                        "ExchangeLocation": null,
                        "ExchangeLocationExclusion": null,
                        "Identity": "xxxxx",
                        "IsValid": true,
                        "Items": 0,
                        "JobEndTime": "2020-01-08T00:45:13.433",
                        "JobId": "xxxxx",
                        "JobRunId": null,
                        "JobStartTime": "2020-01-08T00:44:33.717",
                        "LastModifiedTime": "2020-01-08T00:44:30.94",
                        "LogLevel": "Suppressed",
                        "Name": "example2",
                        "OneDriveLocation": null,
                        "OneDriveLocationExclusion": null,
                        "PublicFolderLocation": null,
                        "PublicFolderLocationExclusion": null,
                        "RunBy": "XSOAR-user2",
                        "RunspaceId": null,
                        "SharePointLocation": null,
                        "SharePointLocationExclusion": null,
                        "Size": 0,
                        "Status": "Completed",
                        "SuccessResults": null,
                        "TenantId": "xxxxx"
                    }
                ]
            }
        }
    }
}

Human Readable Output

Security And Compliance - Search configurations

CreatedBy Description LastModifiedTime Name RunBy
XSOAR-user1 Short description 8/22/2019 6:43:48 AM example1 XSOAR-user1
XSOAR-user2 Short description 1/8/2020 12:44:30 AM example2 XSOAR-user2

o365-sc-get-search


Gets compliance search by name from the Security & Compliance Center.

Base Command

o365-sc-get-search

Input

Argument Name Description Required
search_name The name of the compliance search. Required
limit The maximum number of results to return. If you want to return all requests that match the query, use “-1” for the value of this argument. Optional
all_results Whether to include mailboxes which have no results in results entry context. Optional
export Whether to export search results as json file to war-room. Optional
statistics Show search statistics. Default is “false”. Optional

Context Output

Path Type Description
O365.SecurityAndCompliance.ContentSearch.Search.AllowNotFoundExchangeLocationsEnabled Boolean Whether to include mailboxes other than regular user mailboxes in the compliance search.
O365.SecurityAndCompliance.ContentSearch.Search.AzureBatchFrameworkEnabled Boolean Whether the Azure Batch Framework is enabled for job processing.
O365.SecurityAndCompliance.ContentSearch.Search.CaseId String Identity of a Core eDiscovery case which is associated with the compliance search.
O365.SecurityAndCompliance.ContentSearch.Search.CaseName String Name of a Core eDiscovery case which is associated with the compliance search.
O365.SecurityAndCompliance.ContentSearch.Search.ContentMatchQuery String Compliance text search string or a query that is formatted using the Keyword Query Language (KQL).
O365.SecurityAndCompliance.ContentSearch.Search.CreatedBy String Security and compliance search creator.
O365.SecurityAndCompliance.ContentSearch.Search.CreatedTime Date Security and compliance search creation time.
O365.SecurityAndCompliance.ContentSearch.Search.Description String Security and compliance search description.
O365.SecurityAndCompliance.ContentSearch.Search.Errors String Security and compliance search errors.
O365.SecurityAndCompliance.ContentSearch.Search.ExchangeLocation String Security and compliance search exchange locations to include.
O365.SecurityAndCompliance.ContentSearch.Search.Identity String Security and compliance search identity.
O365.SecurityAndCompliance.ContentSearch.Search.IsValid Boolean Whether the security and compliance search is valid.
O365.SecurityAndCompliance.ContentSearch.Search.Items Number Number of security and compliance search scanned items.
O365.SecurityAndCompliance.ContentSearch.Search.JobEndTime Date Security and compliance search job end time.
O365.SecurityAndCompliance.ContentSearch.Search.JobId String Security and compliance search job ID.
O365.SecurityAndCompliance.ContentSearch.Search.JobRunId String Security and compliance search job run ID.
O365.SecurityAndCompliance.ContentSearch.Search.JobStartTime Date Security and compliance search job run start time.
O365.SecurityAndCompliance.ContentSearch.Search.LastModifiedTime Date Security and compliance search last modification time.
O365.SecurityAndCompliance.ContentSearch.Search.LogLevel String Security and compliance search the Azure log level.
O365.SecurityAndCompliance.ContentSearch.Search.Name String Security and compliance search name.
O365.SecurityAndCompliance.ContentSearch.Search.OneDriveLocation String Security and compliance search OneDrive locations to include.
O365.SecurityAndCompliance.ContentSearch.Search.OneDriveLocationExclusion String Security and compliance search OneDrive locations to exclude.
O365.SecurityAndCompliance.ContentSearch.Search.PublicFolderLocation String Security and compliance search public folder locations to include.
O365.SecurityAndCompliance.ContentSearch.Search.PublicFolderLocationExclusion String Security and compliance search public folder locations to exclude.
O365.SecurityAndCompliance.ContentSearch.Search.RunBy String Security and compliance search last run by UPN (Email representation).
O365.SecurityAndCompliance.ContentSearch.Search.RunspaceId String Security and compliance search run space ID.
O365.SecurityAndCompliance.ContentSearch.Search.SharePointLocation String Security and compliance search SharePoint locations to include.
O365.SecurityAndCompliance.ContentSearch.Search.Size Number Security and compliance search bytes results size.
O365.SecurityAndCompliance.ContentSearch.Search.Status String Security and compliance search status.
O365.SecurityAndCompliance.ContentSearch.Search.TenantId String Security and compliance search Tenant ID.
O365.SecurityAndCompliance.ContentSearch.Search.SuccessResults.Location String Security and compliance search result location.
O365.SecurityAndCompliance.ContentSearch.Search.SuccessResults.ItemsCount Number The number of security and compliance search results in location.
O365.SecurityAndCompliance.ContentSearch.Search.SuccessResults.Size Number The byte size of the security and compliance search results in location.

Command Example

!o365-sc-get-search search_name="example"

Context Example

{
    "O365": {
        "SecurityAndCompliance": {
            "ContentSearch": {
                "Search": {
                    "Errors": "", 
                    "AzureBatchFrameworkEnabled": false,
                    "TenantId": "xxxxx", 
                    "SharePointLocationExclusion": null, 
                    "JobStartTime": "2020-11-29T07:20:59.37", 
                    "CreatedTime": "2020-11-29T07:18:04.283", 
                    "OneDriveLocation": null, 
                    "PublicFolderLocation": null, 
                    "Status": "Completed", 
                    "CaseName": "", 
                    "AllowNotFoundExchangeLocationsEnabled": false, 
                    "LogLevel": "Suppressed", 
                    "JobRunId": null, 
                    "CaseId": null, 
                    "JobId": "xxxxx", 
                    "SuccessResults": {
                        "ItemsCount": "122303", 
                        "Location": "user@onmicrosoft.com", 
                        "Size": "12339007379"
                    }, 
                    "LastModifiedTime": "2020-11-29T07:20:43.283", 
                    "Identity": "1d130723-fe0a-4726-6d2a-08d89437520e", 
                    "Name": "example", 
                    "Items": 122303, 
                    "ExchangeLocation": [
                        "user@onmicrosoft.com"
                    ], 
                    "RunBy": "XSOAR-user", 
                    "Description": "Short description", 
                    "ExchangeLocationExclusion": [], 
                    "IsValid": true, 
                    "PublicFolderLocationExclusion": null, 
                    "SharePointLocation": null, 
                    "CreatedBy": "XSOAR-user", 
                    "JobEndTime": "2020-11-29T07:22:01.99", 
                    "RunspaceId": null, 
                    "Size": 12339007379, 
                    "OneDriveLocationExclusion": null, 
                    "ContentMatchQuery": "Rodrigo"
                }
            }
        }
    }
}

Human Readable Output

Security And Compliance - ‘example’ search

CreatedBy Description LastModifiedTime Name RunBy Status
XSOAR-user Short description 2020-11-29T07:20:43.283 example XSOAR-user NotStarted

o365-sc-start-search


Starts stopped, completed, or not started compliance search in the Security & Compliance Center.

Base Command

o365-sc-start-search

Input

Argument Name Description Required
search_name The name of the compliance search. Required

Context Output

There is no context output for this command.

Command Example

!o365-sc-start-search search_name="example"

Human Readable Output

Security And Compliance - search example started !

o365-sc-stop-search


Stop running compliance search in the Security & Compliance Center.

Base Command

o365-sc-stop-search

Input

Argument Name Description Required
search_name The name of the compliance search. Required

Context Output

There is no context output for this command.

Command Example

!o365-sc-stop-search search_name="example"

Human Readable Output

Security And Compliance - search example stopped !

o365-sc-new-search-action


After you create a content search using the o365-sc-new-search command and run it using the o365-sc-start-search command, you assign a search action to the search using the o365-sc-new-search-action command.

Base Command

o365-sc-new-search-action

Input

Argument Name Description Required
search_name The name of the compliance search. Required
action Search action to perform. Possible values are: “Preview” and “Purge”. Default is “Preview”. Optional
purge_type Purge type. Possible values are: “Soft Delete” and “HardDelete”. Default is “SoftDelete”. Optional

Context Output

Path Type Description
O365.SecurityAndCompliance.ContentSearch.SearchAction.Action String Security and compliance search action type. Either “Purge” or “Preview”.
O365.SecurityAndCompliance.ContentSearch.SearchAction.AllowNotFoundExchangeLocationsEnabled Boolean Whether to include mailboxes other than regular user mailboxes in the compliance search.
O365.SecurityAndCompliance.ContentSearch.SearchAction.AzureBatchFrameworkEnabled Boolean Whether the Azure Batch Framework is enabled for job processing.
O365.SecurityAndCompliance.ContentSearch.SearchAction.CaseId String Identity of a Core eDiscovery case which is associated with the compliance search.
O365.SecurityAndCompliance.ContentSearch.SearchAction.CaseName String Name of a Core eDiscovery case which is associated with the compliance search.
O365.SecurityAndCompliance.ContentSearch.SearchAction.CreatedBy String Security and compliance search action creator.
O365.SecurityAndCompliance.ContentSearch.SearchAction.CreatedTime Date Security and compliance search action creation time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Description String Security and compliance search action description.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Errors String Security and compliance search action errors.
O365.SecurityAndCompliance.ContentSearch.SearchAction.EstimateSearchJobId String Security and compliance search action job ID estimation.
O365.SecurityAndCompliance.ContentSearch.SearchAction.EstimateSearchRunId String Security and compliance search action run ID estimation.
O365.SecurityAndCompliance.ContentSearch.SearchAction.ExchangeLocation String Security and compliance search action exchange locations to include.
O365.SecurityAndCompliance.ContentSearch.SearchAction.ExchangeLocationExclusion String Security and compliance search action exchange locations to exclude.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Identity String Security and compliance search action identity.
O365.SecurityAndCompliance.ContentSearch.SearchAction.IsValid Boolean Whether the security and compliance search action is valid.
O365.SecurityAndCompliance.ContentSearch.SearchAction.JobEndTime Date Security and compliance search action job end time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.JobId String Security and compliance search action job ID.
O365.SecurityAndCompliance.ContentSearch.SearchAction.JobRunId String Security and compliance search action job run ID.
O365.SecurityAndCompliance.ContentSearch.SearchAction.JobStartTime Date Security and compliance search action job start time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.LastModifiedTime Date Security and compliance search action last modified time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Name String Security and compliance search action name.
O365.SecurityAndCompliance.ContentSearch.SearchAction.PublicFolderLocation String Security and compliance search action public folder locations to include.
O365.SecurityAndCompliance.ContentSearch.SearchAction.PublicFolderLocationExclusion String Security and compliance search action public folder locations to exclude.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Retry Boolean Whether to retry if the search action failed.
O365.SecurityAndCompliance.ContentSearch.SearchAction.RunBy String Security and compliance search action run by UPN (email address).
O365.SecurityAndCompliance.ContentSearch.SearchAction.RunspaceId String Security and compliance search action run space ID.
O365.SecurityAndCompliance.ContentSearch.SearchAction.SearchName String Security and compliance search action search name.
O365.SecurityAndCompliance.ContentSearch.SearchAction.SharePointLocation String Security and compliance search action SharePoint locations to include.
O365.SecurityAndCompliance.ContentSearch.SearchAction.SharePointLocationExclusion String Security and compliance search action SharePoint locations to exclude.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Status String Security and compliance search action status. Either “Started” or “Completed”.
O365.SecurityAndCompliance.ContentSearch.SearchAction.TenantId String Security and compliance search action Tenant ID.

Command Example

!o365-sc-new-search-action search_name="example" action="Preview"

Context Example

{
    "O365": {
        "SecurityAndCompliance": {
            "ContentSearch": {
                "SearchAction": {
                    "Action": "Preview",
                    "AllowNotFoundExchangeLocationsEnabled": false,
                    "AzureBatchFrameworkEnabled": false,
                    "CaseId": null,
                    "CaseName": "",
                    "CreatedBy": "XSOAR-user",
                    "CreatedTime": "2020-11-29T07:23:50.05",
                    "Description": "",
                    "Errors": "",
                    "EstimateSearchJobId": "xxxxx",
                    "EstimateSearchRunId": "xxxxx",
                    "ExchangeLocation": [
                        "user@onmicrosoft.com"
                    ],
                    "ExchangeLocationExclusion": null,
                    "Identity": "xxxxx",
                    "IsValid": true,
                    "JobEndTime": "2020-11-29T07:24:05.76",
                    "JobId": "xxxxx",
                    "JobRunId": "xxxxx",
                    "JobStartTime": "2020-11-29T07:23:50.297",
                    "LastModifiedTime": "2020-11-29T07:23:50.05",
                    "Name": "example_Preview",
                    "PublicFolderLocation": null,
                    "PublicFolderLocationExclusion": null,
                    "Results": null,
                    "Retry": false,
                    "RunBy": "XSOAR-user",
                    "RunspaceId": "xxxxx",
                    "SearchName": "example",
                    "SharePointLocation": null,
                    "SharePointLocationExclusion": null,
                    "Status": "Completed",
                    "TenantId": "xxxxx"
                }
            }
        }
    }
}

Human Readable Output

Security And Compliance - search action ‘example_Preview’ created

Action LastModifiedTime Name RunBy SearchName Status
Preview 11/29/2020 7:23:50 AM example_Preview XSOAR-user example Completed

o365-sc-remove-search-action


Removes compliance search action by search the action name from the Security & Compliance Center.

Base Command

o365-sc-remove-search-action

Input

Argument Name Description Required
search_action_name The name of the compliance search action. Required

Context Output

There is no context output for this command.

Command Example

!o365-sc-remove-search-action search_action_name="example_Preview"

Human Readable Output

Security And Compliance - search action example_Preview removed!

o365-sc-list-search-action


Lists compliance search actions from the Security & Compliance Center.

Base Command

o365-sc-list-search-action

Input

There are no input arguments for this command.

Context Output

Path Type Description
O365.SecurityAndCompliance.ContentSearch.SearchAction.Action String Security and compliance search action type. Either “Purge or “Preview”.
O365.SecurityAndCompliance.ContentSearch.SearchAction.AllowNotFoundExchangeLocationsEnabled Boolean Whether to include mailboxes other than regular user mailboxes in the compliance search.
O365.SecurityAndCompliance.ContentSearch.SearchAction.AzureBatchFrameworkEnabled Boolean Whether the Azure Batch Framework is enabled for job processing.
O365.SecurityAndCompliance.ContentSearch.SearchAction.CaseId String Identity of a Core eDiscovery case which is associated with the compliance search.
O365.SecurityAndCompliance.ContentSearch.SearchAction.CaseName String Name of a Core eDiscovery case which is associated with the compliance search.
O365.SecurityAndCompliance.ContentSearch.SearchAction.CreatedBy String Security and compliance search action creator.
O365.SecurityAndCompliance.ContentSearch.SearchAction.CreatedTime Date Security and compliance search action creation time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Description String Security and compliance search action description.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Errors String Security and compliance search action errors.
O365.SecurityAndCompliance.ContentSearch.SearchAction.EstimateSearchJobId String Security and compliance search action job ID estimation.
O365.SecurityAndCompliance.ContentSearch.SearchAction.EstimateSearchRunId String Security and compliance search action run ID estimation.
O365.SecurityAndCompliance.ContentSearch.SearchAction.ExchangeLocation String Security and compliance search action exchange locations to include.
O365.SecurityAndCompliance.ContentSearch.SearchAction.ExchangeLocationExclusion String Security and compliance search action exchange locations to exclude.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Identity String Security and compliance search action identity.
O365.SecurityAndCompliance.ContentSearch.SearchAction.IsValid Boolean Whether the security and compliance search action is valid.
O365.SecurityAndCompliance.ContentSearch.SearchAction.JobEndTime Date Security and compliance search action job end time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.JobId String Security and compliance search action job ID.
O365.SecurityAndCompliance.ContentSearch.SearchAction.JobRunId String Security and compliance search action job run ID.
O365.SecurityAndCompliance.ContentSearch.SearchAction.JobStartTime Date Security and compliance search action job start time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.LastModifiedTime Date Security and compliance search action last modified time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Name String Security and compliance search action name.
O365.SecurityAndCompliance.ContentSearch.SearchAction.PublicFolderLocation String Security and compliance search action public folder locations to include.
O365.SecurityAndCompliance.ContentSearch.SearchAction.PublicFolderLocationExclusion String Security and compliance search action public folder locations to exclude.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Retry Boolean Whether to retry if the search action failed.
O365.SecurityAndCompliance.ContentSearch.SearchAction.RunBy String Security and compliance search action run by UPN (email address).
O365.SecurityAndCompliance.ContentSearch.SearchAction.RunspaceId String Security and compliance search action run space ID.
O365.SecurityAndCompliance.ContentSearch.SearchAction.SearchName String Security and compliance search action search name.
O365.SecurityAndCompliance.ContentSearch.SearchAction.SharePointLocation String Security and compliance search action SharePoint locations to include.
O365.SecurityAndCompliance.ContentSearch.SearchAction.SharePointLocationExclusion String Security and compliance search action SharePoint locations to exclude.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Status String Security and compliance search action status (Started/Completed).
O365.SecurityAndCompliance.ContentSearch.SearchAction.TenantId String Security and compliance search action Tenant ID.

Command Example


#### Context Example

```json
{
    "O365": {
        "SecurityAndCompliance": {
            "ContentSearch": {
                "SearchAction": [
                    {
                        "Action": "Preview",
                        "AllowNotFoundExchangeLocationsEnabled": false,
                        "AzureBatchFrameworkEnabled": false,
                        "CaseId": null,
                        "CaseName": "",
                        "CreatedBy": "XSOAR-user",
                        "CreatedTime": "2020-10-14T13:45:44.14",
                        "Description": "",
                        "Errors": "",
                        "EstimateSearchJobId": "xxxxx",
                        "EstimateSearchRunId": "xxxxx",
                        "ExchangeLocation": null,
                        "ExchangeLocationExclusion": null,
                        "Identity": "xxxxx",
                        "IsValid": true,
                        "JobEndTime": "2020-10-14T13:47:00.103",
                        "JobId": "xxxxx",
                        "JobRunId": "xxxxx",
                        "JobStartTime": "2020-10-14T13:45:58.443",
                        "LastModifiedTime": "2020-10-14T13:45:44.14",
                        "Name": "example_Preview",
                        "PublicFolderLocation": null,
                        "PublicFolderLocationExclusion": null,
                        "Results": null,
                        "Retry": false,
                        "RunBy": "XSOAR-user",
                        "RunspaceId": "xxxxx",
                        "SearchName": "example",
                        "SharePointLocation": null,
                        "SharePointLocationExclusion": null,
                        "Status": "Completed",
                        "TenantId": "xxxxx"
                    },
                   {
                        "Action": "Purge",
                        "AllowNotFoundExchangeLocationsEnabled": false,
                        "AzureBatchFrameworkEnabled": false,
                        "CaseId": null,
                        "CaseName": "",
                        "CreatedBy": "XSOAR-user1",
                        "CreatedTime": "2020-10-14T13:45:44.14",
                        "Description": "",
                        "Errors": "",
                        "EstimateSearchJobId": "xxxxx",
                        "EstimateSearchRunId": "xxxxx",
                        "ExchangeLocation": null,
                        "ExchangeLocationExclusion": null,
                        "Identity": "xxxxx",
                        "IsValid": true,
                        "JobEndTime": "2020-10-14T13:47:00.103",
                        "JobId": "xxxxx",
                        "JobRunId": "xxxxx",
                        "JobStartTime": "2020-10-14T13:45:58.443",
                        "LastModifiedTime": "2020-10-14T13:45:44.14",
                        "Name": "example_Purge",
                        "PublicFolderLocation": null,
                        "PublicFolderLocationExclusion": null,
                        "Results": null,
                        "Retry": false,
                        "RunBy": "XSOAR-user1",
                        "RunspaceId": "xxxxx",
                        "SearchName": "Rodrigo-diffrent",
                        "SharePointLocation": null,
                        "SharePointLocationExclusion": null,
                        "Status": "Completed",
                        "TenantId": "xxxxx"
                    }
                ]
            }
        }
    }
}

Human Readable Output

Security And Compliance - search actions

Action JobEndTime LastModifiedTime Name RunBy SearchName Status
Preview 10/14/2020 1:47:00 PM 10/14/2020 1:45:44 PM example_Preview XSOAR-user example Completed
Purge 11/25/2020 10:51:04 AM 11/25/2020 10:50:37 AM example_Purge XSOAR-user example Completed

o365-sc-get-search-action


Gets compliance search action from the Security & Compliance Center.

Base Command

o365-sc-get-search-action

Input

Argument Name Description Required
search_action_name The name of the compliance search action. Required
limit The maximum number of results to return. If you want to return all requests that match the query, use “-1” for the value of this argument. Optional
export Whether to export search results as json file to war-room. Optional
results Whether to print the results in the War Room. Default is “false”. Optional

Context Output

Path Type Description
O365.SecurityAndCompliance.ContentSearch.SearchAction.Action String Security and compliance search action type. Either “Purge” or “Preview”.
O365.SecurityAndCompliance.ContentSearch.SearchAction.AllowNotFoundExchangeLocationsEnabled Boolean Whether to include mailboxes other than regular user mailboxes in the compliance search.
O365.SecurityAndCompliance.ContentSearch.SearchAction.AzureBatchFrameworkEnabled Boolean Whether the Azure Batch Framework is enabled for job processing.
O365.SecurityAndCompliance.ContentSearch.SearchAction.CaseId String Identity of a Core eDiscovery case which is associated with the compliance search.
O365.SecurityAndCompliance.ContentSearch.SearchAction.CaseName String Name of a Core eDiscovery case which is associated with the compliance search.
O365.SecurityAndCompliance.ContentSearch.SearchAction.CreatedBy String Security and compliance search action creator.
O365.SecurityAndCompliance.ContentSearch.SearchAction.CreatedTime Date Security and compliance search action creation time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Description String Security and compliance search action description.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Errors String Security and compliance search action errors.
O365.SecurityAndCompliance.ContentSearch.SearchAction.EstimateSearchJobId String Security and compliance search action job ID estimation.
O365.SecurityAndCompliance.ContentSearch.SearchAction.EstimateSearchRunId String Security and compliance search action run ID estimation.
O365.SecurityAndCompliance.ContentSearch.SearchAction.ExchangeLocation String Security and compliance search action exchange locations to include.
O365.SecurityAndCompliance.ContentSearch.SearchAction.ExchangeLocationExclusion String Security and compliance search action exchange locations to exclude.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Identity String Security and compliance search action identity.
O365.SecurityAndCompliance.ContentSearch.SearchAction.IsValid Boolean Whether the security and compliance search action is valid.
O365.SecurityAndCompliance.ContentSearch.SearchAction.JobEndTime Date Security and compliance search action job end time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.JobId String Security and compliance search action job ID.
O365.SecurityAndCompliance.ContentSearch.SearchAction.JobRunId String Security and compliance search action job run ID.
O365.SecurityAndCompliance.ContentSearch.SearchAction.JobStartTime Date Security and compliance search action job start time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.LastModifiedTime Date Security and compliance search action last modified time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Name String Security and compliance search action name.
O365.SecurityAndCompliance.ContentSearch.SearchAction.PublicFolderLocation String Security and compliance search action public folder locations to include.
O365.SecurityAndCompliance.ContentSearch.SearchAction.PublicFolderLocationExclusion String Security and compliance search action public folder locations to exclude.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Results.Location String Security and compliance search action result location.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Results.ItemCount String Security and compliance search action result item count.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Results.TotalSize String Security and compliance search action result total size.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Results.FailedCount String Security and compliance search action result failed count.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Results.Sender String Security and compliance search action result mail sender.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Results.Subject String Security and compliance search action result subject.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Results.Type String Security and compliance search action result type.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Results.Size String Security and compliance search action result size.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Results.ReceivedTime Date Security and compliance search action result received time.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Results.DataLink String Security and compliance search action data link.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Retry Boolean Whether to retry if the search action failed.
O365.SecurityAndCompliance.ContentSearch.SearchAction.RunBy String Security and compliance search action run by UPN (email address).
O365.SecurityAndCompliance.ContentSearch.SearchAction.RunspaceId String Security and compliance search action run space ID.
O365.SecurityAndCompliance.ContentSearch.SearchAction.SearchName String Security and compliance search action search name.
O365.SecurityAndCompliance.ContentSearch.SearchAction.SharePointLocation String Security and compliance search action SharePoint locations to include.
O365.SecurityAndCompliance.ContentSearch.SearchAction.SharePointLocationExclusion String Security and compliance search action SharePoint locations to exclude.
O365.SecurityAndCompliance.ContentSearch.SearchAction.Status String Security and compliance search action status. Either “Started” or “Completed”.
O365.SecurityAndCompliance.ContentSearch.SearchAction.TenantId String Security and compliance search action Tenant ID.

Command Example

!o365-sc-get-search-action search_action_name="example_Preview"

Context Example

{
    "O365": {
        "SecurityAndCompliance": {
            "ContentSearch": {
                "SearchAction": {
                    "Action": "Preview",
                    "AllowNotFoundExchangeLocationsEnabled": false,
                    "AzureBatchFrameworkEnabled": false,
                    "CaseId": null,
                    "CaseName": "",
                    "CreatedBy": "XSOAR-user",
                    "CreatedTime": "2020-11-29T07:23:50.05",
                    "Description": "",
                    "Errors": "",
                    "EstimateSearchJobId": "2a967c40-07c3-4903-ed7f-08d89436e9b1",
                    "EstimateSearchRunId": "1d130723-fe0a-4726-6d2a-08d89437520e",
                    "ExchangeLocation": [
                        "user@onmicrosoft.com"
                    ],
                    "ExchangeLocationExclusion": null,
                    "Identity": "xxxxx",
                    "IsValid": true,
                    "JobEndTime": "2020-11-29T07:24:05.76",
                    "JobId": "xxxxx",
                    "JobRunId": "xxxxx",
                    "JobStartTime": "2020-11-29T07:23:50.297",
                    "LastModifiedTime": "2020-11-29T07:23:50.05",
                    "Name": "example_Preview",
                    "PublicFolderLocation": null,
                    "PublicFolderLocationExclusion": null,
                    "Results": [
                        {
                            "DataLink": "data/All/xxxx.eml",
                            "Location": "user@onmicrosoft.com",
                            "ReceivedTime": "11/26/2020 2:59:01 PM",
                            "Sender": "Some user",
                            "Size": "19683",
                            "Subject": "Test1",
                            "Type": "Email"
                        },
                       {
                            "DataLink": "data/All/xxxx.eml",
                            "Location": "user@onmicrosoft.com",
                            "ReceivedTime": "12/26/2020 2:59:01 PM",
                            "Sender": "Some user",
                            "Size": "20225",
                            "Subject": "Test2",
                            "Type": "Email"
                        },
                    ],
                    "Retry": false,
                    "RunBy": "XSOAR-user",
                    "RunspaceId": "xxxxx",
                    "SearchName": "example",
                    "SharePointLocation": null,
                    "SharePointLocationExclusion": null,
                    "Status": "Completed",
                    "TenantId": "xxxxx"
                }
            }
        }
    }
}

Human Readable Output

Security And Compliance - search action ‘example_Preview’

Action JobEndTime LastModifiedTime Name RunBy SearchName Status
Preview 11/29/2020 7:24:05 AM 11/29/2020 7:23:50 AM example_Preview XSOAR-user example Completed

Tips for finding messages to remove

  • Keyword Query Language (KQL)
    • If you know the exact text or phrase used in the subject line of the message, use the Subject property in the search query, e.g., (subject:give me all ur money).
    • If you know that exact date (or date range) of the message, include the Received property in the search query, e.g., (received:6/13/2021..6/16/2021).
    • If you know who sent the message, include the From property in the search query, e.g., (from:user1@demistodev.onmicrosoft.com).
    • For all the available search properties see: Keyword queries and search conditions for eDiscovery.
  • Preview the search results to verify that the search returned only the message (or messages) that you want to delete.
  • Use the search estimate statistics (displayed by using the o365-sc-get-search command) to get a count of the total number of emails.

Known Limitations

  • Security and compliance integrations do not support Security and compliance on-premise.
  • Each security and compliance command creates a PSSession (PowerShell session). The security and compliance PowerShell limits the number of concurrent sessions to 3. Since this affects the behavior of multiple playbooks running concurrently it we recommend that you retry failed tasks when using the integration commands in playbooks.
  • Proxies are not supported due to a Microsoft limitation.
  • Due to a Microsoft limitation, you can perform a search and purge operation on a maximum of 50,000 mailboxes. To work around this limitation, configure multiple instances of the integration each with different permission filtering so that the number of mailboxes in each instance does not exceed 50,000.
  • A maximum of 10 items per mailbox can be removed at one time, due to a Microsoft limitiation.
  • For more Microsoft known limitations see Limits for eDiscovery search.

Configuration parameters

  • url — Search and Compliance URL (required)
  • credentials — Email / User Principal Name (Required)
  • insecure — Trust any certificate (not secure)

Commands (14)

  • o365-sc-auth-complete

    OAuth2.0 - Complete authorization.

  • o365-sc-auth-start

    OAuth2.0 - Start authorization.

  • o365-sc-auth-test

    OAuth2.0 - Test authorization.

  • o365-sc-get-search

    Gets compliance search by name from the Security & Compliance Center.

  • o365-sc-get-search-action

    Gets compliance search action from the Security & Compliance Center.

  • o365-sc-list-search

    List compliance searches in the Security & Compliance Center.

  • o365-sc-list-search-action

    Lists compliance search actions from the Security & Compliance Center.

  • o365-sc-new-search

    Create compliance search in the Security & Compliance Center.

  • o365-sc-new-search-action

    After you create a content search using the o365-sc-new-search command and run it using the o365-sc-start-search command, you assign a search action to the search using the o365-sc-new-search-action command.

  • o365-sc-remove-search

    Remove compliance search by name from the Security & Compliance Center.

  • o365-sc-remove-search-action

    Removes a compliance search action by searching for the action name in the Security & Compliance Center.

  • o365-sc-set-search

    Modifies non-running compliance searches in the Security & Compliance Center.

  • o365-sc-start-search

    Starts stopped, completed, or not started compliance search in the Security & Compliance Center.

  • o365-sc-stop-search

    Stop running compliance search in the Security & Compliance Center.

. $PSScriptRoot\CommonServerPowerShell.ps1

$script:INTEGRATION_NAME = "Security And Compliance"
$script:COMMAND_PREFIX = "o365-sc"
$script:INTEGRATION_ENTRY_CONTEX = "O365.SecurityAndCompliance.ContentSearch"
$script:SEARCH_ENTRY_CONTEXT = "$script:INTEGRATION_ENTRY_CONTEX.Search(val.Name && val.Name == obj.Name)"
$script:SEARCH_ACTION_ENTRY_CONTEXT = "$script:INTEGRATION_ENTRY_CONTEX.SearchAction(val.Name && val.Name == obj.Name)"

#### HELPER FUNCTIONS ####

function UpdateIntegrationContext([OAuth2DeviceCodeClient]$client){
    $integration_context = @{
        "DeviceCode" = $client.device_code
        "DeviceCodeExpiresIn" = $client.device_code_expires_in
        "DeviceCodeCreationTime" = $client.device_code_creation_time
        "AccessToken" = $client.access_token
        "RefreshToken" = $client.refresh_token
        "AccessTokenExpiresIn" = $client.access_token_expires_in
        "AccessTokenCreationTime" = $client.access_token_creation_time
    }

    SetIntegrationContext $integration_context
    <#
        .DESCRIPTION
        Update integration context from OAuth2DeviceCodeClient client

        .EXAMPLE
        UpdateIntegrationContext $client

        .PARAMETER search_name
        OAuth2DeviceCodeClient client.
    #>
}

function GetRedirectUri {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Scope='Function')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '', Scope='Function')]
    param([string]$url, [string]$upn, [string]$password, [string]$bearer_token, [bool]$insecure, [bool]$proxy)
    $end_uri = $url
    if ($password){
        $end_uri = "$url/powershell-liveid/"
    }
    elseif ($bearer_token) {
        $token_value = ConvertTo-SecureString "Bearer $bearer_token" -AsPlainText -Force
        $credential = New-Object System.Management.Automation.PSCredential($upn, $token_value)
        $params = @{
            "URI" = "$url/powershell-liveid?BasicAuthToOAuthConversion=true;PSVersion=7.0.3"
            "Method" = "Post"
            "Credential" = $credential
            "NoProxy" = !$proxy
            "SkipCertificateCheck" = $insecure
            "MaximumRedirection" = 0
        }
        try {
            Invoke-WebRequest @params
        }
        catch {
            if ($_.Exception.Response.StatusCode -eq "Redirect") {
                $end_uri = $_.Exception.Response.Headers.Location.AbsoluteUri
            } else {
                throw $_.Exception
            }
        }
    }

    return $end_uri
    <#
        .DESCRIPTION
        Solve Bug - When using bearer token the new-pssession unable to get redirect url for establishing pssession.
        This function get redirect URI by interacting with WSMAN directly via Rest-API.


        .PARAMETER uri
        Security & Compliance Center uri.

        .PARAMETER upn
        User Principal Name (UPN) is the name of a system user in an email address format.

        .PARAMETER password
        Password is filled only if authentication method is basic auth.

        .PARAMETER bearer_token
        Valid bearer token value.

        .EXAMPLE proxy
        Wheter to user system proxy configuration or not.

        .PARAMETER insecure
        Wheter to trust any TLS/SSL Certificate) or not.

        .OUTPUTS
        [string] Redirect uri if redirected.

        .LINK
        https://github.com/PowerShell/PowerShell/issues/12563
    #>
}

function CreateNewSession {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Scope='Function')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '', Scope='Function')]
    param([string]$url, [string]$upn, [string]$password, [string]$bearer_token, [bool]$insecure, [bool]$proxy)

    $url = GetRedirectUri -url $url -upn $upn -password $password -bearer_token $bearer_token -insecure $insecure -proxy $proxy

    if ($password){
        $credential = ConvertTo-SecureString "$password" -AsPlainText -Force
    } else {
        $credential = ConvertTo-SecureString "Bearer $bearer_token" -AsPlainText -Force
    }
    $credential = New-Object System.Management.Automation.PSCredential($upn, $credential)
    $session_option_params = @{
        "SkipCACheck" = $insecure
        "SkipCNCheck" = $insecure
    }
    $session_options =  New-PSSessionOption @session_option_params
    $sessions_params = @{
        "ConfigurationName" = "Microsoft.Exchange"
        "ConnectionUri" = $url
        "Credential" = $credential
        "Authentication" = "Basic"
        "AllowRedirection" = $true
        "SessionOption" = $session_options
    }
    $session = New-PSSession @sessions_params -WarningAction:SilentlyContinue

    if (!$session) {
        throw "Fail - establishing session to $url"
    }

    return $session
    <#
        .DESCRIPTION
        Creates new pssession using Oauth2.0 method.

        .PARAMETER uri
        Security & Compliance Center uri.

        .PARAMETER upn
        User Principal Name (UPN) is the name of a system user in an email address format.

        .PARAMETER password
        Password is filled only if authentication method is basic auth.

        .PARAMETER bearer_token
        Valid bearer token value.

        .EXAMPLE proxy
        Wheter to user system proxy configuration or not.

        .PARAMETER insecure
        Wheter to trust any TLS/SSL Certificate) or not.


        .EXAMPLE
        CreateNewSession("outlook.com", "user@microsoft.com", "dfhsdkjhkjhvkdvbihsgiu")

        .OUTPUTS
        PSSession - PSSession object.

        .LINK
        https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/new-pssession?view=powershell-7
    #>
}

function ParseSuccessResults([string]$success_results, [int]$limit, [bool]$all_results) {
    $parsed_success_results = New-Object System.Collections.Generic.List[System.Object]
    if ($success_results) {
        $lines = $success_results.Split([Environment]::NewLine)

        if ($limit -ne -1) {
            $limit = ($limit, $lines.Count | Measure-Object -Minimum).Minimum
        } else {
            $limit = $lines.Count
        }

        # Results limit
        $results_count = 0
        # Lines iterator
        $lines_scanned = 0
        while ($results_count -lt $limit -and $lines_scanned -lt $lines.Count) {
            if ($lines[$lines_scanned] -match 'Location: (\S+), Item count: (\d+), Total size: (\d+)')
            {
                if ($matches[2] -ne 0 -or $all_results){
                    $parsed_success_results.Add(@{
                        "Location" = $matches[1]
                        "ItemsCount" = $matches[2]
                        "Size" = $matches[3]
                    })
                    $results_count += 1
                }
            }
            $lines_scanned += 1
        }
    }

    return $parsed_success_results
    <#
        .DESCRIPTION
        Parse string return in Search PSObject property "SuccessResults"

        .PARAMETER success_results
        SuccessResults raw string.

        .EXAMPLE
        ParseSuccessResults 'Location: Private mail box, Item count: 8, Total size: 63'

        .OUTPUTS
        List of psobject SuccessResults object.
    #>
}



function ParseResults([string]$results, [int]$limit = -1, [string]$type = "Preview") {
   if ($type -eq "Preview"){
        $results_matches_preview = (Select-String -AllMatches "\{?Location: (.*); Sender: (.*); Subject: (.*); Type: (.*); Size: (.*); Received Time: (.*); Data Link: (.*)[},]"  -InputObject $results).Matches
        $parsed_results = New-Object System.Collections.Generic.List[System.Object]
        foreach ($match in $results_matches_preview)
        {
            if ($parsed_results.Count -ge $limit -and $limit -ne -1){
                break
            }

            $parsed_results.Add(@{
                "Location" = $match.Groups[1].Value
                "Sender" = $match.Groups[2].Value
                "Subject" = $match.Groups[3].Value
                "Type" = $match.Groups[4].Value
                "Size" = $match.Groups[5].Value
                "ReceivedTime" = $match.Groups[6].Value
                "DataLink" = $match.Groups[7].Value
            })
        }
   }
    if ($type -eq "Purge"){
        $results_matches_purge = (Select-String -AllMatches "\{?Location: (.*); Item count: (.*); Total size: (.*); Failed count: (.*); [},]"  -InputObject $results).Matches
        $parsed_results = New-Object System.Collections.Generic.List[System.Object]
        foreach ($match in $results_matches_purge)
        {
            if ($parsed_results.Count -ge $limit -and $limit -ne -1){
                break
            }
            $parsed_results.Add(@{
                "Location" = $match.Groups[1].Value
                "ItemCount" = $match.Groups[2].Value
                "TotalSize" = $match.Groups[3].Value
                "FailedCount" = $match.Groups[4].Value
            })
        }
    }


    return $parsed_results
    <#
        .DESCRIPTION
        Parse string return in SearchAction PSObject property "Results"

        .PARAMETER success_results
        SuccessResults raw string.

        .EXAMPLE
        ParseResults 'Location: Private mail box; Sender: user@microsoft.com; Type: mail; Size: 100; Received Time: 16 August 2010; Data Link: xxxxx,'

        .OUTPUTS
        List of psobject Results object.
    #>
}

function ParseSearchToEntryContext([psobject]$search, [int]$limit = -1, [bool]$all_results = $false) {
    return @{
        "AllowNotFoundExchangeLocationsEnabled" = $search.AllowNotFoundExchangeLocationsEnabled
        "AzureBatchFrameworkEnabled" = $search.AzureBatchFrameworkEnabled
        "CaseId" = $search.CaseId
        "CaseName" = $search.CaseName
        "ContentMatchQuery" = $search.ContentMatchQuery
        "CreatedBy" = $search.CreatedBy
        "CreatedTime" = $search.CreatedTime
        "Description" = $search.Description
        "Errors" = $search.Errors
        "ExchangeLocation" = $search.ExchangeLocation
        "ExchangeLocationExclusion" = $search.ExchangeLocationExclusion
        "Identity" = $search.Identity
        "IsValid" = $search.IsValid
        "Items" = $search.Items
        "JobEndTime" = $search.JobEndTime
        "JobId" = $search.JobId
        "JobRunId" = $searchJobRunId
        "JobStartTime" = $search.JobStartTime
        "LastModifiedTime" = $search.LastModifiedTime
        "LogLevel" = $search.LogLevel
        "Name" = $search.Name
        "OneDriveLocation" = $search.OneDriveLocation
        "OneDriveLocationExclusion" = $search.OneDriveLocationExclusion
        "PublicFolderLocation" = $search.PublicFolderLocation
        "PublicFolderLocationExclusion" = $search.PublicFolderLocationExclusion
        "RunBy" = $search.RunBy
        "RunspaceId" = $search_action.RunspaceId
        "SharePointLocation" = $search.SharePointLocation
        "SharePointLocationExclusion" = $search.SharePointLocationExclusion
        "Size" = $search.Size
        "Status" = $search.Status
        "SuccessResults" = ParseSuccessResults -success_results $search.SuccessResults -limit $limit -all_results $all_results
        "TenantId" = $search.TenantId
    }
    <#
        .DESCRIPTION
        Parse Search raw response PSObject to Entry Context.

        .PARAMETER search
        search raw psobject.

        .PARAMETER all_results
        Whether to include also not found locations.

        .PARAMETER limit
        Limit found items.

        .EXAMPLE
        ParseSearchToEntryContext $search

        .OUTPUTS
        Search entry context.

        .Notes
        1. Microsoft internal properties: OneDriveLocationExclusion, OneDriveLocation.
        2. SuccessResults property return as string which should be parsed.
    #>
}

function ParseSearchActionToEntryContext([psobject]$search_action, [int]$limit = -1) {
    return @{
        "Action" = $search_action.Action
        "AllowNotFoundExchangeLocationsEnabled" = $search_action.AllowNotFoundExchangeLocationsEnabled
        "AzureBatchFrameworkEnabled" = $search_action.AzureBatchFrameworkEnabled
        "CaseId" = $search_action.CaseId
        "CaseName" = $search_action.CaseName
        "CreatedBy" = $search_action.CreatedBy
        "CreatedTime" = $search_action.CreatedTime
        "Description" = $search_action.Description
        "Errors" = $search_action.Errors
        "EstimateSearchJobId"  = $search_action.EstimateSearchJobId
        "EstimateSearchRunId" = $search_action.EstimateSearchRunId
        "ExchangeLocation" = $search_action.ExchangeLocation
        "ExchangeLocationExclusion" = $search_action.ExchangeLocationExclusion
        "Identity" = $search_action.Identity
        "IsValid" = $search_action.IsValid
        "JobEndTime" = $search_action.JobEndTime
        "JobId" = $search_action.JobId
        "JobRunId" = $search_action.JobRunId
        "JobStartTime" = $search_action.JobStartTime
        "LastModifiedTime" = $search_action.LastModifiedTime
        "PublicFolderLocation" = $search_action.PublicFolderLocation
        "PublicFolderLocationExclusion" = $search_action.PublicFolderLocationExclusion
        "Retry" = $search_action.Retry
        "RunspaceId" = $search_action.RunspaceId
        "SharePointLocation" = $search_action.SharePointLocation
        "SharePointLocationExclusion" = $search_action.SharePointLocationExclusion
        "Name" = $search_action.Name
        "RunBy" = $search_action.RunBy
        "SearchName" = $search_action.SearchName
        "Status" = $search_action.Status
        "TenantId" = $search_action.TenantId
        "Results" = ParseResults -results $search_action.Results -limit $limit -type $search_action.Action
    }
    <#
        .DESCRIPTION
        Parse SearchAction raw response PSObject to Entry Context.

        .PARAMETER search
        SearchAction raw response.

        .EXAMPLE
        ParseSearchActionToEntryContext $search_action

        .OUTPUTS
        SearchAction entry context.

        .Notes
        1. Microsoft internal properties: OneDriveLocationExclusion, OneDriveLocation.
        2. Results property return as string which should be parsed.
    #>
}

#### OAUTH2.0 CLIENT - DEVICE CODE FLOW #####

class OAuth2DeviceCodeClient {
    [string]$application_id = "a0c73c16-a7e3-4564-9a95-2bdf47383716"
    [string]$application_scope = "offline_access%20https%3A//outlook.office365.com/.default"
    [string]$device_code
    [int]$device_code_expires_in
    [int]$device_code_creation_time
    [string]$access_token
    [string]$refresh_token
    [int]$access_token_expires_in
    [int]$access_token_creation_time
    [bool]$insecure
    [bool]$proxy

    OAuth2DeviceCodeClient([string]$device_code, [string]$device_code_expires_in, [string]$device_code_creation_time, [string]$access_token,
                            [string]$refresh_token,[string]$access_token_expires_in, [string]$access_token_creation_time, [bool]$insecure, [bool]$proxy) {
        $this.device_code = $device_code
        $this.device_code_expires_in = $device_code_expires_in
        $this.device_code_creation_time = $device_code_creation_time
        $this.access_token = $access_token
        $this.refresh_token = $refresh_token
        $this.access_token_expires_in = $access_token_expires_in
        $this.access_token_creation_time = $access_token_creation_time
        $this.insecure = $insecure
        $this.proxy = $proxy
        <#
            .DESCRIPTION
            OAuth2DeviceCodeClient manage state of OAuth2.0 device-code flow described in https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code.

            .DESCRIPTION
            Its not recomended to create an object using the constructor, Use static method CreateClientFromIntegrationContext() instead.

            OAuth2DeviceCodeClient states are:
                1. Getting device-code (Will be used in stage 2) and user-code (Will be used by the user to authorize permissions) from Microsoft application.
                2. Getting access-token and refresh-token - after use authorize (Using stage 1 - device code)
                3. Refresh access-token if access-token is expired.

            .PARAMETER device_code
            A long string used to verify the session between the client and the authorization server.
            The client uses this parameter to request the access token from the authorization server.

            .PARAMETER device_code_expires_in
            The number of seconds before the device_code and user_code expire. (15 minutes)

            .PARAMETER access_token
            Opaque string, Issued for the scopes that were requested.

            .PARAMETER refresh_token
            Opaque string, Issued if the original scope parameter included offline_access. (Valid for 90 days)

            .PARAMETER access_token_expires_in
            Number of seconds before the included access token is valid for. (Usally - 60 minutes)

            .PARAMETER access_token_creation_time
            Unix time of access token creation (Used for knowing when to refresh the token).

            .PARAMETER access_token_expires_in
            Number of seconds before the included access token is valid for. (Usally - 60 minutes)

            .PARAMETER insecure
            Wheter to trust any TLS/SSL Certificate) or not.

            .PARAMETER proxy
            Wheter to user system proxy configuration or not.

            .NOTES
            1. Application id - a0c73c16-a7e3-4564-9a95-2bdf47383716 , This is well-known application publicly managed by Microsoft and will not work in on-premise enviorment.

            .LINK
            https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code
        #>
    }

    static [OAuth2DeviceCodeClient]CreateClientFromIntegrationContext([bool]$insecure, [bool]$proxy){
        $ic = GetIntegrationContext
        $client = [OAuth2DeviceCodeClient]::new($ic.DeviceCode, $ic.DeviceCodeExpiresIn, $ic.DeviceCodeCreationTime, $ic.AccessToken, $ic.RefreshToken,
                                                $ic.AccessTokenExpiresIn, $ic.AccessTokenCreationTime, $insecure, $proxy)

        return $client
        <#
            .DESCRIPTION
            Static method which create object (factory method) from populated values in integration context.

            .EXAMPLE
            [OAuth2DeviceCodeClient]::CreateClientFromIntegrationContext()

            .OUTPUTS
            OAuth2DeviceCodeClient initialized object.
        #>
    }

    [PSObject]AuthorizationRequest() {
        # Reset object-properties
        $this.device_code = $null
        $this.device_code_expires_in = $null
        $this.device_code_creation_time = $null
        # Get device-code and user-code
        $params = @{
            "URI" = "https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode"
            "Method" = "Post"
            "Headers" = (New-Object "System.Collections.Generic.Dictionary[[String],[String]]").Add("Content-Type", "application/x-www-form-urlencoded")
            "Body" = "client_id=$($this.application_id)&scope=$($this.application_scope)"
            "NoProxy" = !$this.proxy
            "SkipCertificateCheck" = $this.insecure
        }
        $response = Invoke-WebRequest @params
        $response_body = ConvertFrom-Json $response.Content
        # Update object properties
        $this.device_code = $response_body.device_code
        $this.device_code_creation_time = [int][double]::Parse((Get-Date -UFormat %s))
        $this.device_code_expires_in = [int]::Parse($response_body.expires_in)

        return $response_body

        <#
            .DESCRIPTION
            Reset values populated in instance context and getting new device-code and user-code.

            .EXAMPLE
            $client.AuthorizationRequest()

            .OUTPUTS
            psobject - Raw body response.

            .LINK
            https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code#device-authorization-request
        #>
    }

    [psobject]AccessTokenRequest() {
        # Get new token using device-code
        try {
            $params = @{
                "URI" = "https://login.microsoftonline.com/organizations/oauth2/v2.0/token"
                "Method" = "Post"
                "Headers" = (New-Object "System.Collections.Generic.Dictionary[[String],[String]]").Add("Content-Type", "application/x-www-form-urlencoded")
                "Body" = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code&code=$($this.device_code)&client_id=$($this.application_id)"
                "NoProxy" = !$this.proxy
                "SkipCertificateCheck" = $this.insecure
            }
            $response = Invoke-WebRequest @params
            $response_body = ConvertFrom-Json $response.Content
        }
        catch {
            $response_body = ConvertFrom-Json $_.ErrorDetails.Message
            if ($response_body.error -eq "authorization_pending" -or $response_body.error -eq "invalid_grant") {
                $error_details = "Please run command !$script:COMMAND_PREFIX-auth-start , before running this command."
            }
            elseif ($response_body.error -eq "expired_token") {
                $error_details = "At least $($this.access_token_expires_in) seconds have passed from executing !$script:COMMAND_PREFIX-auth-start, Please run the ***$script:COMMAND_PREFIX-auth-start*** command again."
            } else {
                $error_details = $response_body
            }

            throw "Unable to get access token for your account, $error_details"
        }
        # Update object properties
        $this.access_token = $response_body.access_token
        $this.refresh_token = $response_body.refresh_token
        $this.access_token_expires_in = [int]::Parse($response_body.expires_in)
        $this.access_token_creation_time = [int][double]::Parse((Get-Date -UFormat %s))

        return $response_body

        <#
            .DESCRIPTION
            Getting access-token and refresh-token from Microsoft application based on the device-code we go from AuthorizationRequest() method.

            .EXAMPLE
            $client.AccessTokenRequest()

            .OUTPUTS
            psobject - Raw body response.

            .LINK
            https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code#authenticating-the-user
        #>
    }

    [psobject]RefreshTokenRequest() {
        # Get new token using refresh token
        try {
            $params = @{
                "URI" = "https://login.microsoftonline.com/organizations/oauth2/v2.0/token"
                "Method" = "Post"
                "Headers" = (New-Object "System.Collections.Generic.Dictionary[[String],[String]]").Add("Content-Type", "application/x-www-form-urlencoded")
                "Body" = "grant_type=refresh_token&client_id=$($this.application_id)&refresh_token=$($this.refresh_token)&scope=$($this.application_scope)"
                "NoProxy" = !$this.proxy
                "SkipCertificateCheck" = $this.insecure
            }
            $response = Invoke-WebRequest @params
            $response_body = ConvertFrom-Json $response.Content
        }
        catch {
            $response_body = ConvertFrom-Json $_.ErrorDetails.Message
            $error_details = "Unable to refresh access token for your account"

            # AADSTS50173 points to password change https://login.microsoftonline.com/error?code=50173.
            # In that case, the integration context should be overwritten and the user should execute the auth process from the begining.
            if ($response_body.error_description -like "*AADSTS50173*") {
                $this.ClearContext()
                $error_details = "The account password has been changed or reset. Please run !$script:COMMAND_PREFIX-auth-start to re-authenticate"
            }
            elseif ($response_body.error -eq "invalid_grant") {
                $error_details = "Please login to grant account permissions (After 90 days grant is expired) !$script:COMMAND_PREFIX-auth-start"
            }
            throw "$error_details. Full error message: $response_body"
        }

        # Update object properties
        $this.access_token = $response_body.access_token
        $this.refresh_token = $response_body.refresh_token
        $this.access_token_expires_in = [int]::Parse($response_body.expires_in)
        $this.access_token_creation_time = [int][double]::Parse((Get-Date -UFormat %s))

        return $response_body

        <#
            .DESCRIPTION
            Getting new access-token and refresh-token from Microsoft application based on the refresh-token we got from AccessTokenRequest() method.

            .EXAMPLE
            $client.RefreshTokenRequest()

            .OUTPUTS
            PSObject - Raw body response.

            .LINK
            https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-implicit-grant-flow#refreshing-tokens
        #>
    }

    [bool]IsDeviceCodeExpired(){
        if (!$this.device_code){
            return $true
        }
        $current_time = [int][double]::Parse((Get-Date -UFormat %s)) - 30
        $valid_until = $this.device_code_creation_time + $this.access_token_expires_in

        return $current_time -gt $valid_until

        <#
            .DESCRIPTION
            Check if device-code expired.

            .EXAMPLE
            $client.IsDeviceCodeExpired()

            .OUTPUTS
            bool - True If device-code expired else False.

            .LINK
            https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-configurable-token-lifetimes#configurable-token-lifetime-properties-after-the-retirement
        #>
    }

    [bool]IsAccessTokenExpired(){
        if (!$this.access_token){
            return $true
        }
        $current_time = [int][double]::Parse((Get-Date -UFormat %s)) - 30
        $valid_until = $this.access_token_creation_time + $this.access_token_expires_in

        return $current_time -gt $valid_until
        <#
            .DESCRIPTION
            Check if access-token expired.

            .EXAMPLE
            $client.IsAccessTokenExpired()

            .OUTPUTS
            bool - True If access-token expired else False.

            .LINK
            https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-configurable-token-lifetimes#configurable-token-lifetime-properties-after-the-retirement
        #>
    }

    RefreshTokenIfExpired(){
        if ($this.access_token -and $this.IsAccessTokenExpired()) {
            $this.RefreshTokenRequest()
        }
        <#
            .DESCRIPTION
            Refresh access token if expired, with offset of 30 seconds.

            .EXAMPLE
            $client.RefreshTokenIfExpired()
        #>
    }
    ClearContext(){
        $this.access_token = $null
        $this.refresh_token = $null
        $this.access_token_expires_in = $null
        $this.access_token_creation_time = $null
        UpdateIntegrationContext $this
        <#
            .DESCRIPTION
            Clear the token fields from the integration context on password change case.

            .EXAMPLE
            $client.ClearContext()
        #>

    }
}

#### Security And Compliance client - OAUTH2.0 ####

class SecurityAndComplianceClient {
    [string]$url
    [string]$upn
    [string]$password
    [string]$bearer_token
    [psobject]$session
    [bool]$insecure
    [bool]$proxy

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '', Scope='Function')]
    SecurityAndComplianceClient([string]$url, [string]$upn, [string]$password, [string]$bearer_token, [bool]$insecure, [bool]$proxy) {
        $this.upn = $upn
        $this.password = $password
        $this.bearer_token = $bearer_token
        $this.insecure = $insecure
        $this.proxy = $proxy
        $this.url = $url
        <#
            .DESCRIPTION
            SecurityAndComplianceClient connect to Security & Compliance Center using powershell session (OAuth2.0) and allow interact with it.

            .PARAMETER uri
            Security & Compliance Center uri.

            .PARAMETER upn
            User Principal Name (UPN) is the name of a system user in an email address format.

            .PARAMETER password
            Password is filled only if authentication method is basic auth.

            .PARAMETER bearer_token
            Valid bearer token value.

            .PARAMETER insecure
            Wheter to trust any TLS/SSL Certificate) or not.

            .EXAMPLE proxy
            Wheter to user system proxy configuration or not.

            .EXAMPLE
            $cs_client = [SecurityAndComplianceClient]::new("outlook.com", "user@microsoft.com", "dfhsdkjhkjhvkdvbihsgiu")

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/exchange/?view=exchange-ps#policy-and-compliance-content-search
        #>
    }

    CreateSession() {
        $this.session = CreateNewSession -url $this.url -upn $this.upn -password $this.password -bearer_token $this.bearer_token -insecure $this.insecure -proxy $this.proxy
        <#
            .DESCRIPTION
            This method is for internal use. It creates session to Security & Compliance Center.

            .EXAMPLE
            $client.CreateSession()

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/new-pssession?view=powershell-7
            https://docs.microsoft.com/en-us/powershell/partnercenter/multi-factor-auth?view=partnercenterps-3.0#exchange-online-powershell
        #>
    }

    CloseSession() {
        if ($this.session) {
            Remove-PSSession $this.session
        }
        <#
            .DESCRIPTION
            This method is for internal use. It creates session to Security & Compliance Center.

            .EXAMPLE
            $client.CloseSession()

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/remove-pssession?view=powershell-7
            https://docs.microsoft.com/en-us/powershell/partnercenter/multi-factor-auth?view=partnercenterps-3.0#exchange-online-powershell
        #>
    }

    [psobject]NewSearch([string]$search_name,  [string]$case, [string]$kql, [string]$description, [bool]$allow_not_found_exchange_locations, [string[]]$exchange_location,
                        [string[]]$exchange_location_exclusion, [string[]]$public_folder_location, [string[]]$share_point_location, [string[]]$share_point_location_exclusion) {
        try{
            # Establish session to remote
            $this.CreateSession()
            # Import and Execute command
            Import-PSSession -Session $this.session -CommandName New-ComplianceSearch -AllowClobber
            $cmd_params = @{
                "Name" = $search_name
                "Case" = $case
                "ContentMatchQuery" = $kql
                "Description" = $description
                "AllowNotFoundExchangeLocationsEnabled" = $allow_not_found_exchange_locations
                "ExchangeLocation" = $exchange_location
                "ExchangeLocationExclusion" = $exchange_location_exclusion
                "PublicFolderLocation" = $public_folder_location
                "SharePointLocation" = $share_point_location
                "SharePointLocationExclusion" = $share_point_location_exclusion
            }
            $response = New-ComplianceSearch @cmd_params

            return $response
        }
        finally {
            # Close session to remote
            $this.CloseSession()
        }
        <#
            .DESCRIPTION
            Create compliance search in the Security & Compliance Center.

            .PARAMETER search_name
            The name of the compliance search.

            .PARAMETER case
            Name of a Core eDiscovery case to associate the new compliance search with.

            .PARAMETER kql
            Text search string or a query that's formatted by using the Keyword Query Language (KQL).

            .PARAMETER description
            Optional description for the compliance search.

            .PARAMETER allow_not_found_exchange_locations
            Whether to include mailboxes other than regular user mailboxes in the compliance search.

            .PARAMETER exchange_location
            Mailboxes to include.

            .PARAMETER exchange_location_exclusion
            Mailboxes to exclude when you use the value "All" for the exchange_location parameter.

            .PARAMETER public_folder_location
            Whether to include all public folders in the search.

            .PARAMETER share_point_location
            SharePoint Online sites to include. You identify the site by its URL value, or you can use the value All to include all sites.

            .PARAMETER share_point_location_exclusion
            SharePoint Online sites to exclude when you use the value All for the SharePointLocation parameter. You identify the site by its URL value.

            .EXAMPLE
            $client.NewSearch("new-search")
            $client.NewSearch("new-search", "new-search-description")

            .OUTPUTS
            psobject - Raw response.

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/exchange/new-compliancesearch?view=exchange-ps
        #>
    }

    SetSearch([string]$search_name, [string]$kql, [string]$description, [bool]$allow_not_found_exchange_locations, [string[]]$add_exchange_location,
              [string[]]$add_exchange_location_exclusion, [string[]]$add_public_folder_location, [string[]]$add_share_point_location, [string[]]$add_share_point_location_exclusion,
              [string[]]$remove_exchange_location, [string[]]$remove_exchange_location_exclusion, [string[]]$remove_public_folder_location, [string[]]$remove_share_point_location,
              [string[]]$remove_share_point_location_exclusion) {
        try{
            # Establish session to remote
            $this.CreateSession()
            # Import and Execute command
            Import-PSSession -Session $this.session -CommandName Set-ComplianceSearch -AllowClobber
            $cmd_params = @{
                "Identity" = $search_name
                "ContentMatchQuery" = $kql
                "Description" = $description
                "AllowNotFoundExchangeLocationsEnabled" = $allow_not_found_exchange_locations
                "AddExchangeLocation" = $add_exchange_location
                "AddExchangeLocationExclusion" = $add_exchange_location_exclusion
                "PublicFolderLocation" = $add_public_folder_location
                "AddSharePointLocation" = $add_share_point_location
                "AddSharePointLocationExclusion" = $add_share_point_location_exclusion
                "RemoveExchangeLocation" = $remove_exchange_location
                "RemoveExchangeLocationExclusion" = $remove_exchange_location_exclusion
                "RemovePublicFolderLocation" = $remove_public_folder_location
                "RemoveSharePointLocation" = $remove_share_point_location
                "RemoveSharePointLocationExclusion" = $remove_share_point_location_exclusion
            }
            Set-ComplianceSearch @cmd_params
        }
        finally {
            # Close session to remote
            $this.CloseSession()
        }
        <#
            .DESCRIPTION
            Set compliance search in the Security & Compliance Center.

            .PARAMETER search_name
            The name of the compliance search.

            .PARAMETER kql
            Text search string or a query that's formatted by using the Keyword Query Language (KQL).

            .PARAMETER description
            Optional description for the compliance search.

            .PARAMETER allow_not_found_exchange_locations
            Whether to include mailboxes other than regular user mailboxes in the compliance search.

            .PARAMETER add_exchange_location
            Add mailboxes to include.

            .PARAMETER add_exchange_location_exclusion
            Add mailboxes to exclude when you use the value "All" for the exchange_location parameter.

            .PARAMETER add_public_folder_location
            Add public folders to include.

            .PARAMETER add_share_point_location
            Add sharePoint online sites to include. You identify the site by its URL value.

            .PARAMETER add_share_point_location_exclusion
            Add sharePoint online sites to exclude when you use the value "All" for the SharePointLocation parameter. You identify the site by its URL value.

            .PARAMETER remove_exchange_location
            Remove mailboxes to include.

            .PARAMETER remove_exchange_location_exclusion
            Remove mailboxes to exclude when you use the value "All" for the exchange_location parameter.

            .PARAMETER remove_public_folder_location
            Remove public folders to include.

            .PARAMETER remove_share_point_location
            Remove sharePoint online sites to include. You identify the site by its URL value.

            .PARAMETER remove_share_point_location_exclusion
            Remove sharePoint online sites to exclude when you use the value "All" for the exchange_location (Used in create new compliance search) argument or share_point_location argument. You identify the site by its URL value.

            .EXAMPLE
            $client.SetSearch("new-search", "new-search-description")

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/exchange/set-compliancesearch?view=exchange-ps
        #>
    }

    RemoveSearch([string]$search_name) {
        try{
            # Establish session to remote
            $this.CreateSession()
            # Import and Execute command
            Import-PSSession -Session $this.session -CommandName Remove-ComplianceSearch -AllowClobber
            Remove-ComplianceSearch -Identity $search_name -Confirm:$false
        }
        finally {
            # Close session to remote
            $this.CloseSession()
        }
       <#
            .DESCRIPTION
            Remove compliance search by name from the Security & Compliance Center.

            .PARAMETER search_name
            The name of the compliance search.

            .EXAMPLE
            $client.RemoveSearch("new-search")

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/exchange/remove-compliancesearch?view=exchange-ps
        #>
    }

    [array]ListSearch() {
        try {
            # Establish session to remote
            $this.CreateSession()
            # Import and Execute command
            Import-PSSession -Session $this.session -CommandName Get-ComplianceSearch -AllowClobber
            $response = Get-ComplianceSearch

            return $response
        }
        finally {
            # Close session to remote
            $this.CloseSession()
        }
       <#
            .DESCRIPTION
            List compliance searches in the Security & Compliance Center.

            .EXAMPLE
            $client.ListSearch()

            .OUTPUTS
            array - Raw response.

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/exchange/get-compliancesearch?view=exchange-ps
        #>
    }

    [psobject]GetSearch([string]$search_name) {
        try{
            # Establish session to remote
            $this.CreateSession()
            # Import and Execute command
            Import-PSSession -Session $this.session -CommandName Get-ComplianceSearch -AllowClobber
            $response = Get-ComplianceSearch -Identity $search_name

            return $response
        }
        finally {
            # Close session to remote
            $this.CloseSession()
        }
        <#
            .DESCRIPTION
            Get compliance search by name from the Security & Compliance Center.

            .PARAMETER search_name
            The name of the compliance search.

            .EXAMPLE
            $client.GetSearch("new-search")

            .OUTPUTS
            psobject - Raw response.

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/exchange/get-compliancesearch?view=exchange-ps
        #>
    }

    StartSearch([string]$search_name) {
        try{
            # Establish session to remote
            $this.CreateSession()
            # Import and Execute command
            Import-PSSession -Session $this.session -CommandName Start-ComplianceSearch -AllowClobber
            Start-ComplianceSearch -Identity $search_name -Confirm:$false -Force:$true
        }
        finally {
            # Close session to remote
            $this.CloseSession()
        }
        <#
            .DESCRIPTION
            Start stopped, completed or not started compliance search in the Security & Compliance Center.

            .PARAMETER search_name
            The name of the compliance search.

            .EXAMPLE
            $client.StartSearch("new-search")

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/exchange/start-compliancesearch?view=exchange-ps
        #>
    }

    StopSearch([string]$search_name) {
        try{
            # Establish session to remote
            $this.CreateSession()
            # Import and Execute command
            Import-PSSession -Session $this.session -CommandName Stop-ComplianceSearch  -AllowClobber
            Stop-ComplianceSearch -Identity $search_name -Confirm:$false
        }
        finally {
            # Close session to remote
            $this.CloseSession()
        }
        <#
            .DESCRIPTION
            Stop compliance search by name in the Security & Compliance Center.

            .PARAMETER search_name
            The name of the compliance search.

            .EXAMPLE
            $client.StopSearch("new-search")

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/exchange/stop-compliancesearch?view=exchange-ps
        #>
    }

    [psobject]NewSearchAction([string]$search_name, [string]$action, [string]$purge_type) {
        try{
            # Establish session to remote
            $this.CreateSession()
            # Import and Execute command
            Import-PSSession -Session $this.session -CommandName New-ComplianceSearchAction -AllowClobber
            $cmd_params = @{
                "SearchName" = $search_name
            }
            if ($action -eq "Preview") {
                $cmd_params.Preview = $true
            } elseif ($action -eq "Purge") {
                $cmd_params.Purge = $true
                $cmd_params.PurgeType = $purge_type
                $cmd_params.Confirm = $false
                $cmd_params.Force = $true
            } else {
                throw "New action must include valid action - Preview/Purge"
            }
            $response = New-ComplianceSearchAction @cmd_params
            if (-not $response){
                throw "The search action didn't return any results. Please check the search_name and consider running the o365-sc-start-search command before."
            }

            return $response
        }
        finally {
            # Close session to remote
            $this.CloseSession()
        }
        <#
            .DESCRIPTION
            Create compliance search action in the Security & Compliance Center.

            .PARAMETER search_name
            The name of the compliance search.

            .PARAMETER action
            Search action type - Preview (Showing results) / Purge (Delete found emails)

            .PARAMETER purge_type
            Used if action type is purge, Search action purge type - SoftDelete (allow recover) / HardDelete (not recoverable).

            .EXAMPLE
            $client.NewSearchAction("search-name", "Preview")
            $client.NewSearchAction("search-name", "Purge", "HardDelete")

            .OUTPUTS
            psobject - Raw response.

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/exchange/new-compliancesearchaction?view=exchange-ps
        #>
    }

    RemoveSearchAction([string]$search_action_name) {
        try{
            # Establish session to remote
            $this.CreateSession()
            # Import and Execute command
            Import-PSSession -Session $this.session -CommandName Remove-ComplianceSearchAction -AllowClobber
            Remove-ComplianceSearchAction -Identity $search_action_name -Confirm:$false
        }
        finally {
            # Close session to remote
            $this.CloseSession()
        }
        <#
            .DESCRIPTION
            Remove compliance search action from the Security & Compliance Center.

            .PARAMETER search_action_name
            The name of the compliance search action.

            .EXAMPLE
            $client.RemoveSearchAction("search-name")

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/exchange/remove-compliancesearchaction?view=exchange-ps
        #>
    }

    [array]ListSearchActions() {
        try{
            # Establish session to remote
            $this.CreateSession()
            # Import and Execute command
            Import-PSSession -Session $this.session -CommandName Get-ComplianceSearchAction -AllowClobber
            $response = Get-ComplianceSearchAction

            return $response
        }
        finally {
            # Close session to remote
            $this.CloseSession()
        }
        <#
            .DESCRIPTION
            List all compliance search action in the Security & Compliance Center.

            .EXAMPLE
            $client.ListearchAction()

            .OUTPUTS
            array - Raw response.

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/exchange/get-compliancesearchaction?view=exchange-ps
        #>
    }

    [psobject]GetSearchAction([string]$search_action_name) {
        try{
            # Establish session to remote
            $this.CreateSession()
            # Import and Execute command
            Import-PSSession -Session $this.session -CommandName Get-ComplianceSearchAction -AllowClobber
            $response = Get-ComplianceSearchAction -Identity $search_action_name

            return $response
        }
        finally {
            # Close session to remote
            $this.CloseSession()
        }
        <#
            .DESCRIPTION
            Get compliance search action in the Security & Compliance Center.

            .PARAMETER search_action_name
            The name of the compliance search action.

            .EXAMPLE
            $client.GetSearchAction("search-name")

            .OUTPUTS
            psobject - Raw response.

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/exchange/get-compliancesearchaction?view=exchange-ps
        #>
    }
}

#### COMMAND FUNCTIONS ####

function TestModuleCommand ([OAuth2DeviceCodeClient]$oclient, [SecurityAndComplianceClient]$cs_client) {
    if ($cs_client.password) {
        $cs_client.ListSearchActions() | Out-Null
    }
    else {
        throw "Fill password for basic auth or use command !$script:COMMAND_PREFIX-auth-start for Oauth2.0 authorization (MFA enabled accounts)."
    }
    $raw_response = $null
    $human_readable = "ok"
    $entry_context = $null

    return $human_readable, $entry_context, $raw_response
}

function StartAuthCommand ([OAuth2DeviceCodeClient]$client) {
    $raw_response = $client.AuthorizationRequest()
    $human_readable = "## $script:INTEGRATION_NAME - Authorize instructions
1. To sign in, use a web browser to open the page [https://microsoft.com/devicelogin](https://microsoft.com/devicelogin) and enter the code **$($raw_response.user_code)** to authenticate.
2. Run the **!$script:COMMAND_PREFIX-auth-complete** command in the War Room.
3. Run the **!$script:COMMAND_PREFIX-auth-test** command in the War Room to test the completion of the authorization process and the configured parameters."
    $entry_context = @{}

    return $human_readable, $entry_context, $raw_response
}

function CompleteAuthCommand ([OAuth2DeviceCodeClient]$client) {
    # Verify that user run start before complete
    if (!$client.device_code) {
        throw "Please run !o365-sc-auth-start and follow the command instructions"
    }
    $raw_response = $client.AccessTokenRequest()
    $human_readable = "Your account **successfully** authorized!"
    $entry_context = @{}

    return $human_readable, $entry_context, $raw_response
}

function TestAuthCommand ([OAuth2DeviceCodeClient]$oclient, [SecurityAndComplianceClient]$cs_client) {
    $raw_response = $oclient.RefreshTokenRequest()
    $human_readable = "**Test ok!**"
    $entry_context = @{}
    try {
        $cs_client.CreateSession()
    }
    finally {
        $cs_client.CloseSession()
    }

    return $human_readable, $entry_context, $raw_response
}

function NewSearchCommand([SecurityAndComplianceClient]$client, [hashtable]$kwargs) {
    # Command arguemnts parsing
    $allow_not_found_exchange_locations = ConvertTo-Boolean $kwargs.allow_not_found_exchange_locations
    $exchange_location = ArgToList $kwargs.exchange_location
    $exchange_location_exclusion = ArgToList $kwargs.exchange_location_exclusion
    $public_folder_location = ArgToList $kwargs.public_folder_location
    $share_point_location = ArgToList $kwargs.share_point_location
    $share_point_location_exclusion = ArgToList $kwargs.share_point_location_exclusion
    if (!$kwargs.search_name -or $kwargs.search_name -eq "") {
        $kwargs.search_name = "XSOAR-$(New-Guid)"
    }
    # Raw response
    $raw_response = $client.NewSearch($kwargs.search_name, $kwargs.case, $kwargs.kql, $kwargs.description, $allow_not_found_exchange_locations,
                                      $exchange_location, $exchange_location_exclusion, $public_folder_location, $share_point_location, $share_point_location_exclusion)
    # Human readable
    $md_columns = $raw_response | Select-Object -Property Name, Description, CreatedBy, LastModifiedTime, ContentMatchQuery
    $human_readable = TableToMarkdown $md_columns  "$script:INTEGRATION_NAME - New search '$($kwargs.search_name)' created"
    # Entry context
    $entry_context = @{
        $script:SEARCH_ENTRY_CONTEXT = ParseSearchToEntryContext $raw_response
    }

    return $human_readable, $entry_context, $raw_response
}

function SetSearchCommand([SecurityAndComplianceClient]$client, [hashtable]$kwargs) {
    # Command arguemnts parsing
    if ($kwargs.allow_not_found_exchange_locations) {
        $allow_not_found_exchange_locations = ConvertTo-Boolean $kwargs.allow_not_found_exchange_locations
    }
    $add_exchange_location = ArgToList $kwargs.add_exchange_location
    $add_exchange_location_exclusion = ArgToList $kwargs.add_exchange_location_exclusion
    $add_public_folder_location = ArgToList $kwargs.add_public_folder_location
    $add_share_point_location = ArgToList $kwargs.add_share_point_location
    $add_share_point_location_exclusion = ArgToList $kwargs.add_share_point_location_exclusion
    $remove_exchange_location = ArgToList $kwargs.remove_exchange_location
    $remove_exchange_location_exclusion = ArgToList $kwargs.remove_exchange_location_exclusion
    $remove_public_folder_location = ArgToList $kwargs.remove_public_folder_location
    $remove_share_point_location = ArgToList $kwargs.remove_share_point_location
    $remove_share_point_location_exclusion = ArgToList $kwargs.remove_share_point_location_exclusion
    # Set operation doesn't return any output
    $client.SetSearch($kwargs.search_name, $kwargs.kql, $kwargs.description, $allow_not_found_exchange_locations,
                      $add_exchange_location, $add_exchange_location_exclusion, $add_public_folder_location, $add_share_point_location, $add_share_point_location_exclusion,
                      $remove_exchange_location, $remove_exchange_location_exclusion, $remove_public_folder_location, $remove_share_point_location, $remove_share_point_location_exclusion)
    # Raw response
    $raw_response = @{}
    # Human readable
    $human_readable = "$script:INTEGRATION_NAME - Search **$($kwargs.search_name)** modified!"
    # Entry context
    $entry_context = @{}

    return $human_readable, $entry_context, $raw_response
}

function RemoveSearchCommand([SecurityAndComplianceClient]$client, [hashtable]$kwargs) {
    # Remove operation doesn't return any output
    $client.RemoveSearch($kwargs.search_name)
    # Raw response
    $raw_response = @{}
    # Human readable
    $human_readable = "$script:INTEGRATION_NAME - Search **$($kwargs.search_name)** removed!"
    # Entry context
    $entry_context = @{}

    return $human_readable, $entry_context, $raw_response
}

function ListSearchCommand([SecurityAndComplianceClient]$client, [hashtable]$kwargs) {
    # Raw response
    $raw_response = $client.ListSearch()

    if ($raw_response.count -eq 0){
        return "#### No compliance searches were retrieved from the Compliance Center.", @{}, $raw_response
    }

    # Human readable
    $md_columns = $raw_response | Select-Object -Property Name, Description, CreatedBy, LastModifiedTime, RunBy
    $human_readable = TableToMarkdown $md_columns "$script:INTEGRATION_NAME - Search configurations"
    # Entry context
    $entry_context = @{
        $script:SEARCH_ENTRY_CONTEXT =  $raw_response | ForEach-Object {
            ParseSearchToEntryContext $_
        }
    }

    return $human_readable, $entry_context, $raw_response
}

function GetSearchCommand([SecurityAndComplianceClient]$client, [hashtable]$kwargs) {
    # Command arguemnts parsing
    $statistics = ConvertTo-Boolean $kwargs.statistics
    $all_results = ConvertTo-Boolean $kwargs.all_results
    $export = ConvertTo-Boolean $kwargs.export
    # Raw response
    $raw_response = $client.GetSearch($kwargs.search_name)
    # Entry context
    $entry_context = @{
        $script:SEARCH_ENTRY_CONTEXT = ParseSearchToEntryContext -search $raw_response -limit $kwargs.limit -all_results $all_results
    }
    # Human readable - Basic info
    $md_columns = $raw_response | Select-Object -Property Name, Description, CreatedBy, LastModifiedTime, RunBy, Status
    $human_readable = TableToMarkdown $md_columns  "$script:INTEGRATION_NAME - '$($kwargs.search_name)' search"
    # Human readable - Statistics
    $parsed_results = $entry_context[$script:SEARCH_ENTRY_CONTEXT].SuccessResults
    if ($parsed_results -and $statistics) {
        $human_readable += TableToMarkdown $parsed_results "Search statistics"
    }
    # Results file export
    if ($export) {
        $parsed_results_all = ParseSuccessResults -success_results $raw_response.SuccessResults -limit $kwargs.limit -all_results $all_results
        if ($parsed_results_all.Count -ne 0){
            $file_entry = FileResult "$($kwargs.search_name)_search.json" $($parsed_results_all | ConvertTo-Json) $true
        }
    }

    return $human_readable, $entry_context, $raw_response, $file_entry
}

function StartSearchCommand([SecurityAndComplianceClient]$client, [hashtable]$kwargs) {
    # Start operation doesn't return any output
    $client.StartSearch($kwargs.search_name)
    # Raw response
    $raw_response = @{}
    # Human readable
    $human_readable = "$script:INTEGRATION_NAME - search **$($kwargs.search_name)** started !"
    # Entry context
    $entry_context = @{}

    return $human_readable, $entry_context, $raw_response
}

function StopSearchCommand([SecurityAndComplianceClient]$client, [hashtable]$kwargs) {
    # Stop operation doesn't return any output
    $client.StopSearch($kwargs.search_name)
    # Raw response
    $raw_response = @{}
    # Human readable
    $human_readable = "$script:INTEGRATION_NAME - search **$($kwargs.search_name)** stopped !"
    # Entry context
    $entry_context = @{}

    return $human_readable, $entry_context, $raw_response
}

function NewSearchActionCommand([SecurityAndComplianceClient]$client, [hashtable]$kwargs) {
    # Raw response
    $raw_response = $client.NewSearchAction($kwargs.search_name, $kwargs.action, $kwargs.purge_type)
    # Human readable
    $md_columns = $raw_response | Select-Object -Property Name, SearchName, Action, LastModifiedTime, RunBy, Status
    $human_readable = TableToMarkdown $md_columns "$script:INTEGRATION_NAME - search action '$($raw_response.Name)' created"
    # Entry context
    $entry_context = @{
        $script:SEARCH_ACTION_ENTRY_CONTEXT = ParseSearchActionToEntryContext $raw_response
    }

    return $human_readable, $entry_context, $raw_response
}

function RemoveSearchActionCommand([SecurityAndComplianceClient]$client, [hashtable]$kwargs) {
    # Remove operation doesn't return any output
    $client.RemoveSearchAction($kwargs.search_action_name)
    # Raw response
    $raw_response = @{}
    # Human readable
    $human_readable = "$script:INTEGRATION_NAME - search action **$($kwargs.search_action_name)** removed!"
    # Entry context
    $entry_context = @{}

    return $human_readable, $entry_context, $raw_response
}

function GetSearchActionCommand([SecurityAndComplianceClient]$client, [hashtable]$kwargs) {
    # Command arguemnts parsing
    $results = ConvertTo-Boolean $kwargs.results
    $export = ConvertTo-Boolean $kwargs.export
    # Raw response
    $raw_response = $client.GetSearchAction($kwargs.search_action_name)
    # Entry context
    $entry_context = @{
        $script:SEARCH_ACTION_ENTRY_CONTEXT = ParseSearchActionToEntryContext $raw_response $kwargs.limit
    }
    # Human readable
    $md_columns = $raw_response | Select-Object -Property Name, SearchName, Action, LastModifiedTime, RunBy, JobEndTime, Status
    $human_readable = TableToMarkdown $md_columns "$script:INTEGRATION_NAME - search action '$($kwargs.search_action_name)'"
    # Human readable - Mail results
    $parsed_results = $entry_context[$script:SEARCH_ACTION_ENTRY_CONTEXT].Results
    if ($parsed_results -and $results) {
        $human_readable += TableToMarkdown $parsed_results "Search action results"
    }
    # Results file export
    if ($export) {
        $parsed_results_all = ParseResults -results $raw_response.Results -limit $kwargs.limit
        if ($parsed_results_all.Count -ne 0){
            $file_entry = FileResult "$($kwargs.search_action_name)_search_action.json" $($parsed_results_all | ConvertTo-Json) $true
        }
    }


    return $human_readable, $entry_context, $raw_response, $file_entry
}

function ListSearchActionsCommand([SecurityAndComplianceClient]$client, [hashtable]$kwargs) {
    # Raw response
    $raw_response = $client.ListSearchActions()

    # Human readable
    $md_columns = $raw_response | Select-Object -Property Name, SearchName, Action, LastModifiedTime, RunBy, JobEndTime, Status
    $human_readable = TableToMarkdown $md_columns "$script:INTEGRATION_NAME - search actions"
    # Entry context
    $entry_context = @{
        $script:SEARCH_ACTION_ENTRY_CONTEXT = $raw_response | ForEach-Object {
            ParseSearchActionToEntryContext $_
        }
    }

    return $human_readable, $entry_context, $raw_response
}

#### INTEGRATION COMMANDS MANAGER ####

function Main {
    $command = $Demisto.GetCommand()
    $command_arguments = $Demisto.Args()
    $integration_params = $Demisto.Params()
    <#
        Proxy currently isn't supported by PWSH New-Pssession, However partly implmentation of proxy feature still function (OAuth2.0 and redirect),
        leaving this parameter for feature development if required.
    #>
    $no_proxy = $false
    $insecure = (ConvertTo-Boolean $integration_params.insecure)

    try {
        $Demisto.Debug("Command being called is $Command")

        # Creating Compliance and search client
        $oauth2_client = [OAuth2DeviceCodeClient]::CreateClientFromIntegrationContext($insecure, $no_proxy)

        # Executing oauth2 commands
        switch ($command) {
            "$script:COMMAND_PREFIX-auth-start" {
                ($human_readable, $entry_context, $raw_response) = StartAuthCommand $oauth2_client
            }
            "$script:COMMAND_PREFIX-auth-complete" {
                ($human_readable, $entry_context, $raw_response) = CompleteAuthCommand $oauth2_client
            }
        }

        # Refreshing tokens if expired
        if ($command -ne "$script:COMMAND_PREFIX-auth-start")
        {
            $oauth2_client.RefreshTokenIfExpired()
        }

        # Creating Compliance and search client
        $cs_client = [SecurityAndComplianceClient]::new($integration_params.url, $integration_params.credentials.identifier,
                                                        $integration_params.credentials.password, $oauth2_client.access_token, $insecure, $no_proxy)
        # Executing command
        switch ($command) {
            "test-module" {
                ($human_readable, $entry_context, $raw_response) = TestModuleCommand $oauth2_client $cs_client
            }
            "$script:COMMAND_PREFIX-auth-test" {
                ($human_readable, $entry_context, $raw_response) = TestAuthCommand $oauth2_client $cs_client
            }
            "$script:COMMAND_PREFIX-new-search" {
                ($human_readable, $entry_context, $raw_response) = NewSearchCommand $cs_client $command_arguments
            }
            "$script:COMMAND_PREFIX-set-search" {
                ($human_readable, $entry_context, $raw_response) = SetSearchCommand $cs_client $command_arguments
            }
            "$script:COMMAND_PREFIX-remove-search" {
                ($human_readable, $entry_context, $raw_response) = RemoveSearchCommand $cs_client $command_arguments
            }
            "$script:COMMAND_PREFIX-list-search" {
                ($human_readable, $entry_context, $raw_response) = ListSearchCommand $cs_client $command_arguments
            }
            "$script:COMMAND_PREFIX-get-search" {
                ($human_readable, $entry_context, $raw_response, $file_entry) = GetSearchCommand $cs_client $command_arguments
            }
            "$script:COMMAND_PREFIX-start-search" {
                ($human_readable, $entry_context, $raw_response) = StartSearchCommand $cs_client $command_arguments
            }
            "$script:COMMAND_PREFIX-stop-search" {
                ($human_readable, $entry_context, $raw_response) = StopSearchCommand $cs_client $command_arguments
            }
            "$script:COMMAND_PREFIX-new-search-action" {
                ($human_readable, $entry_context, $raw_response) = NewSearchActionCommand $cs_client $command_arguments
            }
            "$script:COMMAND_PREFIX-remove-search-action" {
                ($human_readable, $entry_context, $raw_response) = RemoveSearchActionCommand $cs_client $command_arguments
            }
            "$script:COMMAND_PREFIX-list-search-action" {
                ($human_readable, $entry_context, $raw_response) = ListSearchActionsCommand $cs_client $command_arguments
            }
            "$script:COMMAND_PREFIX-get-search-action" {
                ($human_readable, $entry_context, $raw_response, $file_entry) = GetSearchActionCommand $cs_client $command_arguments
            }
        }
        # Updating integration context if access token changed
        UpdateIntegrationContext $oauth2_client
        # Return results to Demisto Server
        ReturnOutputs $human_readable $entry_context $raw_response | Out-Null
        if ($file_entry) {
            $Demisto.results($file_entry)
        }
    }
    catch {
        $Demisto.debug("Integration: $script:INTEGRATION_NAME
Command: $command
Arguments: $($command_arguments | ConvertTo-Json)
Error: $($_.Exception.Message)")
        if ($command -ne "test-module") {
            ReturnError "Error:
            Integration: $script:INTEGRATION_NAME
            Command: $command
            Arguments: $($command_arguments | ConvertTo-Json)
            Error: $($_.Exception)" | Out-Null
        }
        else {
            ReturnError $_.Exception.Message
        }
    }
}

# Execute Main when not in Tests
if ($MyInvocation.ScriptName -notlike "*.tests.ps1" -AND -NOT $Test) {
    Main
}