mail.psm1

function Search-MailFrom {
    <#
    .SYNOPSIS
        Search a mailbox by "from address"
    .DESCRIPTION
        Use a search string with a trailing wildcard(*) to filter the mailbox
    #>

    Param(
        [string]$Identity
        , # Cannot start with a wildcard, only end with *
        [string]$From
        , [datetime]$Start = (Get-date).Date
        , [datetime]$End = $Start.addDays(1).Date
        , [switch]$Delete
        , [string]$ResultTarget = $env:username
    )
    Search-Mailbox -identity $Identity -SearchQuery "received:$($Start.toString('yyyy-MM-dd'))..$($End.toString('yyyy-MM-dd')) AND from:`"$from`"" -TargetMailBox $ResultTarget -TargetFolder "Search" -DeleteContent:$Delete -Force:$Delete
}

function Search-MailSubject {
    <#
    .SYNOPSIS
        Search a mailbox by subject
    .DESCRIPTION
        Use a search string with a trailing wildcard(*) to filter the mailbox
    #>

    Param(
        [string]$Identity
        , # Cannot start with a wildcard, only end with *
        [string]$Subject
        , [datetime]$Start = (Get-date).Date
        , [datetime]$End = $Start.addDays(1).Date
        , [switch]$Delete
        , [string]$ResultTarget = $env:username
    )
    Search-Mailbox -identity $Identity -SearchQuery "received:$($Start.toString('yyyy-MM-dd'))..$($End.toString('yyyy-MM-dd')) AND subject:`"$Subject`"" -TargetMailBox $ResultTarget -TargetFolder "Search" -DeleteContent:$Delete
}

function Search-MailDate {
    <#
    .SYNOPSIS
        Search a users mailbox by day
    .EXAMPLE
        Search-MailDate -Identity <searchTarget>
 
        RunspaceId : xxxxxxxx-0000-1111-aaaa-1234abcd5678
        Identity : Domain/Name/Users/Group/searchTarget
        DisplayName : searchTarget
        TargetMailbox : Domain/Name/Users/Group/resultTarget
        TargetPSTFile :
        Success : True
        TargetFolder : \Search\searchTarget-19/10/2016 15:03:48
        ResultItemsCount : 29
        ResultItemsSize : 18.83 MB (19,742,786 bytes)
    #>

    Param(
        [string]$Identity
        , [datetime]$Start = (Get-date).Date
        , [datetime]$End = $Start.addDays(1).Date
        , [switch]$Delete
        , [string]$ResultTarget = $env:username
    )
    "received:$($Start.toString('yyyy-MM-dd'))..$($End.toString('yyyy-MM-dd'))"
    Search-Mailbox -identity $Identity -SearchQuery "received:$($Start.toString('yyyy-MM-dd'))..$($End.toString('yyyy-MM-dd'))" -TargetMailBox $ResultTarget -TargetFolder "Search" -DeleteContent:$Delete
}

$script:session
[bool]$script:import
function Import-MailServer {
    <#
    .SYNOPSIS
        Create the remote connection to the mail server as yourself.
    .DESCRIPTION
        Correctly open remote session to the exchange server which loads the exchange cmdlets.
 
        When you are finished remove the connection with Remove-MailServer
    .EXAMPLE
        Import-MailServer
        WARNING: The names of some imported commands from the module 'tmp_connection.hlk' include unapproved verbs that might
        make them less discoverable. To find the commands with unapproved verbs, run the Import-Module command again with the
        Verbose parameter. For a list of approved verbs, type Get-Verb.
 
        ModuleType Version Name ExportedCommands
        ---------- ------- ---- ----------------
        Script 1.0 tmp_connection.hlk {Add-ADPermission, Add-AvailabilityAddressSpace, Add-Conte...
        WARNING: Use 'Remove-Mailserver' to close the connection for other users to get on.
    #>

    Param(
        [string]
        $ComputerName = 'mailgate.birkdalehigh.co.uk',

        [PSCredential]
        $Credential,

        [ValidateSet('Script','Global')]
        $Scope = 'Global'
    )
    $test = Test-Connection $ComputerName -count 1 -ErrorAction Stop
    $host = [System.Net.Dns]::GetHostbyAddress($test.ProtocolAddress).HostName

    $script:session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$host/PowerShell/" -Authentication Kerberos -Credential:$Credential
    if($Scope -eq 'Global'){
        Import-module (Import-PSSession $script:session) -Global
    } else {
        if(-not $script:import){
            Import-PSSession $script:session
            $script:import = $true
        }
    }
    Write-Warning "Use 'Remove-Mailserver' to close the connection for other users to get on."
}
function Get-MailServer {
    <#
    .SYNOPSIS
        Get the mailserver session
    .DESCRIPTION
        Use the existing session to import into to higher powershell scope to get access to all the exchange cmdlets
    .EXAMPLE
        Import-Mailserver; Import-PSSession (Get-MailServer)
    #>

    if ($script:session){
        return $script:session
    }
    else{
        Write-Error 'Cannot find open session, use Import-Mailserver'
    }
}
function Remove-MailServer {
    <#
    .SYNOPSIS
        Removes the imported remote connection to the mail server.
    .EXAMPLE
        Remove-MailServer
    #>

    Remove-PSSession $script:session
}

function Get-RecentFailedMessage {
    <#
    .SYNOPSIS
        Show failed messages sent to the exchange server in the last hour
    .DESCRIPTION
        Filter the message tracking log for failed messages not caught by Sophos i.e PmE12Transport.
 
        This filtering should be checked regularly as messages failing here will not show up anywhere else.
    .EXAMPLE
        Get-RecentFailedMessage -View
 
        Timestamp EventId Source Sender Recipients MessageSubject
        --------- ------- ------ ------ ---------- --------------
        07/02/2017 11:17:42 FAIL SMTP Sent@example.com {demo@example.com} Re: Example
    .EXAMPLE
        Get-RecentFailedMessage | select source,recipientstatus,sender
 
        Source RecipientStatus Sender ClientIP
        ------ --------------- ------ ---------
        SMTP {550 5.7.1 Sender ID (PRA) Not Permitted} sent@example.com 127.0.0.1
    .NOTES
        550 5.7.1 Sender ID (PRA) Not Permitted = Incorrect SPF record from domain and clientIP
        To fix this you need to exclude the domain by appending to the SenderIdConfig BypassedSenderDomains list
        Set-SenderIdConfig -BypassedSenderDomains ( (Get-SenderIdConfig).BypassedSenderDomains += "example.com" )
        Polite message the sending domain technical support their DNS is configured incorrectly.
    #>

    Param(
        # Past amound of hours to get results
        $PastHours = 1

        , # Friendly view that cannot be consumed down the pipe
        [switch]
        $FormatView
    )
    Process{
        $result = Get-MessageTrackingLog -Start (get-date).addHours(-$PastHours) -EventId "FAIL" | Where-Object sourceContext -ne 'PmE12Transport'
        if($FormatView){
            Write-Output $result | format-table -AutoSize -property timestamp,source,sender,recipients,messagesubject
        } else {
            Write-Output $result
        }
    }
}

function Convert-DistributionGroupToSharedMailbox {
    [CmdletBinding()]
    <#
    .SYNOPSIS
        Recrete a group as a shared mailbox
    .DESCRIPTION
        A distribution gounp is a kind of address that exchanged cannot transform into a different type.
 
        This cmdlet takes a group, stores the address exchange uses to map the address lookup, removes the group and re-creates it as a shared mailbox. Then re-adds the address mapping.
    .EXAMPLE
        PS C:\> Get-DistributionGroup Finance | Convert-DistributionGroupToSharedMailbox
    .INPUTS
        Microsoft.Exchange.Data.Directory.Management.DistributionGroup
    .OUTPUTS
        Microsoft.Exchange.Data.Directory.Management.Mailbox
    .NOTES
        Order of operations.
        todo: test for exsiting distribution group
        todo: Check group membership to re-apply permissions on the mailbox
        Save LegacyExchangeDN attribute for outlook address book mappings
        Remove existing distribution group
        Create new shared mailbox in correct DN
        Add X500 of LegacyExchangeDN of distrubution group that was replaced
    #>

    Param(
        # Pipe Get-DistributionGroup to me.
        [Parameter(Mandatory)]
        [Microsoft.Exchange.Data.Directory.Management.DistributionGroup]
        $Group
    )
    Begin{
        Throw "Never tested. Not even once. Validate for yourself."
    }
    Process{
        $name = $Group.Name
        $Mapping = $Group.LegacyExchangeDN
        Remove-DistributionGroup -Identity $Name
        New-SharedMailbox -DisplayName $Name -Alias $Name | Set-Mailbox -EmailAddresses "X500:$Mapping"
    }
}
function New-SharedMailbox {
    <#
    .SYNOPSIS
        Create a Shared Mailbox with a default configuration
    .DESCRIPTION
 
    .EXAMPLE
        PS C:\> New-SharedMailbox -Name "AddressTitle" -DisplayName "Full Address Title" -Alias "address" -Users "personA", "PersonB"
        Name Alias
        ---- -----
        AddressTitle address
 
    .EXAMPLE
        PS C:\> New-SharedMailbox -Name reports -DisplayName Reports -Alias reports -Users OfficeAdmin,Receptionist
        Name Alias ServerName ProhibitSendQuota
        ---- ----- ---------- -----------------
        reports reports exchange01 unlimited
    .OUTPUTS
        Microsoft.Exchange.Data.Directory.Management.Mailbox
    .NOTES
        Order of operations;
        new shared mailbox in correct DN
        Assign mailbox send parameters
        Add persmissions for each user
            Check to add full send-from persmission from switch
        Add send on behalf permission
        Return mailbox
 
        Permissions:
        There's levels have been discovered in testing and not researched. Enterprise Administrators would be the top level that can do anything to exchange.
        The ability to Set-MailboxSentItemsConfiguration requires "Enterprise Administrators" group membership with the credentials used for import-mailserver.
        Creating the mailbox and assigning permissions requires only "Organization Management" membership.
    #>

    [CmdletBinding()]
    Param(
        # Will show as the name for the contact and mailbox
        [Parameter(Mandatory)]
        [string]
        $Name

        , # The name that appears in the Exchange Management Console under Recipient Configuration
        [string]
        $DisplayName

        , # Emaill address excluding @example.com
        [Parameter(Mandatory)]
        [alias('EmailAddress')]
        [ValidatePattern('.*[^@].*')]
        [string]
        $Alias

        , # Users that initially use the mailbox
        [string[]]
        $Users

        , # Path to shared mailbox user account, not an AD OU Path
        [string]
        [ValidatePattern('[^=;]')]
        $OrganizationalUnit = "bhs.internal/BHS/Mail Users"

        , # mailbox database storage location
        [string]
        $Database = "BHS Staff Mailbox Database"

        , # Users will get permission to send "<user> On behald of <account>", directly from <account> or not at all.
        [ValidateSet('None', 'Behalf', 'From')]
        [string]
        $Send = 'Behalf'
    )
    Begin{
    }
    Process{
        New-MailBox -Name $Name -DisplayName $DisplayName -Alias $alias -OrganizationalUnit $OrganizationalUnit -Database $Database -UserPrincipalName "$Alias@bhs.internal" -Shared
        $mailbox = Get-Mailbox -Identity $Name
        # By default sent items will only show in the senders account. This forces them into the shared sent items folder.
        Set-MailboxSentItemsConfiguration -Identity $mailbox.Identity -SendAsItemsCopiedTo 'SenderAndFrom' -SendOnBehalfOfItemsCopiedTo 'SenderAndFrom'
        $users | ForEach-Object {
            # Mailbox permissions allow the user to perform actions
            Add-MailBoxPermission $mailbox.Identity -AccessRights FullAccess -InheritanceType All -User $PSItem
            # AD permissions allow the users account#new (outlook) to find the mailbox
            Add-ADPermission $mailbox.Identity -ExtendedRights "Receive-As" -User $PSItem
            if($Send -eq 'From'){
                Add-ADPermission $mailbox.Identity -ExtendedRights "Send-As" -User $PSItem
            }
        }
        if($Send -eq 'Behalf'){
            # Send 'From' permissions will supersede this "On behalf" setting.
            # -GrantSendOnBehalfTo only replaces the list, you must append your own users list first.
            Set-Mailbox -Identity $mailbox.Identity -GrantSendOnBehalfTo $users > $null
            Write-Warning "The permission for users to send 'From' will supersede this permission, check their AD-Permissions for 'Send-As'"
        }
    }
    End{
        Write-Output $mailbox
    }
}

Export-ModuleMember -Function "Get-*", "Search-*", "New-*", "*-MailServer"