Exchange 2016 Compliance Search Deprecated

Deprecated. Use EWS V2 instead.

Messaging and Conferencing · Exchange 2016 Compliance Search

Details

IDExchange 2016 Compliance Search
ProviderMicrosoft
CategoryMessaging and Conferencing
From Version5.0.0
Docker Imagedemisto/python:2.7.18.27799
Supported ModulesAgentix

README

Deprecated, use EWS V2 instead.

Use this integration to run and manage compliance searches on your Exchange 2016 Server.

This integration was integrated and tested with Exchange 2016 Server.

Use Cases

Exchange Server 2016 Compliance Search enables you to search for and delete an email message from all mailboxes in your organization. There are no limits to the number of target mailboxes in a single search.

Prerequisite

Install Cortex XSOAR Engine

Permissions
The user that you configure for the integration instance requires the Compliance Management role to run the integration commands. For more information, see the Microsoft Documentation.

Configure Exchange 2016 Compliance Search on Cortex XSOAR

  1. Navigate to Settings>Integrations>Servers & Services.
  2. Search for Exchange 2016 Compliance Search.
  3. Click Add instance to create and configure a new integration instance.
    • Name: a textual name for the integration instance.
    • DOMAIN\USERNAME (e.g., XSOAR.INT\admin)
    • Exchange Server fully qualified domain name (FQDN)
    • Use single engine: engine to run the integration on
    • Note: the integration requires engine and there is no option to use it without engine.
    • Trust any certificate (not secure)
  4. 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.

  1. Start a compliance search: exchange2016-start-compliance-search
  2. Get the results and status of a compliance search: exchange2016-get-compliance-search
  3. Remove a compliance search: exchange2016-remove-compliance-search
  4. Purge the results of a compliance search: exchange2016-purge-compliance-search-results
  5. Get the status of a purge operation: exchange2016-get-compliance-search-purge-status

1. Start a compliance search


Initiates a compliance search.

Base Command

exchange2016-start-compliance-search

Input
Argument Name Description Required
query Query for finding mail messages Required

 

Context Output
Path Type Description
EWS.ComplianceSearch.Name string The name of the compliance search
EWS.ComplianceSearch.Status string The status of the compliance search

 

Command Example
exchange2016-start-compliance-search query="subject:\"Email to be searched and deleted\""
  
Context Example
{
    "EWS": {
        "ComplianceSearch": {
            "Status": "Starting",
            "Name": "DemistoSearch939b54bcd2bb4848bb0885dc4071d366"
        }
    }
}
Human Readable Output

image

2. Get the results and status of a compliance search


Gets the status and results of a compliance search.

Base Command

exchange2016-get-compliance-search

Input
Argument Name Description Required
search-name Name of the compliance search Required

 

Context Output
Path Type Description
EWS.ComplianceSearch.Status string The status of the compliance search

 

Command Example
exchange2016-get-compliance-search search-name=DemistoSearch939b54bcd2bb4848bb0885dc4071d366
  
Context Example
{
    "EWS": {
        "ComplianceSearch": {
            "Status": "InProgress",
            "Name": "DemistoSearch939b54bcd2bb4848bb0885dc4071d366"
        }
    }
}
Human Readable Output

image

3. Remove a compliance search


Removes the compliance search from the Exchange Server.

Base Command

exchange2016-remove-compliance-search

Input
Argument Name Description Required
search-name Name of the compliance search Required

 

Context Output
Path Type Description
EWS.ComplianceSearch.Status string The status of the compliance search

 

Command Example
exchange2016-remove-compliance-search search-name="DemistoSearch939b54bcd2bb4848bb0885dc4071d366"
  
Context Example
{
    "EWS": {
        "ComplianceSearch": {
            "Status": "Removed",
            "Name": "DemistoSearch939b54bcd2bb4848bb0885dc4071d366"
        }
    }
}
Human Readable Output

image

4. Purge the results of a compliance search


Purges the results found during the compliance search.

Base Command

exchange2016-purge-compliance-search-results

Input
Argument Name Description Required
search-name Name of the compliance search Required

 

Context Output

There is no context output for this command.

Command Example
exchange2016-purge-compliance-search-results search-name="DemistoSearch939b54bcd2bb4848bb0885dc4071d366"
  
Context Example
{
    "EWS": {
        "ComplianceSearch": {
            "Status": "Purging",
            "Name": "DemistoSearch939b54bcd2bb4848bb0885dc4071d366"
        }
    }
}
Human Readable Output

image

5. Get the status of a purge operation


Checks the status of the purge operation on the compliance search.

Base Command

exchange2016-get-compliance-search-purge-status

Input
Argument Name Description Required
search-name Name of the compliance search Required

 

Context Output

There is no context output for this command.

Command Example
exchange2016-get-compliance-search-purge-status search-name="DemistoSearch939b54bcd2bb4848bb0885dc4071d366"
  
Context Example
{
    "EWS": {
        "ComplianceSearch": {
            "Status": "Purging",
            "Name": "DemistoSearch939b54bcd2bb4848bb0885dc4071d366"
        }
    }
}
Human Readable Output

image

Configuration parameters

  • credentials — DOMAIN\USERNAME (e.g., DEMISTO.INT\admin) (required)
  • exchangeFQDN — Exchange Server fully qualified domain name (FQDN) (required)
  • insecure — Trust any certificate (not secure)

Commands (5)

  • exchange2016-get-compliance-search

    Gets the status and results of a compliance search.

  • exchange2016-get-compliance-search-purge-status

    Checks the status of the purge operation on the compliance search.

  • exchange2016-purge-compliance-search-results

    Purges the results found during the compliance search.

  • exchange2016-remove-compliance-search

    Removes the compliance search from the Exchange Server.

  • exchange2016-start-compliance-search

    Initiates a compliance search.

import demistomock as demisto
from CommonServerPython import *
import subprocess
import uuid

USERNAME = demisto.params()['credentials']['identifier'].replace("'", "''")
PASSWORD = demisto.params()['credentials']['password'].replace("'", "''")
EXCHANGE_FQDN = demisto.params()['exchangeFQDN'].replace("'", "''")
UNSECURE = demisto.params()['insecure']

STARTCS = '''
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]$username,
[Parameter(Mandatory=$True)]
[string]$query,
[Parameter(Mandatory=$True)]
[string]$server,
[Parameter(Mandatory=$True)]
[bool]$unsecure
)
$WarningPreference = "silentlyContinue"
$password = Read-Host
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$UserCredential = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
$searchName = [guid]::NewGuid().ToString() -replace '[-]'
$searchName = "DemistoSearch" + $searchName
if($unsecure){
    $url = "http://" + $server + "/PowerShell"
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $url `
    -Credential $UserCredential -Authentication Kerberos
}else{
    $url = "https://" + $server + "/PowerShell"
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $url `
    -Credential $UserCredential -Authentication Basic -AllowRedirection
}
if (!$session)
{
    "Failed to create remote PS session"
    return
}
Import-PSSession $session -CommandName *Compliance* -AllowClobber -DisableNameChecking -Verbose:$false | Out-Null
$compliance = New-ComplianceSearch -Name $searchName -ExchangeLocation All -ContentMatchQuery $query -Confirm:$false
Start-ComplianceSearch -Identity $searchName
$complianceSearchName = "Action status: " + $searchName
$complianceSearchName | ConvertTo-Json
Remove-PSSession $session
'''

GETCS = '''
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]$username,
[Parameter(Mandatory=$True)]
[string]$searchName,
[Parameter(Mandatory=$True)]
[string]$server,
[Parameter(Mandatory=$True)]
[bool]$unsecure
)
$WarningPreference = "silentlyContinue"
$password = Read-Host
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$UserCredential = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
if($unsecure){
    $url = "http://" + $server + "/PowerShell"
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $url `
    -Credential $UserCredential -Authentication Kerberos
}else{
    $url = "https://" + $server + "/PowerShell"
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $url `
    -Credential $UserCredential -Authentication Basic -AllowRedirection
}
if (!$session)
{
    "Failed to create remote PS session"
    return
}
Import-PSSession $session -CommandName Get-ComplianceSearch -AllowClobber `
-DisableNameChecking -Verbose:$false | Out-Null
$searchStatus = Get-ComplianceSearch $searchName
$searchStatus.Status
if ($searchStatus.Status -eq "Completed")
{
    $searchStatus.SuccessResults | ConvertTo-Json
}
Remove-PSSession $session
'''

REMOVECS = '''
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]$username,
[Parameter(Mandatory=$True)]
[string]$searchName,
[Parameter(Mandatory=$True)]
[string]$server,
[Parameter(Mandatory=$True)]
[bool]$unsecure
)
$WarningPreference = "silentlyContinue"
$password = Read-Host
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$UserCredential = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
if($unsecure){
    $url = "http://" + $server + "/PowerShell"
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $url `
    -Credential $UserCredential -Authentication Kerberos
}else{
    $url = "https://" + $server + "/PowerShell"
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $url `
    -Credential $UserCredential -Authentication Basic -AllowRedirection
}
if (!$session)
{
    "Failed to create remote PS session"
    return
}
Import-PSSession $session -CommandName *Compliance* -AllowClobber -DisableNameChecking -Verbose:$false | Out-Null
Remove-ComplianceSearch $searchName -Confirm:$false
Remove-PSSession $session
'''

STARTPURGE = '''
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]$username,
[Parameter(Mandatory=$True)]
[string]$searchName,
[Parameter(Mandatory=$True)]
[string]$server,
[Parameter(Mandatory=$True)]
[bool]$unsecure
)
$WarningPreference = "silentlyContinue"
$password = Read-Host
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$UserCredential = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
if($unsecure){
    $url = "http://" + $server + "/PowerShell"
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $url `
    -Credential $UserCredential -Authentication Kerberos
}else{
    $url = "https://" + $server + "/PowerShell"
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $url `
    -Credential $UserCredential -Authentication Basic -AllowRedirection
}
if (!$session)
{
    "Failed to create remote PS session"
    return
}
Import-PSSession $session -CommandName *Compliance* -AllowClobber -DisableNameChecking -Verbose:$false | Out-Null
$newActionResult = New-ComplianceSearchAction -SearchName $searchName -Purge -PurgeType SoftDelete -Confirm:$false
if (!$newActionResult)
{
    "No action was created"
}
Remove-PSSession $session
return
'''

CHECKPURGE = '''
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]$username,
[Parameter(Mandatory=$True)]
[string]$searchName,
[Parameter(Mandatory=$True)]
[string]$server,
[Parameter(Mandatory=$True)]
[bool]$unsecure
)
$WarningPreference = "silentlyContinue"
$password = Read-Host
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$UserCredential = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
if($unsecure){
    $url = "http://" + $server + "/PowerShell"
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $url `
    -Credential $UserCredential -Authentication Kerberos
}else{
    $url = "https://" + $server + "/PowerShell"
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $url `
    -Credential $UserCredential -Authentication Basic -AllowRedirection
}
if (!$session)
{
    "Failed to create remote PS session"
    return
}
Import-PSSession $session -CommandName *Compliance* -AllowClobber -DisableNameChecking -Verbose:$false | Out-Null
$actionName = $searchName + "_Purge"
$actionStatus = Get-ComplianceSearchAction $actionName
""
$actionStatus.Status
Remove-PSSession $session
'''

TESTCON = '''
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]$username,
[Parameter(Mandatory=$True)]
[string]$server,
[Parameter(Mandatory=$True)]
[bool]$unsecure
)
$errorActionPreference = 'Stop'
$WarningPreference = "silentlyContinue"
$password = Read-Host
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$UserCredential = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
try{
    if($unsecure){
        $url = "http://" + $server + "/PowerShell"
        $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $url `
        -Credential $UserCredential -Authentication Kerberos
    }else{
        $url = "https://" + $server + "/PowerShell"
        $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $url `
        -Credential $UserCredential -Authentication Basic -AllowRedirection
    }
    echo "successful connection"
} catch {
    $e = $_.Exception
    echo $e.Message
} finally {
    Remove-PSSession $session
}
'''


def prepare_args(d):
    return dict((k.replace("-", "_"), v) for k, v in d.items())


def str_to_unicode(obj):
    if isinstance(obj, dict):
        obj = {k: str_to_unicode(v) for k, v in obj.iteritems()}
    elif isinstance(obj, list):
        obj = map(str_to_unicode, obj)
    elif isinstance(obj, str):
        obj = unicode(obj, "utf-8")
    return obj


def encode_and_submit_results(obj):
    demisto.results(str_to_unicode(obj))


def get_cs_status(search_name, status):
    return {
        'Type': entryTypes['note'],
        'ContentsFormat': formats['text'],
        'Contents': 'Search {} status: {}'.format(search_name, status),
        'EntryContext': {
            'EWS.ComplianceSearch(val.Name === obj.Name)': {'Name': search_name, 'Status': status}
        }
    }


def create_ps_file(ps_name, ps_content):
    temp_path = os.getenv('TEMP')
    if not temp_path:
        return_error("Check that the integration is using single engine without docker."
                     " If so, add TEMP variable to the enviroment varibes.")

    ps_path = temp_path + '\\' + ps_name  # type: ignore
    with open(ps_path, 'w+') as file:
        file.write(ps_content)
    return ps_path


def delete_ps_file(ps_path):
    if os.path.exists(ps_path):
        os.remove(ps_path)


def start_compliance_search(query):
    try:
        ps_path = create_ps_file('startcs_' + str(uuid.uuid4()).replace('-', '') + '.ps1', STARTCS)
        output = subprocess.Popen(["powershell.exe", ps_path, "'" + USERNAME + "'",
                                   "'" + str(query).replace("'", "''") + "'", "'" + EXCHANGE_FQDN + "'", "$" + str(UNSECURE)],
                                  stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = output.communicate(input=PASSWORD.encode())
    finally:
        delete_ps_file(ps_path)

    if stderr:
        return_error(stderr)
    prefix = '"Action status: '
    pref_ind = stdout.find(prefix)
    sub_start = pref_ind + len(prefix)
    sub_end = sub_start + 45
    search_name = stdout[sub_start:sub_end]
    return {
        'Type': entryTypes['note'],
        'ContentsFormat': formats['text'],
        'Contents': 'Search started: {}'.format(search_name),
        'EntryContext': {
            'EWS.ComplianceSearch': {'Name': search_name, 'Status': 'Starting'}
        }
    }


def get_compliance_search(search_name):
    try:
        ps_path = create_ps_file('getcs_' + search_name + '.ps1', GETCS)
        output = subprocess.Popen(["powershell.exe", ps_path, "'" + USERNAME + "'",
                                   "'" + search_name + "'", "'" + EXCHANGE_FQDN + "'", "$" + str(UNSECURE)],
                                  stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = output.communicate(input=PASSWORD.encode())
    finally:
        delete_ps_file(ps_path)

    stdout = stdout[len(PASSWORD):]

    if stderr:
        return_error(stderr)
    stdsplit = stdout.split('\n', 1)
    status = stdsplit[0].strip()
    results = [get_cs_status(search_name, status)]

    if status == 'Completed' and len(stdsplit[1].strip()) > 4:
        res = list(r[:-1].split(', ') if r[-1] == ',' else r.split(', ') for r in stdsplit[1][2:-4].split(r'\r\n'))
        res = map(lambda x: {k: v for k, v in (s.split(': ') for s in x)}, res)
        results.append(
            {
                'Type': entryTypes['note'],
                'ContentsFormat': formats['text'],
                'Contents': stdout,
                'ReadableContentsFormat': formats['markdown'],
                'HumanReadable': tableToMarkdown('Exchange 2016 Compliance search results',
                                                 res, ['Location', 'Item count', 'Total size'])
            }
        )
    return results


def remove_compliance_search(search_name):
    try:
        ps_path = create_ps_file('removecs_' + search_name + '.ps1', REMOVECS)
        output = subprocess.Popen(["powershell.exe", ps_path, "'" + USERNAME + "'",
                                   "'" + search_name + "'", "'" + EXCHANGE_FQDN + "'", "$" + str(UNSECURE)],
                                  stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = output.communicate(input=PASSWORD.encode())
    finally:
        delete_ps_file(ps_path)

    return return_error(stderr) if stderr else get_cs_status(search_name, 'Removed')


def purge_compliance_search(search_name):
    try:
        ps_path = create_ps_file('startpurge_' + search_name + '.ps1', STARTPURGE)
        output = subprocess.Popen(["powershell.exe", ps_path, "'" + USERNAME + "'",
                                   "'" + search_name + "'", "'" + EXCHANGE_FQDN + "'", "$" + str(UNSECURE)],
                                  stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = output.communicate(input=PASSWORD.encode())
    finally:
        delete_ps_file(ps_path)
    return return_error(stderr) if stderr else get_cs_status(search_name, 'Purging')


def check_purge_compliance_search(search_name):
    try:
        ps_path = create_ps_file('checkpurge_' + search_name + '.ps1', CHECKPURGE)
        output = subprocess.Popen(["powershell.exe", ps_path, "'" + USERNAME + "'",
                                   "'" + search_name + "'", "'" + EXCHANGE_FQDN + "'", "$" + str(UNSECURE)],
                                  stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = output.communicate(input=PASSWORD.encode())
    finally:
        delete_ps_file(ps_path)

    return return_error(stderr) if stderr else get_cs_status(search_name,
                                                             'Purged' if stdout.strip() == 'Completed' else 'Purging')


def test_module():
    try:
        ps_path = create_ps_file('testcon_' + str(uuid.uuid4()).replace('-', '') + '.ps1', TESTCON)
        output = subprocess.Popen(["powershell.exe", ps_path, "'" + USERNAME + "'",
                                   "'" + EXCHANGE_FQDN + "'", "$" + str(UNSECURE)],
                                  stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout = output.communicate(input=PASSWORD.encode())[0].strip()
    finally:
        delete_ps_file(ps_path)

    stdout = stdout[len(PASSWORD):]

    if stdout == "successful connection":
        demisto.results('ok')
    else:
        return_error(stdout)


args = prepare_args(demisto.args())
try:
    if demisto.command() == 'exchange2016-start-compliance-search':
        encode_and_submit_results(start_compliance_search(**args))
    elif demisto.command() == 'exchange2016-get-compliance-search':
        encode_and_submit_results(get_compliance_search(**args))
    elif demisto.command() == 'exchange2016-remove-compliance-search':
        encode_and_submit_results(remove_compliance_search(**args))
    elif demisto.command() == 'exchange2016-purge-compliance-search-results':
        encode_and_submit_results(purge_compliance_search(**args))
    elif demisto.command() == 'exchange2016-get-compliance-search-purge-status':
        encode_and_submit_results(check_purge_compliance_search(**args))
    elif demisto.command() == 'test-module':
        test_module()
except Exception as e:
    if isinstance(e, WindowsError):  # pylint: disable=undefined-variable
        return_error("Could not open powershell on the target engine.")
    else:
        return_error(e)