Exchange Online Restore and Recovery

Restore and Recovery Options for Exchange Online as of 11/16/2020:

Point-in-Time Restore: Not Available. Exchange Online is SaaS. There is no access to database or the log files in Exchange Online. 3rd Party solutions may produce experiences close to Point-in-Time restores, but its built on logic that uses EWS or other front end client access protocols, not necessarily connecting to the raw database and log files.

RetainDeletedItemsFor: 30 Days (max). When email items are hard-deleted from the mailbox, the items will be stored in the Deletions Recoverable Items folder where the items will be recoverable via OWA or Outlook using “Recover Deleted Items” functionality.

Holds:
Litigation Hold: When enabled, all items are protected and nothing will be purged from the mailbox
Compliance/InPlace Holds: When enabled, only items that match specific criteria will be protected and not purged from the mailbox.

SCC Retention Policy: Email can be protected for a certain period of time based on the Received date of the email item.

Lagged Copy for Logical Corruption: A passive database copy will be a lagged copy where it will delay processing log files for a certain time . In the event of logical database corruption, this passive database copy can be used to prevent further corruption to the local database copy

Soft-Deleted Mailbox or 3rd party restore in Exchange On-Premises (previous migrated/on-boarded to Exchange Online): If you recently migrated to Exchange Online, you may be able to access a soft-deleted version of the mailbox using Exchange On-Premises tools and any third party backup software that was used prior to the migration.

Single Item Recovery: Single Item Recovery is enabled by default in Exchange Online. If a user was to attempt to purge out of their “Recover Deleted Items” (Deletions Dumpster folder), than the message would still be retained for the “RetainDeletedItemsFor” period before it is purged.

Note: Folder structure is not recoverable, only email items.

ATP Safe Attachment Scanning Delay

Scenario: We noticed that messages were taking longer to deliver when ATP Safe Attachments was being called for message processing. Users in the Dynamic action policy noticed that the attachment would be reinserted hours after the message delivery. Other users in a Replace action policy noticed that the entire message was delayed delivery for hours.

Script: We found a script online that we modified to fit our needs. Here is our modified version of the script:

ATP Safe Attachment Scanning Delay Scriptlets


#Declare Variables
$Outfile = “C:\temp\ATPSafeAttachmentScan-$(Get-Date -Format “MMddyyyy_hhmmss”).csv”
$MessageList = $null 
$CurrMessages = $null
$Page = 1
$PageSize = 5000
$start = (Get-Date).AddHours(-4)
$end = Get-Date

$MessgaeListCount = 0


#Loop for Message Trace
do 
{   
Write-Host “Collecting Message Trace – Current Count: $MessageListCount – Page $Page…” 

$CurrMessages = Get-MessageTrace -StartDate $start -EndDate $end -PageSize $pagesize -Page $Page -Status Delivered | Where {$_.Size -gt 1MB}

$Page++ 
$MessageList += $CurrMessages 
$MessageListCount = $messageList.count
} until ($CurrMessages -eq $null)



#Loop Message Trace Results for ATP Events
$Row = 0
$TotalRow = $MessageList.count

$MessageList | Select-Object -Skip $Row | % {
    $ID = ($_.MessageTraceId).Guid
    $Sender = $_.SenderAddress
    $Recipient = $_.RecipientAddress
    $Size = $_.Size
    $MessageDetails = $_ | Get-MessageTraceDetail | where { $_.Event -eq “Advanced Threat Protection” -or $_.Event -eq “Deliver”} | sort Date
    $First = ($MessageDetails | select -First 1).Date
    $Last = ($MessageDetails | select -Last 1).Date


    # Only if ATP was used
    If ($First -ne $null -and $Last -ne $null) {
        If ($First -eq $Last) {$Delay = 0}
        Else { $Delay =  [math]::Round((New-TimeSpan –Start $First –End $Last).TotalSeconds,0) }
        $Item = New-Object System.Object;
        $Item | Add-Member -Type NoteProperty -Name “ID” -Value $ID;
       $Item | Add-Member -Type NoteProperty -Name “Sender” -Value $Sender;
        $Item | Add-Member -Type NoteProperty -Name “Recipient” -Value $Recipient;
        $Item | Add-Member -Type NoteProperty -Name “Size” -Value $Size;        $Item | Add-Member -Type NoteProperty -Name “Start” -Value $First;
        $Item | Add-Member -Type NoteProperty -Name “End” -Value $Last;
        $Item | Add-Member -Type NoteProperty -Name “Delay” -Value $Delay;
        #$results += $Item
        $Item | Export-Csv $Outfile  -NoTypeInformation -Append    }

    $Row++
    Write-Host “$(Get-Date -Format “HH:mm”): Processed row $Row out of $TotalRow” }
}






#REF: Original Script that we modified: https://jocha.se/blog/tech/exchange-atp-attachment-delay

Exchange Online: Place a hold on a Soft-Deleted mailbox

Scenario: Recently a user has left the organization and you need to place a hold on the Exchange Online mailbox. Currently the mailbox is SoftDeleted (get-mailbox -softdeletedmailbox).

Solution/Scriptlets:
If a Microsoft Security and Compliance case is not already configured, create a one via PowerShell of course! The scriptlets below will create the Compliance Case, Case Hold Policy, and the Case Hold Rule. The Case Hold Policy will group all specified mailboxes to be placed on Hold and the Case Hold Rule will set the entire mailbox on hold.

New-ComplianceCase “InactiveMailboxes_Hold” -Description “This is a hold for mailboxes that are no longer active”

New-CaseHoldPolicy -Case InactiveMailboxes_hold -Name InactiveMailboxes_Hold -ExchangeLocation Steve,Bob -Enabled $true


Note: If the mailboxes are Inactive/SoftDeleted, you may have to place a period in front of the name similar to this command: New-CaseHoldPolicy -Case InactiveMailboxes_hold -Name InactiveMailboxes_Hold -ExchangeLocation .Steve,.Bob -Enabled $true

New-CaseHoldRule -Name InactiveMailboxes_Hold -Policy InactiveMailboxes_hold


If the eDiscovery case is already created and you need to add users, run the following:

set-caseholdpolicy InactiveMailboxes_hold -AddExchangeLocation Jim

Complex Search criteria for New-ComplianceSearch -ContentMatchQuery

Scenario: You need to search the entire Exchange Online organization for emails with specific criteria:
-Received: 5/1/2020 — 5/15/2020
-Subject: “This is a test”
-Keywords: Trophy OR Lose OR Award

Scriptlets:

$name = “Search_11102020”

New-ComplianceSearch -Name $name -Description “This is a compliance search made from PowerShell” -ExchangeLocation ALL -ContentMatchQuery “Trophy (c:s) Lose(c:s) Award(c:c)(subject:’This is a test’)(received=2020-05-01..2020-05-15)”

Start-ComplianceSearch $name

Notes:
(c:c) = AND
(c:s) = OR


Put a hold on a Soft-Deleted Mailbox in Exchange Online

LEGACY: The instruction below with the steps to create a Mailbox Search is no longer relevant and has been deprecated by Microsoft. Go to this post for an updated version on how to perform this via the Microsoft Security and Compliance Center:
https://ex-shell.com/2020/11/11/exchange-online-place-a-hold-on-a-soft-deleted-mailbox/

Scenario: A person is no longer with the organization and their mailbox is currently retained in Exchange Online as a Soft-Deleted mailbox (get-mailbox -softdeletedmailbox). It was discovered that we need to place the mailbox on a hold to keep the email data preserved.

Scriptlets:
We are going to create a InPlace Hold on the inactive mailbox by running the following commands:

$mbx = get-mailbox -identity <user> -softdeletedmailbox

New-MailboxSearch -Name “InactiveMailboxHold” -SourceMailboxes $mbx.DistinguishedName -InPlaceHoldEnabled $true



DeviceAccessState: Blocked DeviceAccessStateReason: CommandFrequency

Scenario: After a migration to Exchange Online, multiple mobile devices for a single user were receiving an error similar to: Cannot Get Mail – The connection to the server failed. When running this command: Get-mobiledevicestatistics -mailbox <user> | Select DeviceType, DeviceAccessState,LastSuccessSync we found that the device was blocked due to Command Frequency.

What was happening: It appears that Microsoft was throttling all ActiveSync requests to the server for this specific migrated mailbox. After reviewing and monitoring, the device would unblock itself and continue to successfully sync. The theory is that there were multiple devices attempting to synchronize the mailbox data at the exact same time. Plus the administrator was clicking in and out of folders on the mobile device during the initial sync.

Scriptlet: We were monitoring the mobile device status for the mailbox using a command similar to this:

Do{$d = get-date;
$d = $d.tostring();”
Checking devices at :$d
“;
Get-MobileDeviceStatistics -Mailbox usera | Where deviceuseragent -like Apple*|Select DeviceType,DeviceAccessState,lastsucce*;
sleep 15;}While($c -ne 10101)

How many users are actually using the IMAP and POP protocols for Exchange On-Premises?

Scenario: There is a lot of mailbox in Exchange On-Premises that have IMAP and POP enabled when running get-casmailbox, but how many are actually using those protocols to connect to Exchange? Use the scriptlets below to help:

Scriptlets: Lets first grab the users who have IMAP and POP enabled in the environment and then compare it to the IMAP and POP Logs.

1. Filter For IMAP and POP Enabled Users

$ImapEnabled = Get-CASMailbox -Filter {Imapenabled -eq $true} -ResultSize unlimited
$PopEnabled = Get-CASMailbox -Filter {Popenabled -eq $true} -ResultSize unlimited

2. Parse through the IMAP and POP Logs to see which accounts are actually connecting with those protocols. (LogParser needs to be installed)

#Declare Variables
$files = @()
$servers = “ExServer1″,”ExServer2”


#Loop it to find log files
$servers | %{
“Searching Files on: $_”

$files += Get-childitem “\$_\c$\Program Files\Microsoft\Exchange Server\V15\Logging\Imap4\IMAP4.log” | Where name -notlike “BE

$files += Get-childitem “\$_\c$\Program Files\Microsoft\Exchange Server\V15\Logging\Pop3\POP3.log” | Where name -notlike “BE

}


#Separate the IMAP and POP Log Files
$IMAPFiles = $files | Where fullname -like “\IMAP4*” | Select -ExpandProperty fullname
$POPFiles = $files | Where fullname -like “\POP3*” | Select -ExpandProperty fullname


#Search IMAP logs and put all unique values into $IMAPAll
$IMAPLogs = @()
$IMAPfiles | %{
“$_”
$1 = & “C:\Program Files (x86)\Log Parser 2.2\logparser.exe” -i:CSV -q:on -rtp:-1 -nskiplines:5 @”
Select user From ‘$_’
“@
$IMAPLogs += $1
$IMAPLogs = $IMAPLogs | select -Unique
}


#Search POP logs and put all unique values into $POPAll
$POPLogs = @()
$Popfiles | %{
“$_”
$2 = & “C:\Program Files (x86)\Log Parser 2.2\logparser.exe” -i:CSV -q:on -rtp:-1 -nskiplines:5 @”
Select user From ‘$_’
“@
$POPLogs += $2
$POPLogs = $PopLogs | select -Unique
}

3. Review and Compare the Data Sets

Now you have two data sets that you can review and compare. The users that are actively using IMAP and POP are stored in the $IMAPLogs and $POPLogs. Everyone else found in $IMAPEnabled and $POPEnabled tha tare not in the $IMAPLogs and $POPLogs could have the protocols potentially disabled.

You could use Excel or commands similar to below:

Compare-Object $ImapLogs $($ImapEnabled.name) | Where sideindicator -eq “<=”
Compare-Object $POPLogs $($POPEnabled.name) | Where sideindicator -eq “<=”

Run Get-ADUser by pulling the WindowsLiveID from the Exchange Online Mailbox

Scenario: You need to pull in additional AD properties for users with Exchange Online mailboxes that are only available when running the Get-ADUser command because they are not included in the AD Sync to Microsoft Online/Azure.

Scriptlet:

Declare Variables:

$mbx = Get-mailbox -resultsize unlimited

$ADUserData = @()

$c=0 #Just for a counter

Run the Loop:

$mbx | Where WindowsLiveID -ne “” | Sort | %{

$c++ #Increase the Counter

$upn = $_.windowsliveid #Create the UPN based off windowsliveid

$f = “Userprincipalname -eq ‘$upn'” #Create a Filter for get-aduser

“$c – $f ” #Display on PS Screen

$ADuserData += get-aduser -filter $f -properties * #Fill in $userData with Get-Aduser Data

}

Timeout and Errors when running ‘Get-Mailbox -resultsize Unlimited’ within Exchange Online?

Scearnio: With a large number of mailboxes in our Exchange Online environment, anytime we run a command where it is get-mailbox -resultsize unlmited, we also run into errors/timeouts due to the amount of time and size it takes.

WorkAround: Use filtering within the get-mailbox command to split the data pull and join them into a Master Variable:

Create Variables

$All = @()
$d = (Get-date).adddays(-730)

Set Filter and Run

$f = “{WhenMailboxCreated -ge ‘$d’}”
$1 = get-recipient -filter $f -resultsize unlimited | select name, windowsliveid,primarysmtpaddress

Change Filter and Run

$f = “{WhenMailboxCreated -lt ‘$d’}”
$2 = get-recipient -filter $f -resultsize unlimited| select name, windowsliveid,primarysmtpaddress

Build the Master Variable to contain both datasets

$All += $1
$All += $2

The call to ‘https://mail.domain.com/EWS/mrsproxy.svc’ failed because no service was listening on the specified endpoint.

Scenario: When migrating (onboarding) to Exchange Online, you may receive a error message similar to this:

The call to ‘https://servername/EWS/mrsproxy.svc&#8217; failed because no service was listening on the specified endpoint. Error details: There was no endpoint listening at https://servername/EWS/mrsproxy.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details. –> The remote server returned an error: (404) Not Found.

Solution: Verify the ExchangeGuid is the same value:

Exchange OnPrem: Get-Mailbox <alias> | Select ExchangeGUID

Exchange Online: Get-mailuser <alias> | Select Exchange GUID

Fix – Exchange Online: Set-mailuser <alias> -exchangeguid <GUID of ExOnPrem user>