From f9d21a8dc6189ca72ebb8c5232d2c5c0a123d769 Mon Sep 17 00:00:00 2001 From: Sam Erde Date: Tue, 3 Feb 2026 15:54:13 -0500 Subject: [PATCH 1/3] chore: Add "Delicensing" and "MOERA" to custom spell check words --- .cspell.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.cspell.json b/.cspell.json index 3b13173e6..e816bcb7b 100644 --- a/.cspell.json +++ b/.cspell.json @@ -7,9 +7,11 @@ "language": "en", "words": [ "Contoso", + "Delicensing", "Docusaurus", "Entra", "Maester", + "MOERA", "passwordless", "SSPR", "XSPM" From eda7479dfb334d20f59aad04c62f25c343a79afc Mon Sep 17 00:00:00 2001 From: Sam Erde Date: Tue, 3 Feb 2026 15:58:05 -0500 Subject: [PATCH 2/3] refactor: Enhance Test-MtExoMoeraMailActivity function for clarity and error handling - Improve checks for require scopes and authentication context. - Proceed with obfuscated report if attempts to de-obfuscate fail. - More resilient cleanup of temp file in end block. - Improved verbose output in error handling. - Standardize variable naming. - Add timestamp to file output to avoid conflicts and clarify leftover history. - Fix condition where results were returned as an array instead of boolean. --- .../exchange/Test-MtExoMoeraMailActivity.ps1 | 215 ++++++++++-------- 1 file changed, 120 insertions(+), 95 deletions(-) diff --git a/powershell/public/maester/exchange/Test-MtExoMoeraMailActivity.ps1 b/powershell/public/maester/exchange/Test-MtExoMoeraMailActivity.ps1 index 95b8d34d1..c6eee7dc9 100644 --- a/powershell/public/maester/exchange/Test-MtExoMoeraMailActivity.ps1 +++ b/powershell/public/maester/exchange/Test-MtExoMoeraMailActivity.ps1 @@ -1,95 +1,120 @@ -<# -.SYNOPSIS - Checks the sent mail activity for MOERA addresses in the past 7 days. - -.DESCRIPTION - This command retrieves the mail actiivty for the past 7 days, and checks - for any sent mail from MOERA addresses. - -.EXAMPLE - Test-MtExoMoeraMailActivity - - Returns true if no sent mail activity from MOERA addresses in past 7 days. - -.LINK - https://maester.dev/docs/commands/Test-MtExoMoeraMailActivity -#> -function Test-MtExoMoeraMailActivity { - [CmdletBinding()] - [OutputType([bool])] - param() - - if (!(Test-MtConnection Graph)) { - Add-MtTestResultDetail -SkippedBecause NotConnectedGraph - return $null - } - - try { - Write-Verbose "Checking current report obfuscation" - $reportSettings = Invoke-MgGraphRequest -Method Get -Uri "v1.0/admin/reportSettings" - } catch { - Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_ - return $null - } - - if ($reportSettings.displayConcealedNames -and !((Get-MgContext).Scopes -contains "ReportSettings.ReadWrite.All")) { - Add-MtTestResultDetail -SkippedBecause LimitedPermissions - return $null - } elseif ($reportSettings.displayConcealedNames) { - try { - Write-Verbose "Disabling report obfuscation" - Invoke-MgGraphRequest -Method PATCH -Uri "v1.0/admin/reportSettings" -Body (@{displayConcealedNames = $false}|ConvertTo-Json) - } catch { - Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_ - return $null - } - } - $file = "$([System.IO.Path]::GetTempPath())maester-EmailActivityUserDetail.csv" - - try { - Write-Verbose "Downloading report" - $oProgressPreference = $ProgressPreference # save progressPreference - $ProgressPreference = 'SilentlyContinue' - Invoke-MgGraphRequest -Uri "v1.0/reports/getEmailActivityUserDetail(period='D7')" -OutputFilePath $file - } catch { - Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_ - return $null - } finally { - # Always restore progressPreference, even if exception occurs - $ProgressPreference = $oProgressPreference - } - $results = Import-Csv $file - $filteredResults = $results|Where-Object { - $_."User Principal Name" -like "*.onmicrosoft.com" -and ` - $_."Send Count" -gt 0 - } - - $testResult = ($filteredResults|Measure-Object).Count -gt 0 - if (!$testResult){ - $testResultMarkdown = "Well Done. Microsoft Online Exchange Routing Addresses (MOERA) are not in use for sending email in the past 7 days.`n`n" - } else { - $testResultMarkdown = "Microsoft Online Exchange Routing Addresses (MOERA) are in use for sending email in the past 7 days.`n`n" - $testResultMarkdown += "| User Principal Name | Send Count |`n" - $testResultMarkdown += "| --- | --- |`n" - foreach ($result in $filteredResults){ - $testResultMarkdown += "| $($result."User Principal Name") | $($result."Send Count") |`n" - } - } - - if ($reportSettings.displayConcealedNames) { - try { - Write-Verbose "Enabling report obfuscation" - Invoke-MgGraphRequest -Method PATCH -Uri "v1.0/admin/reportSettings" -Body (@{displayConcealedNames = $true}|ConvertTo-Json) - } catch { - Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_ - return $null - } - } - Write-Verbose "Removing temp report file" - Remove-Item $file - - Write-Verbose $testResultMarkdown|ConvertTo-Json -Compress - Add-MtTestResultDetail -Result $testResultMarkdown - - return !$result -} \ No newline at end of file +function Test-MtExoMoeraMailActivity { + <# + .SYNOPSIS + Checks the sent mail activity for MOERA addresses in the past 7 days. + + .DESCRIPTION + This command retrieves the mail activity for the past 7 days, and checks + for any sent mail from MOERA addresses. + + .EXAMPLE + Test-MtExoMoeraMailActivity + + Returns true if no sent mail activity from MOERA addresses in past 7 days. + + .LINK + https://maester.dev/docs/commands/Test-MtExoMoeraMailActivity + #> + [CmdletBinding()] + [OutputType([bool])] + param() + + begin { + if (!(Test-MtConnection Graph)) { + Add-MtTestResultDetail -SkippedBecause NotConnectedGraph + return $null + } + + # Prepare temp file for report download + $file = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath "Maester-EmailActivityUserDetail-$(Get-Date -Format yyMMddHHmmss).csv" + + # Track if we disabled obfuscation to re-enable it later + $script:obfuscationWasDisabled = $false + } + + process { + try { + Write-Verbose 'Checking current report obfuscation' + $reportSettings = Invoke-MgGraphRequest -Method Get -Uri 'v1.0/admin/reportSettings' + } catch { + Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_ + return $null + } + + # Check if report obfuscation is enabled (displayConcealedNames) and if we have the necessary permissions to disable it + # Note: This endpoint requires ReportSettings.ReadWrite.All permission (application permission, not delegated) + # and the application identity must have appropriate admin roles assigned (Reports Administrator or Security Administrator) + if ($reportSettings.displayConcealedNames -and ((Get-MgContext).Scopes -contains 'ReportSettings.ReadWrite.All')) { + try { + Write-Verbose 'Disabling report obfuscation' + [void](Invoke-MgGraphRequest -Method PATCH -Uri 'v1.0/admin/reportSettings' -Body (@{displayConcealedNames = $false } | ConvertTo-Json)) + $script:obfuscationWasDisabled = $true + } catch { + Write-Verbose "Failed to disable report obfuscation: $_. Continuing with obfuscated data." + } + } elseif ($reportSettings.displayConcealedNames) { + Write-Verbose 'Report obfuscation is enabled but insufficient permissions to disable it. Continuing without de-obfuscating user details.' + } + + try { + Write-Verbose 'Downloading report' + $previousProgressPreference = $ProgressPreference # save progressPreference + $ProgressPreference = 'SilentlyContinue' + Invoke-MgGraphRequest -Uri "v1.0/reports/getEmailActivityUserDetail(period='D7')" -OutputFilePath $file + } catch { + # Unable to download report + Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_ + return $null + } finally { + # Always restore progressPreference, even if exception occurs + $ProgressPreference = $previousProgressPreference + } + $results = Import-Csv $file -ErrorVariable ImportCsvError + + if (-not $results) { + Add-MtTestResultDetail -SkippedBecause Error -SkippedError "Failed to import CSV report: $ImportCsvError" + return $null + } + + # Filter for MOERA addresses (*.onmicrosoft.com) that have sent mail + # MOERA addresses are not intended for sending email and should not be used + $filteredResults = $results | Where-Object { + $_.'User Principal Name' -like '*.onmicrosoft.com' -and ` + $_.'Send Count' -gt 0 + } + + # Return true (pass) if no results found; false (fail) if any results found. + [bool]$testResult = ($filteredResults | Measure-Object).Count -eq 0 + if ($testResult) { + $testResultMarkdown = "Well Done. Microsoft Online Exchange Routing Addresses (MOERA) are not in use for sending email in the past 7 days.`n`n" + } else { + $testResultMarkdown = "Microsoft Online Exchange Routing Addresses (MOERA) are in use for sending email in the past 7 days.`n`n" + $testResultMarkdown += "| User Principal Name | Send Count |`n" + $testResultMarkdown += "| --- | --- |`n" + foreach ($result in $filteredResults) { + $testResultMarkdown += "| $($result.'User Principal Name') | $($result.'Send Count') |`n" + } + } + + Write-Verbose $testResultMarkdown + Add-MtTestResultDetail -Result $testResultMarkdown + + return $testResult + } + + end { + # Re-enable report obfuscation if we disabled it + if ($script:obfuscationWasDisabled) { + try { + Write-Verbose 'Re-enabling report obfuscation' + [void](Invoke-MgGraphRequest -Method PATCH -Uri 'v1.0/admin/reportSettings' -Body (@{displayConcealedNames = $true } | ConvertTo-Json)) + } catch { + # If we fail to re-enable obfuscation, log a warning but do not fail the test + Write-Warning "Failed to re-enable report obfuscation: $_" + } + } + + Write-Verbose 'Removing temp report file' + Remove-Item $file -ErrorAction SilentlyContinue + } +} From 53ab8dbea36823292608ab49e47406dc40801665 Mon Sep 17 00:00:00 2001 From: Sam Erde Date: Tue, 3 Feb 2026 16:17:00 -0500 Subject: [PATCH 3/3] refactor: Replace script-scoped variable with function-scoped variable for obfuscation tracking --- .../public/maester/exchange/Test-MtExoMoeraMailActivity.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powershell/public/maester/exchange/Test-MtExoMoeraMailActivity.ps1 b/powershell/public/maester/exchange/Test-MtExoMoeraMailActivity.ps1 index c6eee7dc9..7a3ca93b2 100644 --- a/powershell/public/maester/exchange/Test-MtExoMoeraMailActivity.ps1 +++ b/powershell/public/maester/exchange/Test-MtExoMoeraMailActivity.ps1 @@ -29,7 +29,7 @@ function Test-MtExoMoeraMailActivity { $file = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath "Maester-EmailActivityUserDetail-$(Get-Date -Format yyMMddHHmmss).csv" # Track if we disabled obfuscation to re-enable it later - $script:obfuscationWasDisabled = $false + $obfuscationWasDisabled = $false } process { @@ -48,7 +48,7 @@ function Test-MtExoMoeraMailActivity { try { Write-Verbose 'Disabling report obfuscation' [void](Invoke-MgGraphRequest -Method PATCH -Uri 'v1.0/admin/reportSettings' -Body (@{displayConcealedNames = $false } | ConvertTo-Json)) - $script:obfuscationWasDisabled = $true + $obfuscationWasDisabled = $true } catch { Write-Verbose "Failed to disable report obfuscation: $_. Continuing with obfuscated data." } @@ -104,7 +104,7 @@ function Test-MtExoMoeraMailActivity { end { # Re-enable report obfuscation if we disabled it - if ($script:obfuscationWasDisabled) { + if ($obfuscationWasDisabled) { try { Write-Verbose 'Re-enabling report obfuscation' [void](Invoke-MgGraphRequest -Method PATCH -Uri 'v1.0/admin/reportSettings' -Body (@{displayConcealedNames = $true } | ConvertTo-Json))