Private/Compliance/MessageSearch.ps1

function MessageSearch {
    <#
        .SYNOPSIS
        Search for content with Micrsoft's compliance search and optionally delete said content

        .DESCRIPTION
        Search for content with Micrsoft's compliance search and optionally delete said content

        Currently supports the Exchange workflow. Workflows like SharePoint, OneDrive

        .PARAMETER _ExchangeServer
        Add the fqdn to the on-premises Exchange Server

        .PARAMETER _ExchangeServerBasicAuth
        Check this checkbox if connecting from a domain that is different from where the Exchange Server lives.

        .PARAMETER _ExchangeOnline
        Check this checkbox to connect to the Office 365 Security and Compliance Center (SCC)

        .PARAMETER RequiredSearchName
        A unique name for your organization.

        Feel free to use spaces, and dates for uniqueness.

        Commas cannot be used in the name

        .PARAMETER __HardDelete
        Parameter description

        .PARAMETER _From
        Accepts a single email address

        .PARAMETER _To

        Accepts one more more email addresses separated by commas

        Examples:

        1. jane@contoso.com

        - The search will find email with just Jane
        - The search will find email with Jane and Joe
        - The search will find email with Jane, Joe, and Pat

        2. jane@contoso.com, joe@contoso.com

        - The search will not find email with just Jane
        - The search will find email with Jane and Joe
        - The search will find email with Jane, Joe, and Pat

        .PARAMETER _SubjectContains
        The search will only find email with this subject

        Use the checkbox "SubjectContainsIsCommaSeparated" to specify a
            list of words (comma separated), all of which must be found in the emails subject

        Always use quotes around your search with commas

        Example:

        1. Apple
        2. "Apple, Pear, Kiwi"

        If not using SubjectContainsIsCommaSeparated checkbox, the search
            will look for the entire string

        If using SubjectContainsIsCommaSeparated checkgbox, the search
            will look for each word in the comma separated list of words. Not necessarily together as a phrase.

        .PARAMETER _SubjectContainsIsCommaSeparated
        Check this checkbox to look for more than one word in the subject

        Example:

        1. You check this checkbox.
        2. In the "Subject" field you type:

            Apple, Pear, Orange

        - The search will find this subject:

            The Apple, Pear and Orange

        - The search will not find:

            The Apple, Pear and Banana

        .PARAMETER _SubjectDoesNotContain
        This removes from search any emails with

        .PARAMETER _DateStart

        A date (and optionally a time) in the past from when you wish to start the search

        Use the format: YYYY-MM-DDThh:mm:ss

        A few examples:

        2020-06-25
        2020-06-25T14:00
        2020-06-25T14:00:12

        .PARAMETER _DateEnd
        A date (and optionally a time) in the past from when you wish to end the search

        NOTE: Must be more recent that _DateStart

        Use the format: YYYY-MM-DDThh:mm:ss

        A few examples:

        2020-06-25
        2020-06-25T15:00
        2020-06-25T15:00:12

        .PARAMETER AttachmentName
        Find any emails with the AttachmentName specified

        Only one attachment name can be specified per search

        Examples:

        1. Attachment Test.txt
        2. AttachmentTest.txt

        .PARAMETER MailboxesToSearch
        The MailboxesToSearch parameter specifies the mailboxes to include. Valid values are:

        A regular user mailbox. Including other types of mailboxes (for example, inactive mailboxes or Microsoft 365 guest users) is controlled by the AllowNotFoundExchangeLocationsEnabled parameter.

        A distribution group or mail-enabled security group (all mailboxes that are currently members of the group).

        To specify a mailbox or distribution group, use the email address. You can specify multiple values separated by commas.

        The default value is All, for all mailboxes.

        .PARAMETER ExceptionList
        A list of exceptions to ALL,. the default value of MailboxesToSearch (when MailboxesToSearch is left blank, ALL is used)

        This parameter specifies the mailboxes to exclude when you use the value All for the MailboxesToSearch parameter.

        Valid values are:

        A mailbox(es)

        A distribution group(s) or mail-enabled security group (all mailboxes that are currently members of the group).

        You can specify multiple values separated by commas.

        .PARAMETER ExceptionFilePath

        Specify a file path to a text file.

        1. The file should be a text file
        2. The file should contain a list of emailaddresses separated by commas

        .EXAMPLE

        Simply type the command

        New-MessageSearch

        .NOTES
        General notes
        #>


    [CmdletBinding()]
    param (
        [Parameter()]
        [string]
        $_ExchangeServer,

        [Parameter()]
        [switch]
        $_ExchangeServerBasicAuth,

        [Parameter()]
        [switch]
        $_ExchangeOnline,

        [Parameter(Mandatory)]
        [string]
        $RequiredSearchName,

        [Parameter()]
        [switch]
        $__HardDelete,

        [Parameter()]
        [mailaddress]
        $_From,

        [Parameter()]
        [mailaddress[]]
        $_To,

        [Parameter()]
        [mailaddress[]]
        $_CC,

        [Parameter()]
        [switch]
        $_SubjectContainsIsCommaSeparated,

        [Parameter()]
        [string]
        $_SubjectContains,

        [Parameter()]
        [string[]]
        $_SubjectDoesNotContain,

        [Parameter()]
        [datetime]
        $_DateStart,

        [Parameter()]
        [datetime]
        $_DateEnd = ([datetime]::Now),

        [Parameter()]
        [string]
        $AttachmentName,

        [Parameter()]
        [mailaddress[]]
        $MailboxesToSearch,

        [Parameter()]
        [mailaddress[]]
        $ExceptionList,

        [Parameter()]
        [string]
        [ValidateScript( { Test-Path $_ })]
        $ExceptionFilePath
    )
    $Script:HardOrSoft = $null
    if ($__HardDelete -and $_ExchangeOnline) { $Script:HardOrSoft = 'HardDelete' }
    else { $Script:HardOrSoft = 'SoftDelete' }

    $Splat = @{ }
    $Splat['Name'] = $RequiredSearchName

    $Query = [System.Collections.Generic.List[string]]::New()

    if ( $_From ) { $Query.Add('From:''{0}''' -f $_From) }
    if ( $_CC ) { (@($_CC) -ne '') | ForEach-Object { $Query.Add('CC:''{0}''' -f $_) } }
    if ( $_To ) { (@($_To) -ne '') | ForEach-Object { $Query.Add('To:''{0}''' -f $_) } }
    if ( $_SubjectContains ) {
        if ( $_SubjectContainsIsCommaSeparated ) { (@($_SubjectContains) -ne '').split(',') | foreach-object { $Query.Add('Subject:''{0}''' -f $_) } }
        else { $Query.Add(('Subject:''{0}''' -f $_SubjectContains)) }
    }
    if ( $_SubjectDoesNotContain ) { (@($_SubjectDoesNotContain) -ne '') | ForEach-Object { $Query.Add('-Subject:''{0}''' -f $_) } }
    if ( $_DateStart ) { $Query.Add(('Received:{0}..{1}' -f $_DateStart.ToUniversalTime().ToString("O") , $_DateEnd.ToUniversalTime().ToString("O"))) }
    if ( $AttachmentName ) { $Query.Add('Attachment:''{0}''' -f $AttachmentName) }

    if ( $Query ) {
        $KQL = '({0})' -f (@($Query) -join ') AND (')
        $Splat['ContentMatchQuery'] = $KQL
    }
    if (-not $MailboxesToSearch) { $Splat['ExchangeLocation'] = 'ALL' }
    if (-not $MailboxesToSearch -and ($ExceptionList -or $ExceptionFilePath)) {
        if ($ExceptionFilePath -or ($ExceptionList -and $ExceptionFilePath)) {
            $Exceptions = (Get-Content $ExceptionFilePath).split(',')
        }
        else { $Exceptions = $ExceptionList }
        $Splat['ExchangeLocationExclusion'] = $Exceptions
    }
    elseif ($MailboxesToSearch) { $Splat['ExchangeLocation'] = $MailboxesToSearch }

    $Session = Get-PSSession
    if ($_ExchangeServer -and ($Session.State -match 'Broken|Disconnected|Closed' -or (-not (Get-Command Get-ComplianceSearch -ErrorAction SilentlyContinue)) -or
            $Session.Count -gt 1 -or $Session.ComputerName -match 'ps.compliance.protection.outlook.com' -and
            (Get-PSSession -Name $_ExchangeServer -ErrorAction SilentlyContinue).State -ne 'Opened')) {
        Get-PSSession | Remove-PSSession
        Connect-OnPremExchange -Server $_ExchangeServer -Basic:$_ExchangeServerBasicAuth
    }
    elseif ($_ExchangeOnline -and ($Session.State -match 'Broken|Disconnected|Closed' -or (-not (Get-Command 'Get-ComplianceSearch' -ErrorAction SilentlyContinue) -or
                $Session.Count -gt 1 -or $Session.ComputerName -notmatch 'ps.compliance.protection.outlook.com'))) {
        Get-PSSession | Remove-PSSession
        Connect-ExchangeOnline -ConnectionUri 'https://ps.compliance.protection.outlook.com/powershell-liveid' -ShowBanner:$false
        Write-Host "You have successfully connected to Security & Compliance Center" -foregroundcolor "magenta" -backgroundcolor "white"
    }
    if (-not $_ExchangeServer -and -not $_ExchangeOnline) { return }

    $Splat
}