Skip to content

Commit

Permalink
Merge branch 'main' into CalLogAugTweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
dpaulson45 authored Sep 10, 2024
2 parents 1dccacc + 05a447d commit f1f0734
Show file tree
Hide file tree
Showing 16 changed files with 766 additions and 25 deletions.
4 changes: 4 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@
# MDO script owners
/M365/MDO/ @iserrano76 @rosspa05 @microsoft/css-exchange-admins
/docs/M365/MDO/ @iserrano76 @rosspa05 @microsoft/css-exchange-admins

# EXO PF Team
/PublicFolders/Update-PublicFolderPermissions.ps1 @vishmittal @microsoft/css-exchange-admins
/docs/PublicFolders/Update-PublicFolderPermissions.md @vishmittal @microsoft/css-exchange-admins
2 changes: 1 addition & 1 deletion M365/MDO/MDOThreatPolicyChecker.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ process {
$spamMatchedRule = Test-Rules -Rules $hostedContentFilterRules -Email $stEmailAddress
}
if ($null -eq $spamMatchedRule) {
Write-Host "`nAnti-spam::`n`tDefault policy" -ForegroundColor Yellow
Write-Host "`nAnti-spam:`n`tDefault policy" -ForegroundColor Yellow
$hostedContentFilterPolicy = Get-HostedContentFilterPolicy "Default"
} else {
$hostedContentFilterPolicy = Get-HostedContentFilterPolicy $spamMatchedRule.Name
Expand Down
343 changes: 343 additions & 0 deletions PublicFolders/Update-PublicFolderPermissions.ps1

Large diffs are not rendered by default.

215 changes: 215 additions & 0 deletions Transport/Measure-EmailDelayInMTL.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

<#
.NOTES
Name: Measure-EmailDelayInMTL.ps1
Requires: User Rights
Major Release History:
08/05/2024 - Initial Release
.SYNOPSIS
Generates a report of the maximum message delay for all messages in an Message Tracking Log.
.DESCRIPTION
Gather message tracking log details of all message to / from a given recipient for a given time range.
Recommend using Start-HistoricalSearch in EXO.
The script will provide an output of all unique message ids with the following information:
MessageID
Time Sent
Total Time in transit
Useful for determining if a "slow" message was a one off or a pattern.
.PARAMETER MTLFile
MTL File to process.
.PARAMETER ReportPath
Folder path for the output file.
.OUTPUTS
CSV File with the following information.
MessageID ID of the Message
TimeSent First time we see the message in the MTL
TimeReceived Last delivery time in the MTL
MessageDelay How long before the message was delivered
Default Output File:
$PSScriptRoot\MTL_report.csv
.EXAMPLE
.\Measure-EmailDelayInMTL -MTLPath C:\temp\MyMtl.csv
Generates a report from the MyMtl.csv file.
#>

[CmdletBinding()]
param (
[Parameter()]
[string]
$MTLFile,
[Parameter()]
[string]
$ReportPath = $PSScriptRoot
)

. $PSScriptRoot\..\Shared\ScriptUpdateFunctions\Test-ScriptVersion.ps1

function Test-CSVData {
param(
[array]$CSV,
[array]$ColumnsToCheck
)

# Check to make sure we have data in the CSV
if (($null -eq $CSV) -or !($CSV.count -gt 0)) {
Write-Error "Provided CSV null or empty" -ErrorAction Stop
return $false
}

# Read thru the data and make sure we have the needed columns
$ColumnHeaders = ($CSV | Get-Member -MemberType NoteProperty).Name
foreach ( $ColumnToCheck in $ColumnsToCheck) {
if (!($ColumnHeaders.ToLower().Contains($ColumnToCheck.ToLower())) ) {
return $false
}
}
return $true
}

if (Test-ScriptVersion -AutoUpdate) {
# Update was downloaded, so stop here.
Write-Host "Script was updated. Please rerun the command."
return
}

# make sure out output variable is null
$output = $Null

# Test for the provided file and load it.
# Need to make sure the MTL file is there and if so load it.
# Straight from EXO it will be in Unicode. Onprem and modified files are not.
# First verify the file
if (!(Test-Path $MTLFile)) {
Write-Error "Unable to find the specified file" -ErrorAction Stop
}

# Make sure the path for the output is good
if (!(Test-Path $ReportPath)) {
Write-Error ("Unable to find report path " + $ReportPath)
}

# Try to load the file with Unicode since we need to start somewhere.
$mtl = Import-Csv $MTLFile -Encoding Unicode

# If it is null then we need to try without Unicode
if ($null -eq $mtl) {
Write-Host "Failed to Load as Unicode; trying normal load"
$mtl = Import-Csv $MTLFile
# If we still have nothing then log an error and fail
if ($null -eq $mtl) {
Write-Error "Failed to load CSV" -ErrorAction Stop
}
# Need to know that we loaded without Unicode.
else {
Write-Host "Loaded CSV without Unicode"
}
} else {
Write-Host "Loaded MTL with Unicode"
}

# Detecting if this is an onprem MTL
if (Test-CSVData -CSV $mtl -ColumnsToCheck "eventid", "source", "messageId", "timestamp") {
Write-Host "On Prem message trace detected; Updating property names"
$mtl = $mtl | Select-Object -Property @{N = "date_time_utc"; E = { $_.timestamp } }, @{N = "message_id"; E = { $_.messageID } }, source, @{N = "event_id"; E = { $_.EventId } }
}

# Making sure the MTL contains the fields we want.
if (!(Test-CSVData -CSV $mtl -ColumnsToCheck "event_id", "source", "message_id", "date_time_utc")) {
Write-Error "MTL is missing one or more required fields." -ErrorAction Stop
}

# Converting our strings into [DateTime]
Write-Host "Converting date_time_utc values"
for ($i = 0; $i -lt $mtl.Count; $i++) {
$mtl[$i].date_time_utc = Get-Date($mtl[$i].date_time_utc)
}

# get all of the unique message IDs in the file.
[array]$uniqueMessageIDs = $mtl | Select-Object -ExpandProperty message_id | Sort-Object | Get-Unique

if ($uniqueMessageIDs.count -eq 0) {
Write-Error "No Unique MessageIDs found in data."
}

# Carve the data up into smaller collections
# Most of what is in the MTL we don't need
$SMTPReceive = $mtl | Where-Object { ($_.event_id -eq 'Receive') -and ($_.source -eq 'SMTP') }
$StoreDeliver = $mtl | Where-Object { ($_.event_id -eq 'Deliver') -and ($_.source -eq 'StoreDriver') }
$SMTPDeliver = $mtl | Where-Object { ($_.event_id -eq 'SendExternal') -and ($_.source -eq 'SMTP') }

# Loop thru each unique messageID
foreach ($id in $uniqueMessageIDs) {

# make sure we aren't carrying anything over from the previous foreach.
$AllSentTimes = $Null
$AllStoreDeliverTimes = $Null
$AllRemoteDeliverTimes = $Null

# extract the times for a message ID ... there can be more than one of each of these.
[array]$AllSentTimes = ($SMTPReceive | Where-Object { ($_.message_id -eq $id) }).date_time_utc
[array]$AllStoreDeliverTimes = ($StoreDeliver | Where-Object { ($_.message_id -eq $id) }).date_time_utc
[array]$AllRemoteDeliverTimes = ($SMTPDeliver | Where-Object { ($_.message_id -eq $id) }).date_time_utc

# If we didn't find any sent information then drop the messageID
if ($AllSentTimes.count -eq 0) {
Write-Warning ($id.ToString() + " unable to find sent time. Discarding messageID")
continue
}

# If we didn't find any delivery information then drop the messageID
if ($AllStoreDeliverTimes.count -eq 0 -and $AllRemoteDeliverTimes.count -eq 0) {
Write-Warning ($id + " not able to find delivery time in MTL. Discarding messageID")
continue
}

# Get the newest time sent that we found
$SortedTimeSent = Get-Date ($AllSentTimes | Sort-Object | Select-Object -First 1)

# Combine all of the delivery times and grab the newest one
$SortedTimeDelivered = (($AllStoreDeliverTimes + $AllRemoteDeliverTimes) | Sort-Object | Select-Object -Last 1)

# Build report object
[array]$output += [PSCustomObject]@{
MessageID = $id
TimeSent = $SortedTimeSent
TimeReceived = $SortedTimeDelivered
MessageDelay = $SortedTimeDelivered - $SortedTimeSent
}
}

# Make sure we have something to output
if ($null -eq $output) {
Write-Error "No output generated" -ErrorAction Stop
} else {

# Export the data to the output file
$outputFile = (Join-Path -Path $ReportPath -ChildPath ("MTL_Latency_Report_" + (Get-Date -Format FileDateTime).ToString() + ".csv"))
$output | Sort-Object -Property MessageDelay -Descending | Export-Csv -IncludeTypeInformation:$false -Path $outputFile
Write-Output ("Report written to file " + $outputFile)

# Gather general statistical data and output to the screen
$Stats = ($output.MessageDelay.TotalMilliseconds | Measure-Object -Average -Maximum -Minimum)

$GeneralData = [PSCustomObject]@{
EmailCount = $Stats.Count
MaximumDelay = [TimeSpan]::FromMilliseconds($Stats.Maximum)
MinimumDelay = [TimeSpan]::FromMilliseconds($Stats.Minimum)
AverageDelay = [TimeSpan]::FromMilliseconds($Stats.Average)
}

Write-Output $GeneralData
}
1 change: 1 addition & 0 deletions docs/Emerging-Issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ This page lists emerging issues for Exchange On-Premises deployments, possible r

|**Updated on**|**Update causing the issue**|**Issue**|**Workaround/Solution**|
|-|-|-|-|
| 8/23/2024 | [August 2024 update for Windows](https://support.microsoft.com/kb/5041578) | After installing the [August 2024 update for Windows](https://support.microsoft.com/kb/5041578), MS Exchange Transport service may start crashing | Please follow steps in [this KB](https://learn.microsoft.com/windows/release-health/status-windows-10-1809-and-windows-server-2019#3375msgdesc) |
| 4/23/2024 | [March 2024 Security Update for Exchange 2019,2016](https://techcommunity.microsoft.com/t5/exchange-team-blog/released-march-2024-exchange-server-security-updates/ba-p/4075348) | After installing the [March 2024 Security Update]((https://techcommunity.microsoft.com/t5/exchange-team-blog/released-march-2024-exchange-server-security-updates/ba-p/4075348)), Search in Outlook (cached mode) may show "We're having trouble fetching results from the server...". The search works fine in OWA or Outlook online mode. | Please install [April 2024 Hotfix Update](https://techcommunity.microsoft.com/t5/exchange-team-blog/released-april-2024-exchange-server-hotfix-updates/ba-p/4120536) |
| 4/23/2024 | [March 2024 Security Update for Exchange 2019,2016](https://techcommunity.microsoft.com/t5/exchange-team-blog/released-march-2024-exchange-server-security-updates/ba-p/4075348) | After installing the Security Update, add-ins may stop working with following error <BR><BR>"Add-in Error Something went wrong and we couldn't start this add-in. Please try again later or contact your system administrator | Please install [April 2024 Hotfix Update](https://techcommunity.microsoft.com/t5/exchange-team-blog/released-april-2024-exchange-server-hotfix-updates/ba-p/4120536) |
| 4/23/2024 | [March 2024 Security Update for Exchange 2019,2016](https://techcommunity.microsoft.com/t5/exchange-team-blog/released-march-2024-exchange-server-security-updates/ba-p/4075348) |After installing the March 2024 Security Update, Unread envelope icon is not getting updated after applying March 2024 SU | Please install [April 2024 Hotfix Update](https://techcommunity.microsoft.com/t5/exchange-team-blog/released-april-2024-exchange-server-hotfix-updates/ba-p/4120536) |
Expand Down
82 changes: 58 additions & 24 deletions docs/M365/MDO/MDOThreatPolicyChecker.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,37 @@

Download the latest release: [MDOThreatPolicyChecker.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/MDOThreatPolicyChecker.ps1)

This script checks which Microsoft Defender for Office 365 and Exchange Online Protection threat policies cover a particular user, including anti-malware, anti-phishing, inbound and outbound anti-spam, as well as Safe Attachments and Safe Links policies in case these are licensed for your tenant. In addition, the script can check for threat policies that have inclusion and/or exclusion settings that may be redundant or confusing and lead to missed coverage of users or coverage by an unexpected threat policy.
Use this script to find inconsistencies or redundancies in user membership and policy application of Microsoft Defender for Office 365 and Exchange Online Protection threat policies that lead to missed or unexpected coverage of users by the policy. If issues are found, the script provides guidance on how to resolve them.

It also includes an option to show all the actions and settings of the policies that apply to a user.
The script also helps you identify which threat policies cover a particular user, including anti-malware, anti-phishing, inbound and outbound anti-spam, as well as Safe Attachments and Safe Links policies in case these are licensed for your tenant.

## Common Usage
The script uses Exchange Online cmdlets from Exchange Online module and Microsoft.Graph cmdLets from Microsoft.Graph.Authentication, Microsoft.Graph.Groups and Microsoft.Graph.Users modules.
The script can help with such questions as:

To run the PowerShell Graph cmdlets used in this script, you need only the following modules from the Microsoft.Graph PowerShell SDK:
- Microsoft.Graph.Groups: Contains cmdlets for managing groups, including `Get-MgGroup` and `Get-MgGroupMember`.
- Microsoft.Graph.Users: Includes cmdlets for managing users, such as `Get-MgUser`.
- Microsoft.Graph.Authentication: Required for authentication purposes and to run any cmdlet that interacts with Microsoft Graph.
- Are there confusing policies with conditions that lead to unexpected coverage or coverage gaps?

- Which threat policies apply to a recipient, **or should have applied** but did not? **No actual detection or Network Message ID needed.**

- Which actions would be taken on an email for each policy matched?

The script runs only in Read mode from Exchange Online and Microsoft Graph PowerShell. It does not modify any policies, and only provides actionable guidance for administrators for remediation.

## Prerequisites
The script uses Powershell cmdlets from the Exchange Online module and from the Microsoft.Graph.Authentication, Microsoft.Graph.Groups, and Microsoft.Graph.Users modules.

To run the Graph cmdlets used in this script, you only need the following modules from the Microsoft.Graph PowerShell SDK:

- Microsoft.Graph.Groups: for managing groups, including `Get-MgGroup` and `Get-MgGroupMember`.

- Microsoft.Graph.Users: for managing users, such as `Get-MgUser`.

- Microsoft.Graph.Authentication: for authentication purposes and to run any cmdlet that interacts with Microsoft Graph.

You can find the Microsoft Graph modules in the following link:<br>
&nbsp;&nbsp;&nbsp;&nbsp;https://www.powershellgallery.com/packages/Microsoft.Graph/<br>

&nbsp;&nbsp;&nbsp;&nbsp;https://learn.microsoft.com/en-us/powershell/microsoftgraph/installation?view=graph-powershell-1.0#installation


Here's how you can install the required submodules for the PowerShell Graph SDK cmdlets:

```powershell
Expand All @@ -31,12 +46,12 @@ Install-Module -Name Microsoft.Graph.Users -Scope CurrentUser
Remember to run these commands in a PowerShell session with the appropriate permissions. The -Scope CurrentUser parameter installs the modules for the current user only, which doesn't require administrative privileges.


In the Graph connection you will need the following scopes 'Group.Read.All','User.Read.All'<br>
In the Graph connection, you will need the following scopes 'Group.Read.All','User.Read.All'<br>
```powershell
Connect-MgGraph -Scopes 'Group.Read.All','User.Read.All'
```
<br><br>
You need as well an Exchange Online session.<br>
You also need an Exchange Online session.<br>
```powershell
Connect-ExchangeOnline
```
Expand All @@ -46,16 +61,40 @@ You can find the Exchange module and information in the following links:<br>
&nbsp;&nbsp;&nbsp;&nbsp;https://www.powershellgallery.com/packages/ExchangeOnlineManagement


## Examples:
To check all threat policies for potentially confusing user inclusion and/or exclusion conditions and print them out for review, run the following:<br>
```powershell
.\MDOThreatPolicyChecker.ps1
```
## Parameters and Use Cases:
Run the script without any parameters to review all threat protection policies and to find inconsistencies with user inclusion and/or exclusion conditions:

To provide a CSV input file with email addresses and see only EOP policies, run the following:<br>
```powershell
.\MDOThreatPolicyChecker.ps1 -CsvFilePath [Path\filename.csv]
```
!['No Logical inconsistencies found'](img/No-Logical-Inconsistencies.png)

**Script Output 1: No logical inconsistencies found** message if the policies are configured correctly, and no further corrections are required.

![Potentially illogical inclusions found.](img/Logical-Inconsistency-Found.png)

**Script Output 2: Logical inconsistencies found**. Inconsistencies found in the antispam policy named 'Custom antispam policy', and consequent recommendations shown -- illogical inclusions as both users and groups are specified. This policy will only apply to the users who are also members of the specified group.

- IncludeMDOPolicies

Add the parameter -IncludeMDOPolicies to view Microsoft Defender for Office 365 Safe Links and Safe Attachments policies:

![Policies, including MDO.](img/Show-Policies-Including-MDO.png)

**Script Output 3: Parameters -EmailAddress and -IncludeMDOPoliciesEOP** specified to validate Microsoft Defender for Office 365 Safe Attachments and Safe Links policies, on top of Exchange Online Protection policies.

- ShowDetailedPolicies

To see policy details, run the script with the -ShowDetailedPolicies parameter:

![Show policy actions.](img/Show-Detailed-Policies-1.png)

![Show policy actions.](img/Show-Detailed-Policies-2.png)

![Show policy actions.](img/Show-Detailed-Policies-3.png)

![Show policy actions.](img/Show-Detailed-Policies-4.png)

**Script Output 4: Policy actions**. Use -ShowDetailedPolicies to see the details and actions for each policy.

## Additional examples

To provide multiple email addresses by command line and see only EOP policies, run the following:<br>
```powershell
Expand All @@ -72,11 +111,6 @@ To provide an email address and see only MDO (Safe Attachment and Safe Links) po
.\MDOThreatPolicyChecker.ps1 -EmailAddress [email protected] -OnlyMDOPolicies
```

To see the details of the policies applied to mailbox in a CSV file for both EOP and MDO, run the following:<br>
```powershell
.\MDOThreatPolicyChecker.ps1 -CsvFilePath [Path\filename.csv] -IncludeMDOPolicies -ShowDetailedPolicies
```

To get all mailboxes in your tenant and print out their EOP and MDO policies, run the following:<br>
```powershell
.\MDOThreatPolicyChecker.ps1 -IncludeMDOPolicies -EmailAddress @(Get-ExOMailbox -ResultSize unlimited | Select-Object -ExpandProperty PrimarySmtpAddress)
Expand Down
Binary file added docs/M365/MDO/img/Logical-Inconsistency-Found.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/M365/MDO/img/No-Logical-Inconsistencies.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/M365/MDO/img/Show-Detailed-Policies-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/M365/MDO/img/Show-Detailed-Policies-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/M365/MDO/img/Show-Detailed-Policies-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/M365/MDO/img/Show-Detailed-Policies-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/M365/MDO/img/Show-Policies-Including-MDO.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit f1f0734

Please sign in to comment.