From fe9c47e1adef156f1258a5f7507e0cf2cf96bf17 Mon Sep 17 00:00:00 2001 From: Merill Fernando Date: Sun, 23 Jun 2024 18:32:08 +1000 Subject: [PATCH 1/9] Added SkipReason support --- powershell/Maester.psd1 | 2 +- powershell/internal/Get-MtSkippedReason.ps1 | 21 +++++++ powershell/public/Add-MtTestResultDetail.ps1 | 22 ++++++- .../exchange/Test-MtCisaAntiSpamAllowList.ps1 | 52 ++++++++++++++++ .../exchange/Test-MtCisaAntiSpamSafeList.ps1 | 46 ++++++++++++++ .../Test-MtCisaAutoExternalForwarding.ps1 | 5 ++ .../exchange/Test-MtCisaCalendarSharing.ps1 | 56 +++++++++++++++++ .../exchange/Test-MtCisaContactSharing.ps1 | 56 +++++++++++++++++ .../Test-MtCisaExternalSenderWarning.ps1 | 60 +++++++++++++++++++ .../exchange/Test-MtCisaMailboxAuditing.ps1 | 42 +++++++++++++ .../Test-MtCisaSmtpAuthentication.ps1 | 38 ++++++++++++ tests/CISA/@template.Testsps1.txt | 7 ++- .../Test-MtCisaAntiSpamAllowList.Tests.ps1 | 10 ++++ .../Test-MtCisaAntiSpamSafeList.Tests.ps1 | 10 ++++ ...est-MtCisaAutoExternalForwarding.Tests.ps1 | 13 ++-- .../Test-MtCisaCalendarSharing.Tests.ps1 | 10 ++++ .../Test-MtCisaContactSharing.Tests.ps1 | 10 ++++ ...Test-MtCisaExternalSenderWarning.Tests.ps1 | 10 ++++ .../Test-MtCisaMailboxAuditing.Tests.ps1 | 10 ++++ .../Test-MtCisaSmtpAuthentication.Tests.ps1 | 10 ++++ 20 files changed, 480 insertions(+), 10 deletions(-) create mode 100644 powershell/internal/Get-MtSkippedReason.ps1 create mode 100644 powershell/public/CISA/exchange/Test-MtCisaAntiSpamAllowList.ps1 create mode 100644 powershell/public/CISA/exchange/Test-MtCisaAntiSpamSafeList.ps1 create mode 100644 powershell/public/CISA/exchange/Test-MtCisaCalendarSharing.ps1 create mode 100644 powershell/public/CISA/exchange/Test-MtCisaContactSharing.ps1 create mode 100644 powershell/public/CISA/exchange/Test-MtCisaExternalSenderWarning.ps1 create mode 100644 powershell/public/CISA/exchange/Test-MtCisaMailboxAuditing.ps1 create mode 100644 powershell/public/CISA/exchange/Test-MtCisaSmtpAuthentication.ps1 create mode 100644 tests/CISA/exchange/Test-MtCisaAntiSpamAllowList.Tests.ps1 create mode 100644 tests/CISA/exchange/Test-MtCisaAntiSpamSafeList.Tests.ps1 create mode 100644 tests/CISA/exchange/Test-MtCisaCalendarSharing.Tests.ps1 create mode 100644 tests/CISA/exchange/Test-MtCisaContactSharing.Tests.ps1 create mode 100644 tests/CISA/exchange/Test-MtCisaExternalSenderWarning.Tests.ps1 create mode 100644 tests/CISA/exchange/Test-MtCisaMailboxAuditing.Tests.ps1 create mode 100644 tests/CISA/exchange/Test-MtCisaSmtpAuthentication.Tests.ps1 diff --git a/powershell/Maester.psd1 b/powershell/Maester.psd1 index 56aeba6c..31063232 100644 --- a/powershell/Maester.psd1 +++ b/powershell/Maester.psd1 @@ -109,7 +109,7 @@ FunctionsToExport = 'Add-MtTestResultDetail', 'Clear-MtGraphCache', 'Connect-Mae 'Test-MtCisaGuestUserAccess', 'Test-MtCisaGuestInvitation', 'Test-MtCisaAutoExternalForwarding', 'Test-MtConditionalAccessWhatIf', - 'Test-MtConnection', + 'Test-MtConnection', 'Test-MtConnectedTo', 'Test-MtEidscaAF01', 'Test-MtEidscaAF02', 'Test-MtEidscaAF03', 'Test-MtEidscaAF04', 'Test-MtEidscaAF05', 'Test-MtEidscaAF06', 'Test-MtEidscaAG01', diff --git a/powershell/internal/Get-MtSkippedReason.ps1 b/powershell/internal/Get-MtSkippedReason.ps1 new file mode 100644 index 00000000..e2220d3c --- /dev/null +++ b/powershell/internal/Get-MtSkippedReason.ps1 @@ -0,0 +1,21 @@ +<# +.SYNOPSIS + Returns the description for why a test was skipped. +#> +function Get-MtSkippedReason { + param( + # The reason for skipping + [string] $SkippedBecause + ) + + switch($SkippedBecause){ + "NotConnectedAzure" { "Not connected to Azure. See [Connecting to Azure](https://maester.dev/docs/installation#optional-modules-and-permissions)" ; break} + "NotConnectedExchange" { "Not connected to Exchange Online. See [Connecting to Exchange Online](https://maester.dev/docs/installation#optional-modules-and-permissions)"; break} + "NotLicensed" { "Not licensed for the required workload"; break} + "NotLicensedEntraIDP1" { "Not licensed for Entra ID P1"; break} + "NotLicensedEntraIDP2" { "Not licensed for Entra ID P2"; break} + "NotLicensedEntraIDGovernance" { "Not licensed for Entra ID Governance"; break} + "NotLicensedEntraWorkloadID" { "Not licensed for Entra Workload ID"; break} + default { $SkippedBecause; break} + } +} \ No newline at end of file diff --git a/powershell/public/Add-MtTestResultDetail.ps1 b/powershell/public/Add-MtTestResultDetail.ps1 index caed6030..d1746694 100644 --- a/powershell/public/Add-MtTestResultDetail.ps1 +++ b/powershell/public/Add-MtTestResultDetail.ps1 @@ -54,17 +54,29 @@ Function Add-MtTestResultDetail { # The type of graph object, this will be used to show the right deeplink to the test results report. [ValidateSet('AuthenticationMethod', 'AuthorizationPolicy', 'ConditionalAccess', 'ConsentPolicy', 'Devices', 'DiagnosticSettings', 'Domains', 'Groups', 'IdentityProtection', 'Users', 'UserRole' - )] + )] [string] $GraphObjectType, # Pester test name # Use the test name from the Pester context by default [Parameter(Mandatory = $false)] - [string] $TestName = $____Pester.CurrentTest.ExpandedName + [string] $TestName = $____Pester.CurrentTest.ExpandedName, + + [ValidateSet('NotConnectedAzure', 'NotConnectedExchange', 'NotLicensed', 'NotLicensedEntraIDP1', + 'NotLicensedEntraIDP2', 'NotLicensedEntraIDGovernance', 'NotLicensedEntraWorkloadID' + )] + [string] $SkippedBecause ) $hasGraphResults = $GraphObjects -and $GraphObjectType + if ($SkippedBecause) { + $SkippedReason = Get-MtSkippedReason $SkippedBecause + if([string]::IsNullOrEmpty($Result)){ + $Result = "Skipped. $SkippedReason" + } + } + if ([string]::IsNullOrEmpty($Description)) { # Check if a markdown file exists for the cmdlet and parse the content $cmdletPath = $MyInvocation.PSCommandPath @@ -94,6 +106,8 @@ Function Add-MtTestResultDetail { $testInfo = @{ TestDescription = $Description TestResult = $Result + TestSkipped = $SkippedBecause + SkippedReason = $SkippedReason } Write-MtProgress -Activity "Running tests" -Status $testName @@ -107,4 +121,8 @@ Function Add-MtTestResultDetail { $__MtSession.TestResultDetail[$testName] = $testInfo } } + + if ($SkippedBecause) { #This needs to be set at the end. + Set-ItResult -Skipped -Because $SkippedReason + } } \ No newline at end of file diff --git a/powershell/public/CISA/exchange/Test-MtCisaAntiSpamAllowList.ps1 b/powershell/public/CISA/exchange/Test-MtCisaAntiSpamAllowList.ps1 new file mode 100644 index 00000000..c110483a --- /dev/null +++ b/powershell/public/CISA/exchange/Test-MtCisaAntiSpamAllowList.ps1 @@ -0,0 +1,52 @@ +<# +.SYNOPSIS + Checks state of anti-spam policies + +.DESCRIPTION + + IP allow lists SHOULD NOT be created. + +.EXAMPLE + Test-MtCisaAntiSpamAllowList + + Returns true if no allowed IPs in anti-spam policy +#> + +Function Test-MtCisaAntiSpamAllowList { + [CmdletBinding()] + [OutputType([bool])] + param() + + if(!(Test-MtConnection ExchangeOnline)){ + Add-MtTestResultDetail -SkippedBecause NotConnectedExchange + return $null + } + + $policy = Get-HostedConnectionFilterPolicy + + $resultPolicy = $policy | Where-Object {` + ($_.IPAllowList | Measure-Object).Count -gt 0 + } + + $testResult = ($resultPolicy | Measure-Object).Count -eq 0 + + $portalLink = "https://security.microsoft.com/antispam" + + if ($testResult) { + $testResultMarkdown = "Well done. Your tenant does not have any [Anti-spam IP allow lists]($portalLink)." + } else { + $testResultMarkdown = "Your tenant has [Anti-spam IP allow lists]($portalLink).`n`n%TestResult%" + $resultPolicy | ForEach-Object { + $result = "* $($_.Name)`n" + $_.IPAllowList | ForEach-Object {` + $result += " * $_`n" + } + } + } + + $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result + + Add-MtTestResultDetail -Result $testResultMarkdown + + return $testResult +} \ No newline at end of file diff --git a/powershell/public/CISA/exchange/Test-MtCisaAntiSpamSafeList.ps1 b/powershell/public/CISA/exchange/Test-MtCisaAntiSpamSafeList.ps1 new file mode 100644 index 00000000..fffc3e1d --- /dev/null +++ b/powershell/public/CISA/exchange/Test-MtCisaAntiSpamSafeList.ps1 @@ -0,0 +1,46 @@ +<# +.SYNOPSIS + Checks state of anti-spam policies + +.DESCRIPTION + + Safe lists SHOULD NOT be enabled. + +.EXAMPLE + Test-MtCisaAntiSpamSafeList + + Returns true if Safe List is disabled in anti-spam policy +#> + +Function Test-MtCisaAntiSpamSafeList { + [CmdletBinding()] + [OutputType([bool])] + param() + + if(!(Test-MtConnection ExchangeOnline)){ + Add-MtTestResultDetail -SkippedBecause NotConnectedExchange + return $null + } + + $policy = Get-HostedConnectionFilterPolicy + + $resultPolicy = $policy | Where-Object {` + -not $_.EnableSafeList + } + + $testResult = ($resultPolicy|Measure-Object).Count -eq 1 + + $portalLink = "https://security.microsoft.com/antispam" + + if ($testResult) { + $testResultMarkdown = "Well done. [Safe List]($portalLink) is disabled in your tenant." + } else { + $testResultMarkdown = "[Safe List]($portalLink) is enabled in your tenant." + } + + $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result + + Add-MtTestResultDetail -Result $testResultMarkdown + + return $testResult +} \ No newline at end of file diff --git a/powershell/public/CISA/exchange/Test-MtCisaAutoExternalForwarding.ps1 b/powershell/public/CISA/exchange/Test-MtCisaAutoExternalForwarding.ps1 index a4e26f4c..74fe6c5f 100644 --- a/powershell/public/CISA/exchange/Test-MtCisaAutoExternalForwarding.ps1 +++ b/powershell/public/CISA/exchange/Test-MtCisaAutoExternalForwarding.ps1 @@ -17,6 +17,11 @@ Function Test-MtCisaAutoExternalForwarding { [OutputType([bool])] param() + if(!(Test-MtConnection ExchangeOnline)){ + Add-MtTestResultDetail -SkippedBecause NotConnectedExchange + return $null + } + $domains = Get-RemoteDomain $forwardingDomains = $domains | Where-Object { ` diff --git a/powershell/public/CISA/exchange/Test-MtCisaCalendarSharing.ps1 b/powershell/public/CISA/exchange/Test-MtCisaCalendarSharing.ps1 new file mode 100644 index 00000000..c79b7fbd --- /dev/null +++ b/powershell/public/CISA/exchange/Test-MtCisaCalendarSharing.ps1 @@ -0,0 +1,56 @@ +<# +.SYNOPSIS + Checks state of sharing policies + +.DESCRIPTION + + Calendar details SHALL NOT be shared with all domains. + +.EXAMPLE + Test-MtCisaCalendarSharing + + Returns true if no sharing policies allow uncontrolled calendar sharing. +#> + +Function Test-MtCisaCalendarSharing { + [CmdletBinding()] + [OutputType([bool])] + param() + + if(!(Test-MtConnection ExchangeOnline)){ + Add-MtTestResultDetail -SkippedBecause NotConnectedExchange + return $null + } + + $policies = Get-SharingPolicy + + $resultPolicies = $policies | Where-Object {` + $_.Enabled -and ` + ($_.Domains -like "`*:*CalendarSharing*" -or ` + $_.Domains -like "Anonymous:*CalendarSharing*") + } + + $testResult = ($resultPolicies|Measure-Object).Count -eq 0 + + if ($testResult) { + $testResultMarkdown = "Well done. Your tenant does not allow uncontrolled calendar sharing.`n`n%TestResult%" + } else { + $testResultMarkdown = "Your tenant allows uncontrolled calendar sharing.`n`n%TestResult%" + } + + $result = "| Policy Name | Test Result |`n" + $result += "| --- | --- |`n" + foreach ($item in $policies | Sort-Object -Property Name) { + $portalLink = "https://admin.exchange.microsoft.com/#/individualsharing/:/individualsharingdetails/$($item.ExchangeObjectId)/managedomain" + $itemResult = "✅ Pass" + if ($item.ExchangeObjectId -in $resultPolicies.ExchangeObjectId) { + $itemResult = "❌ Fail" + } + $result += "| [$($item.Name)]($portalLink) | $($itemResult) |`n" + } + $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result + + Add-MtTestResultDetail -Result $testResultMarkdown + + return $testResult +} \ No newline at end of file diff --git a/powershell/public/CISA/exchange/Test-MtCisaContactSharing.ps1 b/powershell/public/CISA/exchange/Test-MtCisaContactSharing.ps1 new file mode 100644 index 00000000..3d370ef5 --- /dev/null +++ b/powershell/public/CISA/exchange/Test-MtCisaContactSharing.ps1 @@ -0,0 +1,56 @@ +<# +.SYNOPSIS + Checks state of sharing policies + +.DESCRIPTION + + Contact folders SHALL NOT be shared with all domains. + +.EXAMPLE + Test-MtCisaContactSharing + + Returns true if no sharing policies allow uncontrolled contact sharing. +#> + +Function Test-MtCisaContactSharing { + [CmdletBinding()] + [OutputType([bool])] + param() + + if(!(Test-MtConnection ExchangeOnline)){ + Add-MtTestResultDetail -SkippedBecause NotConnectedExchange + return $null + } + + $policies = Get-SharingPolicy + + $resultPolicies = $policies | Where-Object {` + $_.Enabled -and ` + ($_.Domains -like "`*:*ContactsSharing*" -or ` + $_.Domains -like "Anonymous:*ContactsSharing*") + } + + $testResult = ($resultPolicies|Measure-Object).Count -eq 0 + + if ($testResult) { + $testResultMarkdown = "Well done. Your tenant does not allow uncontrolled contact sharing.`n`n%TestResult%" + } else { + $testResultMarkdown = "Your tenant allows uncontrolled contact sharing.`n`n%TestResult%" + } + + $result = "| Policy Name | Test Result |`n" + $result += "| --- | --- |`n" + foreach ($item in $policies | Sort-Object -Property Name) { + $portalLink = "https://admin.exchange.microsoft.com/#/individualsharing/:/individualsharingdetails/$($item.ExchangeObjectId)/managedomain" + $itemResult = "✅ Pass" + if ($item.ExchangeObjectId -in $resultPolicies.ExchangeObjectId) { + $itemResult = "❌ Fail" + } + $result += "| [$($item.Name)]($portalLink) | $($itemResult) |`n" + } + $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result + + Add-MtTestResultDetail -Result $testResultMarkdown + + return $testResult +} \ No newline at end of file diff --git a/powershell/public/CISA/exchange/Test-MtCisaExternalSenderWarning.ps1 b/powershell/public/CISA/exchange/Test-MtCisaExternalSenderWarning.ps1 new file mode 100644 index 00000000..8ad283c1 --- /dev/null +++ b/powershell/public/CISA/exchange/Test-MtCisaExternalSenderWarning.ps1 @@ -0,0 +1,60 @@ +<# +.SYNOPSIS + Checks state of transport policies + +.DESCRIPTION + + External sender warnings SHALL be implemented. + +.EXAMPLE + Test-MtCisaExternalSenderWarning + + Returns true if a transport policy appends a warning. +#> + +Function Test-MtCisaExternalSenderWarning { + [CmdletBinding()] + [OutputType([bool])] + param() + + if(!(Test-MtConnection ExchangeOnline)){ + Add-MtTestResultDetail -SkippedBecause NotConnectedExchange + return $null + } + + $rules = Get-TransportRule + + $resultRules = $rules | Where-Object {` + $_.State -eq "Enabled" -and ` + $_.Mode -eq "Enforce" -and ` + $_.FromScope -eq "NotInOrganization" -and ` + $_.SenderAddressLocation -eq "Header" -and ` + $_.PrependSubject -like "*[External]*" + } + + $testResult = ($resultRules | Measure-Object).Count -ge 1 + + if ($testResult) { + $testResultMarkdown = "Well done. Your tenant has an external sender warning.`n`n%TestResult%" + } else { + $testResultMarkdown = "Your tenant does not have an external sender warning.`n`n%TestResult%" + } + + if ($rules) { # Only show table if there are rules + $result = "| Policy Name | Test Result |`n" + $result += "| --- | --- |`n" + foreach ($item in $rules | Sort-Object -Property Name) { + $portalLink = "https://admin.exchange.microsoft.com/#/transportrules/:/ruleDetails/$($item.Guid)/viewinflyoutpanel" + $itemResult = "❌ Fail" + if ($resultRules.Guid -contains $item.Guid) { + $itemResult = "✅ Pass" + } + $result += "| [$($item.Name)]($portalLink) | $($itemResult) |`n" + } + } + $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result + + Add-MtTestResultDetail -Result $testResultMarkdown + + return $testResult +} \ No newline at end of file diff --git a/powershell/public/CISA/exchange/Test-MtCisaMailboxAuditing.ps1 b/powershell/public/CISA/exchange/Test-MtCisaMailboxAuditing.ps1 new file mode 100644 index 00000000..e0cb4dbd --- /dev/null +++ b/powershell/public/CISA/exchange/Test-MtCisaMailboxAuditing.ps1 @@ -0,0 +1,42 @@ +<# +.SYNOPSIS + Checks state of mailbox auditing + +.DESCRIPTION + + Mailbox auditing SHALL be enabled. + +.EXAMPLE + Test-MtCisaMailboxAuditing + + Returns true if mailbox auditing is enabled. +#> + +Function Test-MtCisaMailboxAuditing { + [CmdletBinding()] + [OutputType([bool])] + param() + + if(!(Test-MtConnection ExchangeOnline)){ + Add-MtTestResultDetail -SkippedBecause NotConnectedExchange + return $null + } + + $config = Get-OrganizationConfig + + $testResult = (-not $config.AuditDisabled) + + if ($testResult) { + $testResultMarkdown = "Well done. Your tenant has mailbox auditing enabled.`n`n%TestResult%" + $result = "✅ Pass" + } else { + $testResultMarkdown = "Your tenant does not have mailbox auditing enabled.`n`n%TestResult%" + $result = "❌ Fail" + } + + $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result + + Add-MtTestResultDetail -Result $testResultMarkdown + + return $testResult +} \ No newline at end of file diff --git a/powershell/public/CISA/exchange/Test-MtCisaSmtpAuthentication.ps1 b/powershell/public/CISA/exchange/Test-MtCisaSmtpAuthentication.ps1 new file mode 100644 index 00000000..19ed98ec --- /dev/null +++ b/powershell/public/CISA/exchange/Test-MtCisaSmtpAuthentication.ps1 @@ -0,0 +1,38 @@ +<# +.SYNOPSIS + Checks state of SMTP authentication in Exchange Online. + +.DESCRIPTION + SMTP authentication SHALL be disabled. + +.EXAMPLE + Test-MtCisaSmtpAuthentication + + Returns true if SMTP authentication is disabled in Exchange Online. +#> + +Function Test-MtCisaSmtpAuthentication { + [CmdletBinding()] + [OutputType([bool])] + param() + + if(!(Test-MtConnection ExchangeOnline)){ + Add-MtTestResultDetail -SkippedBecause NotConnectedExchange + return $null + } + + $config = Get-TransportConfig + + $testResult = $config.SmtpClientAuthenticationDisabled + + $portalLink = "https://admin.exchange.microsoft.com/#/settings" + if ($testResult) { + $testResultMarkdown = "Well done. Your tenant has [SMTP Authentication]($portalLink) disabled." + } else { + $testResultMarkdown = "Your tenant has [SMTP Authentication]($portalLink) enabled." + } + + Add-MtTestResultDetail -Result $testResultMarkdown + + return $testResult +} \ No newline at end of file diff --git a/tests/CISA/@template.Testsps1.txt b/tests/CISA/@template.Testsps1.txt index fedc6f3b..7e0146c7 100644 --- a/tests/CISA/@template.Testsps1.txt +++ b/tests/CISA/@template.Testsps1.txt @@ -1,5 +1,10 @@ Describe "CISA SCuBA" -Tag "MS.***", "MS.***.#.#", "CISA", "Security", "All" { It "MS.***.#.#: Control Should/Must description." { - FunctionName | Should -Be $true -Because "..." + + $shortFunctionName = FunctionName + + if ($null -ne $shortFunctionName) { + $shortFunctionName | Should -Be $true -Because "..." + } } } \ No newline at end of file diff --git a/tests/CISA/exchange/Test-MtCisaAntiSpamAllowList.Tests.ps1 b/tests/CISA/exchange/Test-MtCisaAntiSpamAllowList.Tests.ps1 new file mode 100644 index 00000000..483619cd --- /dev/null +++ b/tests/CISA/exchange/Test-MtCisaAntiSpamAllowList.Tests.ps1 @@ -0,0 +1,10 @@ +Describe "CISA SCuBA" -Tag "MS.EXO", "MS.EXO.12.1", "CISA", "Security", "All" { + It "MS.EXO.12.1: IP allow lists SHOULD NOT be created." { + + $cisaAntiSpamAllowList = Test-MtCisaAntiSpamAllowList + + if ($null -ne $cisaAntiSpamAllowList) { + $cisaAntiSpamAllowList | Should -Be $true -Because "no anti-spam policy allow IPs." + } + } +} \ No newline at end of file diff --git a/tests/CISA/exchange/Test-MtCisaAntiSpamSafeList.Tests.ps1 b/tests/CISA/exchange/Test-MtCisaAntiSpamSafeList.Tests.ps1 new file mode 100644 index 00000000..e9209754 --- /dev/null +++ b/tests/CISA/exchange/Test-MtCisaAntiSpamSafeList.Tests.ps1 @@ -0,0 +1,10 @@ +Describe "CISA SCuBA" -Tag "MS.EXO", "MS.EXO.12.2", "CISA", "Security", "All" { + It "MS.EXO.12.2: Safe lists SHOULD NOT be enabled." { + + $cisaAntiSpamSafeList = Test-MtCisaAntiSpamSafeList + + if($null -eq $cisaAntiSpamSafeList) { + $cisaAntiSpamSafeList | Should -Be $true -Because "Safe Lists should be disabled." + } + } +} \ No newline at end of file diff --git a/tests/CISA/exchange/Test-MtCisaAutoExternalForwarding.Tests.ps1 b/tests/CISA/exchange/Test-MtCisaAutoExternalForwarding.Tests.ps1 index 6113ad89..3ef69a3f 100644 --- a/tests/CISA/exchange/Test-MtCisaAutoExternalForwarding.Tests.ps1 +++ b/tests/CISA/exchange/Test-MtCisaAutoExternalForwarding.Tests.ps1 @@ -1,9 +1,10 @@ -BeforeDiscovery { - $exoSession = Test-MtConnection -Service ExchageOnline -} - -Describe "CISA SCuBA" -Tag "MS.EXO", "MS.EXO.1.1", "CISA", "Security", "All" -Skip:((-not $exoSession)) { +Describe "CISA SCuBA" -Tag "MS.EXO", "MS.EXO.1.1", "CISA", "Security", "All" { It "MS.EXO.1.1: Automatic forwarding to external domains SHALL be disabled." { - Test-MtCisaAutoExternalForwarding | Should -Be $true -Because "auto forwarding is not enabled for any domains" + + $cisaAutoExternalForwarding = Test-MtCisaAutoExternalForwarding + + if($null -ne $cisaAutoExternalForwarding) { + $cisaAutoExternalForwarding | Should -Be $true -Because "auto forwarding is not enabled for any domains" + } } } \ No newline at end of file diff --git a/tests/CISA/exchange/Test-MtCisaCalendarSharing.Tests.ps1 b/tests/CISA/exchange/Test-MtCisaCalendarSharing.Tests.ps1 new file mode 100644 index 00000000..67f98148 --- /dev/null +++ b/tests/CISA/exchange/Test-MtCisaCalendarSharing.Tests.ps1 @@ -0,0 +1,10 @@ +Describe "CISA SCuBA" -Tag "MS.EXO", "MS.EXO.6.2", "CISA", "Security", "All" { + It "MS.EXO.6.2: Calendar details SHALL NOT be shared with all domains." { + + $cisaCalendarSharing = Test-MtCisaCalendarSharing + + if($null -eq $cisaCalendarSharing) { + $cisaCalendarSharing | Should -Be $true -Because "calendar sharing is disabled." + } + } +} \ No newline at end of file diff --git a/tests/CISA/exchange/Test-MtCisaContactSharing.Tests.ps1 b/tests/CISA/exchange/Test-MtCisaContactSharing.Tests.ps1 new file mode 100644 index 00000000..27e9c234 --- /dev/null +++ b/tests/CISA/exchange/Test-MtCisaContactSharing.Tests.ps1 @@ -0,0 +1,10 @@ +Describe "CISA SCuBA" -Tag "MS.EXO", "MS.EXO.6.1", "CISA", "Security", "All" { + It "MS.EXO.6.1: Contact folders SHALL NOT be shared with all domains." { + + $cisaContactSharing = Test-MtCisaContactSharing + + if($null -eq $cisaContactSharing) { + $cisaContactSharing | Should -Be $true -Because "contact sharing is disabled." + } + } +} \ No newline at end of file diff --git a/tests/CISA/exchange/Test-MtCisaExternalSenderWarning.Tests.ps1 b/tests/CISA/exchange/Test-MtCisaExternalSenderWarning.Tests.ps1 new file mode 100644 index 00000000..02d23a1c --- /dev/null +++ b/tests/CISA/exchange/Test-MtCisaExternalSenderWarning.Tests.ps1 @@ -0,0 +1,10 @@ +Describe "CISA SCuBA" -Tag "MS.EXO", "MS.EXO.7.1", "CISA", "Security", "All" { + It "MS.EXO.7.1: External sender warnings SHALL be implemented." { + + $cisaExternalSenderWarning = Test-MtCisaExternalSenderWarning + + if ($null -ne $cisaExternalSenderWarning) { + $cisaExternalSenderWarning | Should -Be $true -Because "external sender warning is set." + } + } +} \ No newline at end of file diff --git a/tests/CISA/exchange/Test-MtCisaMailboxAuditing.Tests.ps1 b/tests/CISA/exchange/Test-MtCisaMailboxAuditing.Tests.ps1 new file mode 100644 index 00000000..d8077e32 --- /dev/null +++ b/tests/CISA/exchange/Test-MtCisaMailboxAuditing.Tests.ps1 @@ -0,0 +1,10 @@ +Describe "CISA SCuBA" -Tag "MS.EXO", "MS.EXO.13.1", "CISA", "Security", "All" { + It "MS.EXO.13.1: Mailbox auditing SHALL be enabled." { + + $cisaMailboxAuditing = Test-MtCisaMailboxAuditing + + if($null -ne $cisaMailboxAuditing) { + $cisaMailboxAuditing | Should -Be $true -Because "mailbox auditing is enabled." + } + } +} \ No newline at end of file diff --git a/tests/CISA/exchange/Test-MtCisaSmtpAuthentication.Tests.ps1 b/tests/CISA/exchange/Test-MtCisaSmtpAuthentication.Tests.ps1 new file mode 100644 index 00000000..8260af8a --- /dev/null +++ b/tests/CISA/exchange/Test-MtCisaSmtpAuthentication.Tests.ps1 @@ -0,0 +1,10 @@ +Describe "CISA SCuBA" -Tag "MS.EXO", "MS.EXO.5.1", "CISA", "Security", "All" { + It "MS.EXO.5.1: SMTP AUTH SHALL be disabled." { + + $cisaSmtpAuthentication = Test-MtCisaSmtpAuthentication + + if ($null -ne $cisaSmtpAuthentication) { + $cisaSmtpAuthentication | Should -Be $true -Because "SMTP Authentication is disabled." + } + } +} \ No newline at end of file From fff33bbf6b8ebf3253165961d8496233abfd4d7a Mon Sep 17 00:00:00 2001 From: Merill Fernando Date: Sun, 23 Jun 2024 19:39:50 +1000 Subject: [PATCH 2/9] Moved license check to Add-MtTestResult --- powershell/internal/Get-MtSkippedReason.ps1 | 9 ++- powershell/public/Add-MtTestResultDetail.ps1 | 14 +++-- .../Test-MtAppManagementPolicyEnabled.md | 62 +++++++++++++++++++ .../Test-MtAppManagementPolicyEnabled.ps1 | 19 +++++- .../Test-AppManagementPolicies.Tests.ps1 | 12 ++-- 5 files changed, 99 insertions(+), 17 deletions(-) create mode 100644 powershell/public/Test-MtAppManagementPolicyEnabled.md diff --git a/powershell/internal/Get-MtSkippedReason.ps1 b/powershell/internal/Get-MtSkippedReason.ps1 index e2220d3c..aac56632 100644 --- a/powershell/internal/Get-MtSkippedReason.ps1 +++ b/powershell/internal/Get-MtSkippedReason.ps1 @@ -11,11 +11,10 @@ function Get-MtSkippedReason { switch($SkippedBecause){ "NotConnectedAzure" { "Not connected to Azure. See [Connecting to Azure](https://maester.dev/docs/installation#optional-modules-and-permissions)" ; break} "NotConnectedExchange" { "Not connected to Exchange Online. See [Connecting to Exchange Online](https://maester.dev/docs/installation#optional-modules-and-permissions)"; break} - "NotLicensed" { "Not licensed for the required workload"; break} - "NotLicensedEntraIDP1" { "Not licensed for Entra ID P1"; break} - "NotLicensedEntraIDP2" { "Not licensed for Entra ID P2"; break} - "NotLicensedEntraIDGovernance" { "Not licensed for Entra ID Governance"; break} - "NotLicensedEntraWorkloadID" { "Not licensed for Entra Workload ID"; break} + "NotLicensedEntraIDP1" { "This test is for tenants that are licensed for Entra ID P1. See [Entra ID licensing](https://learn.microsoft.com/entra/fundamentals/licensing)"; break} + "NotLicensedEntraIDP2" { "This test is for tenants that are licensed for Entra ID P2. See [Entra ID licensing](https://learn.microsoft.com/entra/fundamentals/licensing)"; break} + "NotLicensedEntraIDGovernance" { "This test is for tenants that are licensed for Entra ID Governance. See [Entra ID Governance licensing](https://learn.microsoft.com/entra/fundamentals/licensing#microsoft-entra-id-governance)"; break} + "NotLicensedEntraWorkloadID" { "This test is for tenants that are licensed for Entra Workload ID. See [Entra Workload ID licensing](https://learn.microsoft.com/entra/workload-id/workload-identities-faqs)"; break} default { $SkippedBecause; break} } } \ No newline at end of file diff --git a/powershell/public/Add-MtTestResultDetail.ps1 b/powershell/public/Add-MtTestResultDetail.ps1 index d1746694..15a3cbb3 100644 --- a/powershell/public/Add-MtTestResultDetail.ps1 +++ b/powershell/public/Add-MtTestResultDetail.ps1 @@ -62,7 +62,7 @@ Function Add-MtTestResultDetail { [Parameter(Mandatory = $false)] [string] $TestName = $____Pester.CurrentTest.ExpandedName, - [ValidateSet('NotConnectedAzure', 'NotConnectedExchange', 'NotLicensed', 'NotLicensedEntraIDP1', + [ValidateSet('NotConnectedAzure', 'NotConnectedExchange', 'NotLicensedEntraIDP1', 'NotLicensedEntraIDP2', 'NotLicensedEntraIDGovernance', 'NotLicensedEntraWorkloadID' )] [string] $SkippedBecause @@ -72,7 +72,8 @@ Function Add-MtTestResultDetail { if ($SkippedBecause) { $SkippedReason = Get-MtSkippedReason $SkippedBecause - if([string]::IsNullOrEmpty($Result)){ + + if ([string]::IsNullOrEmpty($Result)) { $Result = "Skipped. $SkippedReason" } } @@ -90,7 +91,11 @@ Function Add-MtTestResultDetail { if (![string]::IsNullOrEmpty($Result)) { # If a result was provided in the parameter insert it into the markdown content - $mdResult = $mdResult -replace "%TestResult%", $Result + if ($mdResult -match "%TestResult%") { + $mdResult = $mdResult -replace "%TestResult%", $Result + } else { + $mdResult = $Result + } } $Description = $mdDescription @@ -122,7 +127,8 @@ Function Add-MtTestResultDetail { } } - if ($SkippedBecause) { #This needs to be set at the end. + if ($SkippedBecause) { + #This needs to be set at the end. Set-ItResult -Skipped -Because $SkippedReason } } \ No newline at end of file diff --git a/powershell/public/Test-MtAppManagementPolicyEnabled.md b/powershell/public/Test-MtAppManagementPolicyEnabled.md new file mode 100644 index 00000000..211ca243 --- /dev/null +++ b/powershell/public/Test-MtAppManagementPolicyEnabled.md @@ -0,0 +1,62 @@ +By default Microsoft Entra ID allows service principals and applications to be configured with weak credentials. + +This can include + +- client secrets instead of certificates +- secrets and certificates with long expiry (e.g. 10 year) + +## How to fix + +Using shorter expiry periods and certificates instead of secrets can help reduce the risk of credentials being compromised and used by an attacker. + +The sample policy below can be used to enforce credential configurations on apps and service principals. + +```powershell +Import-Module Microsoft.Graph.Identity.SignIns + +$params = @{ +isEnabled = $true +applicationRestrictions = @{ + passwordCredentials = @( + @{ + restrictionType = "passwordAddition" + maxLifetime = $null + restrictForAppsCreatedAfterDateTime = [System.DateTime]::Parse("2021-01-01T10:37:00Z") + } + @{ + restrictionType = "passwordLifetime" + maxLifetime = "P365D" + restrictForAppsCreatedAfterDateTime = [System.DateTime]::Parse("2017-01-01T10:37:00Z") + } + @{ + restrictionType = "symmetricKeyAddition" + maxLifetime = $null + restrictForAppsCreatedAfterDateTime = [System.DateTime]::Parse("2021-01-01T10:37:00Z") + } + @{ + restrictionType = "customPasswordAddition" + maxLifetime = $null + restrictForAppsCreatedAfterDateTime = [System.DateTime]::Parse("2015-01-01T10:37:00Z") + } + @{ + restrictionType = "symmetricKeyLifetime" + maxLifetime = "P365D" + restrictForAppsCreatedAfterDateTime = [System.DateTime]::Parse("2015-01-01T10:37:00Z") + } + ) + keyCredentials = @( + @{ + restrictionType = "asymmetricKeyLifetime" + maxLifetime = "P365D" + restrictForAppsCreatedAfterDateTime = [System.DateTime]::Parse("2015-01-01T10:37:00Z") + } + ) +} +} + +Update-MgPolicyDefaultAppManagementPolicy -BodyParameter $params +``` + +## Learn more + +- [Tenant App Management Policy - Microsoft Graph Reference](https://learn.microsoft.com/graph/api/resources/tenantappmanagementpolicy?view=graph-rest-1.0) diff --git a/powershell/public/Test-MtAppManagementPolicyEnabled.ps1 b/powershell/public/Test-MtAppManagementPolicyEnabled.ps1 index f7f8bda5..7c177ea1 100644 --- a/powershell/public/Test-MtAppManagementPolicyEnabled.ps1 +++ b/powershell/public/Test-MtAppManagementPolicyEnabled.ps1 @@ -14,8 +14,21 @@ Function Test-MtAppManagementPolicyEnabled { [OutputType([bool])] param() - $result = Invoke-MtGraphRequest -RelativeUri "policies/defaultAppManagementPolicy" - Write-Verbose -Message "Default App Management Policy: $($result.isEnabled)" - return $result.isEnabled -eq 'True' + if (!(Get-MtLicenseInformation EntraWorkloadID)) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraWorkloadID + return $null + } + $defaultAppManagementPolicy = Invoke-MtGraphRequest -RelativeUri "policies/defaultAppManagementPolicy" + $result = $defaultAppManagementPolicy.isEnabled -eq 'True' + + if ($result) { + $resultMarkdown = "Well done. Your tenant has an app management policy enabled." + } else { + $resultMarkdown = "Your tenant does not have an app management policy defined." + } + + Add-MtTestResultDetail -Result $resultMarkdown + + return $result } \ No newline at end of file diff --git a/tests/Maester/Entra/Test-AppManagementPolicies.Tests.ps1 b/tests/Maester/Entra/Test-AppManagementPolicies.Tests.ps1 index 4295c108..8e05a808 100644 --- a/tests/Maester/Entra/Test-AppManagementPolicies.Tests.ps1 +++ b/tests/Maester/Entra/Test-AppManagementPolicies.Tests.ps1 @@ -1,8 +1,10 @@ -BeforeDiscovery { - $EntraWorkloadID = Get-MtLicenseInformation -Product EntraWorkloadID -} Describe "App Management Policies" -Tag "App", "Security", "All" { - It "MT.1002: App management restrictions on applications and service principals is configured and enabled. See https://maester.dev/docs/tests/MT.1002" -Tag "L-WI", "MT.1002" -Skip:( !$EntraWorkloadID ) { - Test-MtAppManagementPolicyEnabled | Should -Be $true -Because "an app policy for workload identities should be defined to enforce strong credentials instead of passwords and a maximum expiry period (e.g. credential should be renewed every six months)" + It "MT.1002: App management restrictions on applications and service principals is configured and enabled. See https://maester.dev/docs/tests/MT.1002" -Tag "MT.1002" { + + $appManagementPolicyEnabled = Test-MtAppManagementPolicyEnabled + + if ($null -ne $appManagementPolicyEnabled) { + $appManagementPolicyEnabled | Should -Be $true -Because "an app policy for workload identities should be defined to enforce strong credentials instead of passwords and a maximum expiry period (e.g. credential should be renewed every six months)" + } } } \ No newline at end of file From 98fc487783130853cfe9f0e1a3b43ccb5599a7ed Mon Sep 17 00:00:00 2001 From: Merill Fernando Date: Sun, 23 Jun 2024 19:43:16 +1000 Subject: [PATCH 3/9] Fixed failing pester test --- powershell/Maester.psd1 | 2 +- powershell/public/Test-MtAppManagementPolicyEnabled.ps1 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/powershell/Maester.psd1 b/powershell/Maester.psd1 index d0bb2137..6e452c00 100644 --- a/powershell/Maester.psd1 +++ b/powershell/Maester.psd1 @@ -112,7 +112,7 @@ FunctionsToExport = 'Add-MtTestResultDetail', 'Clear-MtGraphCache', 'Connect-Mae 'Test-MtCisaExternalSenderWarning', 'Test-MtCisaAntiSpamAllowList', 'Test-MtCisaAntiSpamSafeList', 'Test-MtCisaMailboxAuditing', 'Test-MtConditionalAccessWhatIf', - 'Test-MtConnection', 'Test-MtConnectedTo', + 'Test-MtConnection', 'Test-MtEidscaAF01', 'Test-MtEidscaAF02', 'Test-MtEidscaAF03', 'Test-MtEidscaAF04', 'Test-MtEidscaAF05', 'Test-MtEidscaAF06', 'Test-MtEidscaAG01', diff --git a/powershell/public/Test-MtAppManagementPolicyEnabled.ps1 b/powershell/public/Test-MtAppManagementPolicyEnabled.ps1 index 7c177ea1..67aef82a 100644 --- a/powershell/public/Test-MtAppManagementPolicyEnabled.ps1 +++ b/powershell/public/Test-MtAppManagementPolicyEnabled.ps1 @@ -20,6 +20,7 @@ Function Test-MtAppManagementPolicyEnabled { } $defaultAppManagementPolicy = Invoke-MtGraphRequest -RelativeUri "policies/defaultAppManagementPolicy" + Write-Verbose -Message "Default App Management Policy: $($result.isEnabled)" $result = $defaultAppManagementPolicy.isEnabled -eq 'True' if ($result) { From 53a04a380c41b928286d7608371ea08d2acb82ad Mon Sep 17 00:00:00 2001 From: Fabian Bader Date: Mon, 24 Jun 2024 08:41:16 +0200 Subject: [PATCH 4/9] chore: Add support for "SecurityDefaultDisabled" skip reason in Add-MtTestResultDetail.ps1 --- powershell/public/Add-MtTestResultDetail.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powershell/public/Add-MtTestResultDetail.ps1 b/powershell/public/Add-MtTestResultDetail.ps1 index 15a3cbb3..7d5b389a 100644 --- a/powershell/public/Add-MtTestResultDetail.ps1 +++ b/powershell/public/Add-MtTestResultDetail.ps1 @@ -63,7 +63,7 @@ Function Add-MtTestResultDetail { [string] $TestName = $____Pester.CurrentTest.ExpandedName, [ValidateSet('NotConnectedAzure', 'NotConnectedExchange', 'NotLicensedEntraIDP1', - 'NotLicensedEntraIDP2', 'NotLicensedEntraIDGovernance', 'NotLicensedEntraWorkloadID' + 'NotLicensedEntraIDP2', 'NotLicensedEntraIDGovernance', 'NotLicensedEntraWorkloadID', "SecurityDefaultDisabled" )] [string] $SkippedBecause ) From 1e707141d37d689c31e2d865dba16f357aec90d4 Mon Sep 17 00:00:00 2001 From: Fabian Bader Date: Mon, 24 Jun 2024 08:42:22 +0200 Subject: [PATCH 5/9] feat: Add support for "SecurityDefaultDisabled" skip reason in Get-MtSkippedReason.ps1 --- powershell/internal/Get-MtSkippedReason.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/powershell/internal/Get-MtSkippedReason.ps1 b/powershell/internal/Get-MtSkippedReason.ps1 index aac56632..4f3fc1b3 100644 --- a/powershell/internal/Get-MtSkippedReason.ps1 +++ b/powershell/internal/Get-MtSkippedReason.ps1 @@ -15,6 +15,7 @@ function Get-MtSkippedReason { "NotLicensedEntraIDP2" { "This test is for tenants that are licensed for Entra ID P2. See [Entra ID licensing](https://learn.microsoft.com/entra/fundamentals/licensing)"; break} "NotLicensedEntraIDGovernance" { "This test is for tenants that are licensed for Entra ID Governance. See [Entra ID Governance licensing](https://learn.microsoft.com/entra/fundamentals/licensing#microsoft-entra-id-governance)"; break} "NotLicensedEntraWorkloadID" { "This test is for tenants that are licensed for Entra Workload ID. See [Entra Workload ID licensing](https://learn.microsoft.com/entra/workload-id/workload-identities-faqs)"; break} + "SecurityDefaultDisabled" { "Security defaults are disabled. See [Security defaults](https://learn.microsoft.com/en-us/entra/fundamentals/security-defaults)"; break} default { $SkippedBecause; break} } } \ No newline at end of file From 414898b8cc8cc8a51d3ec125ea9d61f58161cbab Mon Sep 17 00:00:00 2001 From: Fabian Bader Date: Wed, 26 Jun 2024 13:20:26 +0200 Subject: [PATCH 6/9] chore: Update conditional access test scripts to skip when EntraID license is "Free" --- powershell/internal/Get-MtSkippedReason.ps1 | 2 +- powershell/public/Add-MtTestResultDetail.ps1 | 2 +- powershell/public/Test-MtCaAllAppsExists.ps1 | 5 ++ ...est-MtCaApplicationEnforcedRestriction.ps1 | 5 ++ ...LegacyExchangeActiveSyncAuthentication.ps1 | 5 ++ ...est-MtCaBlockLegacyOtherAuthentication.ps1 | 5 ++ ...lockUnknownOrUnsupportedDevicePlatform.ps1 | 5 ++ .../Test-MtCaDeviceComplianceAdminsExists.ps1 | 9 +++- .../Test-MtCaDeviceComplianceExists.ps1 | 5 ++ .../public/Test-MtCaEmergencyAccessExists.ps1 | 5 ++ ...MtCaEnforceNonPersistentBrowserSession.ps1 | 5 ++ .../Test-MtCaEnforceSignInFrequency.ps1 | 5 ++ ...t-MtCaExclusionForDirectorySyncAccount.ps1 | 5 ++ .../public/Test-MtCaLicenseUtilization.ps1 | 9 ++++ powershell/public/Test-MtCaMfaForAdmin.ps1 | 9 +++- .../public/Test-MtCaMfaForAdminManagement.ps1 | 5 ++ powershell/public/Test-MtCaMfaForAllUsers.ps1 | 5 ++ powershell/public/Test-MtCaMfaForGuest.ps1 | 5 ++ .../public/Test-MtCaMfaForRiskySignIn.ps1 | 5 ++ ...CaRequirePasswordChangeForHighUserRisk.ps1 | 5 ++ ...est-MtCaSecureSecurityInfoRegistration.ps1 | 5 ++ .../Test-MtCaWIFBlockLegacyAuthentication.ps1 | 5 ++ .../Test-AppManagementPolicies.Tests.ps1 | 6 +-- .../Test-ConditionalAccessBaseline.Tests.ps1 | 18 ++++---- .../Test-ConditionalAccessWhatIf.Tests.ps1 | 6 ++- .../Test-PrivilegedAssignments.Tests.ps1 | 46 +++++++++++++------ 26 files changed, 156 insertions(+), 36 deletions(-) diff --git a/powershell/internal/Get-MtSkippedReason.ps1 b/powershell/internal/Get-MtSkippedReason.ps1 index 4f3fc1b3..e93f7ddb 100644 --- a/powershell/internal/Get-MtSkippedReason.ps1 +++ b/powershell/internal/Get-MtSkippedReason.ps1 @@ -15,7 +15,7 @@ function Get-MtSkippedReason { "NotLicensedEntraIDP2" { "This test is for tenants that are licensed for Entra ID P2. See [Entra ID licensing](https://learn.microsoft.com/entra/fundamentals/licensing)"; break} "NotLicensedEntraIDGovernance" { "This test is for tenants that are licensed for Entra ID Governance. See [Entra ID Governance licensing](https://learn.microsoft.com/entra/fundamentals/licensing#microsoft-entra-id-governance)"; break} "NotLicensedEntraWorkloadID" { "This test is for tenants that are licensed for Entra Workload ID. See [Entra Workload ID licensing](https://learn.microsoft.com/entra/workload-id/workload-identities-faqs)"; break} - "SecurityDefaultDisabled" { "Security defaults are disabled. See [Security defaults](https://learn.microsoft.com/en-us/entra/fundamentals/security-defaults)"; break} + "LicensedEntraIDPremium" { "This test is for tenants that are not licensed for any Entra ID Premium license. See [Entra ID licensing](https://learn.microsoft.com/entra/fundamentals/licensing)"; break} default { $SkippedBecause; break} } } \ No newline at end of file diff --git a/powershell/public/Add-MtTestResultDetail.ps1 b/powershell/public/Add-MtTestResultDetail.ps1 index 7d5b389a..dc8df408 100644 --- a/powershell/public/Add-MtTestResultDetail.ps1 +++ b/powershell/public/Add-MtTestResultDetail.ps1 @@ -63,7 +63,7 @@ Function Add-MtTestResultDetail { [string] $TestName = $____Pester.CurrentTest.ExpandedName, [ValidateSet('NotConnectedAzure', 'NotConnectedExchange', 'NotLicensedEntraIDP1', - 'NotLicensedEntraIDP2', 'NotLicensedEntraIDGovernance', 'NotLicensedEntraWorkloadID', "SecurityDefaultDisabled" + 'NotLicensedEntraIDP2', 'NotLicensedEntraIDGovernance', 'NotLicensedEntraWorkloadID', "LicensedEntraIDPremium" )] [string] $SkippedBecause ) diff --git a/powershell/public/Test-MtCaAllAppsExists.ps1 b/powershell/public/Test-MtCaAllAppsExists.ps1 index ae2bc13c..8710ad0c 100644 --- a/powershell/public/Test-MtCaAllAppsExists.ps1 +++ b/powershell/public/Test-MtCaAllAppsExists.ps1 @@ -30,6 +30,11 @@ Function Test-MtCaAllAppsExists { [switch] $SkipCheckAllUsers = $false ) + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } | Where-Object { $_.grantcontrols.builtincontrols -notcontains 'passwordChange' } $testDescription = " diff --git a/powershell/public/Test-MtCaApplicationEnforcedRestriction.ps1 b/powershell/public/Test-MtCaApplicationEnforcedRestriction.ps1 index bff9e115..42d9ee07 100644 --- a/powershell/public/Test-MtCaApplicationEnforcedRestriction.ps1 +++ b/powershell/public/Test-MtCaApplicationEnforcedRestriction.ps1 @@ -17,6 +17,11 @@ Function Test-MtCaApplicationEnforcedRestriction { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } $result = $false diff --git a/powershell/public/Test-MtCaBlockLegacyExchangeActiveSyncAuthentication.ps1 b/powershell/public/Test-MtCaBlockLegacyExchangeActiveSyncAuthentication.ps1 index 323b3d4f..f982d6af 100644 --- a/powershell/public/Test-MtCaBlockLegacyExchangeActiveSyncAuthentication.ps1 +++ b/powershell/public/Test-MtCaBlockLegacyExchangeActiveSyncAuthentication.ps1 @@ -18,6 +18,11 @@ Function Test-MtCaBlockLegacyExchangeActiveSyncAuthentication { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } $testDescription = " diff --git a/powershell/public/Test-MtCaBlockLegacyOtherAuthentication.ps1 b/powershell/public/Test-MtCaBlockLegacyOtherAuthentication.ps1 index 0cbb0124..da61b1c3 100644 --- a/powershell/public/Test-MtCaBlockLegacyOtherAuthentication.ps1 +++ b/powershell/public/Test-MtCaBlockLegacyOtherAuthentication.ps1 @@ -18,6 +18,11 @@ Function Test-MtCaBlockLegacyOtherAuthentication { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } # Remove policies that require password change, as they are related to user risk and not MFA on signin $policies = $policies | Where-Object { $_.grantcontrols.builtincontrols -notcontains 'passwordChange' } diff --git a/powershell/public/Test-MtCaBlockUnknownOrUnsupportedDevicePlatform.ps1 b/powershell/public/Test-MtCaBlockUnknownOrUnsupportedDevicePlatform.ps1 index 4ec37d95..9dd44938 100644 --- a/powershell/public/Test-MtCaBlockUnknownOrUnsupportedDevicePlatform.ps1 +++ b/powershell/public/Test-MtCaBlockUnknownOrUnsupportedDevicePlatform.ps1 @@ -17,6 +17,11 @@ Function Test-MtCaBlockUnknownOrUnsupportedDevicePlatform { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } $testDescription = " diff --git a/powershell/public/Test-MtCaDeviceComplianceAdminsExists.ps1 b/powershell/public/Test-MtCaDeviceComplianceAdminsExists.ps1 index 3c479a5d..21947c0a 100644 --- a/powershell/public/Test-MtCaDeviceComplianceAdminsExists.ps1 +++ b/powershell/public/Test-MtCaDeviceComplianceAdminsExists.ps1 @@ -20,6 +20,11 @@ Function Test-MtCaDeviceComplianceAdminsExists { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $AdministrativeRolesToCheck = @( "62e90394-69f5-4237-9190-012177145e10", "194ae4cb-b126-40b2-bd5b-6091b380977d", @@ -65,8 +70,8 @@ See [Require compliant or Microsoft Entra hybrid joined device for administrator $PolicyIncludesAllRoles = $true $AdministrativeRolesToCheck | ForEach-Object { if ( ( $_ -notin $policy.conditions.users.includeRoles ` - -and $policy.conditions.users.includeUsers -ne 'All' ) ` - -or $_ -in $policy.conditions.users.excludeRoles ` + -and $policy.conditions.users.includeUsers -ne 'All' ) ` + -or $_ -in $policy.conditions.users.excludeRoles ` ) { $PolicyIncludesAllRoles = $false } diff --git a/powershell/public/Test-MtCaDeviceComplianceExists.ps1 b/powershell/public/Test-MtCaDeviceComplianceExists.ps1 index 1184e49f..4ce8fe65 100644 --- a/powershell/public/Test-MtCaDeviceComplianceExists.ps1 +++ b/powershell/public/Test-MtCaDeviceComplianceExists.ps1 @@ -18,6 +18,11 @@ Function Test-MtCaDeviceComplianceExists { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policies = Get-MtConditionalAccessPolicy $result = $false diff --git a/powershell/public/Test-MtCaEmergencyAccessExists.ps1 b/powershell/public/Test-MtCaEmergencyAccessExists.ps1 index 36e9510f..472f0804 100644 --- a/powershell/public/Test-MtCaEmergencyAccessExists.ps1 +++ b/powershell/public/Test-MtCaEmergencyAccessExists.ps1 @@ -19,6 +19,11 @@ Function Test-MtCaEmergencyAccessExists { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + # Only check policies that are not related to authentication context $policies = Get-MtConditionalAccessPolicy | Where-Object { -not $_.conditions.applications.includeAuthenticationContextClassReferences } diff --git a/powershell/public/Test-MtCaEnforceNonPersistentBrowserSession.ps1 b/powershell/public/Test-MtCaEnforceNonPersistentBrowserSession.ps1 index d5796c42..f13ca3ee 100644 --- a/powershell/public/Test-MtCaEnforceNonPersistentBrowserSession.ps1 +++ b/powershell/public/Test-MtCaEnforceNonPersistentBrowserSession.ps1 @@ -20,6 +20,11 @@ Function Test-MtCaEnforceNonPersistentBrowserSession { [switch]$AllDevices ) + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } $testDescription = " diff --git a/powershell/public/Test-MtCaEnforceSignInFrequency.ps1 b/powershell/public/Test-MtCaEnforceSignInFrequency.ps1 index 5c6487e3..e7a75000 100644 --- a/powershell/public/Test-MtCaEnforceSignInFrequency.ps1 +++ b/powershell/public/Test-MtCaEnforceSignInFrequency.ps1 @@ -20,6 +20,11 @@ Function Test-MtCaEnforceSignInFrequency { [switch]$AllDevices ) + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } $testDescription = " diff --git a/powershell/public/Test-MtCaExclusionForDirectorySyncAccount.ps1 b/powershell/public/Test-MtCaExclusionForDirectorySyncAccount.ps1 index 5a1eee61..b62d124d 100644 --- a/powershell/public/Test-MtCaExclusionForDirectorySyncAccount.ps1 +++ b/powershell/public/Test-MtCaExclusionForDirectorySyncAccount.ps1 @@ -18,6 +18,11 @@ Function Test-MtCaExclusionForDirectorySyncAccount { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $testDescription = "It is recommended to exclude directory synchronization accounts from all conditional access policies scoped to all cloud apps." $testResult = "The following conditional access policies are scoped to all users but don't exclude the directory synchronization accounts:`n`n" diff --git a/powershell/public/Test-MtCaLicenseUtilization.ps1 b/powershell/public/Test-MtCaLicenseUtilization.ps1 index 9e80e2f2..7f061bca 100644 --- a/powershell/public/Test-MtCaLicenseUtilization.ps1 +++ b/powershell/public/Test-MtCaLicenseUtilization.ps1 @@ -26,6 +26,15 @@ function Test-MtCaLicenseUtilization { [string]$License ) + if (( Get-MtLicenseInformation EntraID ) -eq "Free") { + if ($License -eq "P1") { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + } elseif ($License -eq "P2") { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP2 + } + return $null + } + # Get the total number of users in the tenant $TotalUserCount = Get-MtTotalEntraIdUserCount diff --git a/powershell/public/Test-MtCaMfaForAdmin.ps1 b/powershell/public/Test-MtCaMfaForAdmin.ps1 index 48206249..b0eda5be 100644 --- a/powershell/public/Test-MtCaMfaForAdmin.ps1 +++ b/powershell/public/Test-MtCaMfaForAdmin.ps1 @@ -18,6 +18,11 @@ Function Test-MtCaMfaForAdmin { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $AdministrativeRolesToCheck = @( "62e90394-69f5-4237-9190-012177145e10", "194ae4cb-b126-40b2-bd5b-6091b380977d", @@ -42,8 +47,8 @@ Function Test-MtCaMfaForAdmin { $PolicyIncludesAllRoles = $true $AdministrativeRolesToCheck | ForEach-Object { if ( ( $_ -notin $policy.conditions.users.includeRoles ` - -and $policy.conditions.users.includeUsers -ne 'All' ) ` - -or $_ -in $policy.conditions.users.excludeRoles ` + -and $policy.conditions.users.includeUsers -ne 'All' ) ` + -or $_ -in $policy.conditions.users.excludeRoles ` ) { $PolicyIncludesAllRoles = $false } diff --git a/powershell/public/Test-MtCaMfaForAdminManagement.ps1 b/powershell/public/Test-MtCaMfaForAdminManagement.ps1 index dc2f3ece..48c4611e 100644 --- a/powershell/public/Test-MtCaMfaForAdminManagement.ps1 +++ b/powershell/public/Test-MtCaMfaForAdminManagement.ps1 @@ -18,6 +18,11 @@ Function Test-MtCaMfaForAdminManagement { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } $result = $false diff --git a/powershell/public/Test-MtCaMfaForAllUsers.ps1 b/powershell/public/Test-MtCaMfaForAllUsers.ps1 index 1611e52a..6f639c7a 100644 --- a/powershell/public/Test-MtCaMfaForAllUsers.ps1 +++ b/powershell/public/Test-MtCaMfaForAllUsers.ps1 @@ -18,6 +18,11 @@ Function Test-MtCaMfaForAllUsers { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } # Remove policies that require password change, as they are related to user risk and not MFA on signin $policies = $policies | Where-Object { $_.grantcontrols.builtincontrols -notcontains 'passwordChange' } diff --git a/powershell/public/Test-MtCaMfaForGuest.ps1 b/powershell/public/Test-MtCaMfaForGuest.ps1 index 94d77a51..5b445441 100644 --- a/powershell/public/Test-MtCaMfaForGuest.ps1 +++ b/powershell/public/Test-MtCaMfaForGuest.ps1 @@ -17,6 +17,11 @@ Function Test-MtCaMfaForGuest { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } # Remove policies that require password change, as they are related to user risk and not MFA on signin $policies = $policies | Where-Object { $_.grantcontrols.builtincontrols -notcontains 'passwordChange' } diff --git a/powershell/public/Test-MtCaMfaForRiskySignIn.ps1 b/powershell/public/Test-MtCaMfaForRiskySignIn.ps1 index 8d21146f..284ec233 100644 --- a/powershell/public/Test-MtCaMfaForRiskySignIn.ps1 +++ b/powershell/public/Test-MtCaMfaForRiskySignIn.ps1 @@ -17,6 +17,11 @@ Function Test-MtCaMfaForRiskySignIn { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -ne "P2" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP2 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } # Remove policies that require password change, as they are related to user risk and not MFA on signin $policies = $policies | Where-Object { $_.grantcontrols.builtincontrols -notcontains 'passwordChange' } diff --git a/powershell/public/Test-MtCaRequirePasswordChangeForHighUserRisk.ps1 b/powershell/public/Test-MtCaRequirePasswordChangeForHighUserRisk.ps1 index 82e26ad8..c60f8514 100644 --- a/powershell/public/Test-MtCaRequirePasswordChangeForHighUserRisk.ps1 +++ b/powershell/public/Test-MtCaRequirePasswordChangeForHighUserRisk.ps1 @@ -17,6 +17,11 @@ Function Test-MtCaRequirePasswordChangeForHighUserRisk { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -ne "P2" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP2 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } # Only check policies that have password change as a grant control $policies = $policies | Where-Object { $_.grantcontrols.builtincontrols -contains 'passwordChange' } diff --git a/powershell/public/Test-MtCaSecureSecurityInfoRegistration.ps1 b/powershell/public/Test-MtCaSecureSecurityInfoRegistration.ps1 index 7b8099d2..4929fd1d 100644 --- a/powershell/public/Test-MtCaSecureSecurityInfoRegistration.ps1 +++ b/powershell/public/Test-MtCaSecureSecurityInfoRegistration.ps1 @@ -17,6 +17,11 @@ Function Test-MtCaSecureSecurityInfoRegistration { [OutputType([bool])] param () + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" } # Remove policies that require password change, as they are related to user risk and not MFA on signin $policies = $policies | Where-Object { $_.grantcontrols.builtincontrols -notcontains 'passwordChange' } diff --git a/powershell/public/Test-MtCaWIFBlockLegacyAuthentication.ps1 b/powershell/public/Test-MtCaWIFBlockLegacyAuthentication.ps1 index c6876591..bdafffa2 100644 --- a/powershell/public/Test-MtCaWIFBlockLegacyAuthentication.ps1 +++ b/powershell/public/Test-MtCaWIFBlockLegacyAuthentication.ps1 @@ -20,6 +20,11 @@ function Test-MtCaWIFBlockLegacyAuthentication { [string]$UserId ) + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + return $null + } + $policiesResult = Test-MtConditionalAccessWhatIf -UserId $UserId -IncludeApplications "00000002-0000-0ff1-ce00-000000000000" -ClientAppType exchangeActiveSync if ( $null -ne $policiesResult ) { diff --git a/tests/Maester/Entra/Test-AppManagementPolicies.Tests.ps1 b/tests/Maester/Entra/Test-AppManagementPolicies.Tests.ps1 index 8e05a808..6c58d237 100644 --- a/tests/Maester/Entra/Test-AppManagementPolicies.Tests.ps1 +++ b/tests/Maester/Entra/Test-AppManagementPolicies.Tests.ps1 @@ -1,10 +1,6 @@ Describe "App Management Policies" -Tag "App", "Security", "All" { It "MT.1002: App management restrictions on applications and service principals is configured and enabled. See https://maester.dev/docs/tests/MT.1002" -Tag "MT.1002" { - $appManagementPolicyEnabled = Test-MtAppManagementPolicyEnabled - - if ($null -ne $appManagementPolicyEnabled) { - $appManagementPolicyEnabled | Should -Be $true -Because "an app policy for workload identities should be defined to enforce strong credentials instead of passwords and a maximum expiry period (e.g. credential should be renewed every six months)" - } + Test-MtAppManagementPolicyEnabled | Should -Be $true -Because "an app policy for workload identities should be defined to enforce strong credentials instead of passwords and a maximum expiry period (e.g. credential should be renewed every six months)" } } \ No newline at end of file diff --git a/tests/Maester/Entra/Test-ConditionalAccessBaseline.Tests.ps1 b/tests/Maester/Entra/Test-ConditionalAccessBaseline.Tests.ps1 index 8bbdc483..7772238c 100644 --- a/tests/Maester/Entra/Test-ConditionalAccessBaseline.Tests.ps1 +++ b/tests/Maester/Entra/Test-ConditionalAccessBaseline.Tests.ps1 @@ -1,8 +1,4 @@ -BeforeDiscovery { - $EntraIDPlan = Get-MtLicenseInformation -Product "EntraID" -} - -Describe "Conditional Access Baseline Policies" -Tag "CA", "Security", "All" -Skip:( $EntraIDPlan -eq "Free" ) { +Describe "Conditional Access Baseline Policies" -Tag "CA", "Security", "All" { It "MT.1001: At least one Conditional Access policy is configured with device compliance. See https://maester.dev/docs/tests/MT.1001" -Tag "MT.1001" { Test-MtCaDeviceComplianceExists | Should -Be $true -Because "there is no policy which requires device compliances" } @@ -65,16 +61,20 @@ Describe "Conditional Access Baseline Policies" -Tag "CA", "Security", "All" -Sk $LicenseReport = Test-MtCaLicenseUtilization -License "P1" $LicenseReport.TotalLicensesUtilized | Should -BeLessOrEqual $LicenseReport.EntitledLicenseCount -Because "this is the maximium number of user that can utilize a P1 license" } - It "MT.1023: All users utilizing a P2 license should be licensed. See https://maester.dev/docs/tests/MT.1023" -Skip:( $EntraIDPlan -ne "P2" ) -Tag "MT.1023" { + It "MT.1023: All users utilizing a P2 license should be licensed. See https://maester.dev/docs/tests/MT.1023" -Tag "MT.1023" { $LicenseReport = Test-MtCaLicenseUtilization -License "P2" $LicenseReport.TotalLicensesUtilized | Should -BeLessOrEqual $LicenseReport.EntitledLicenseCount -Because "this is the maximium number of user that can utilize a P2 license" } } } -Describe "Security Defaults" -Tag "CA", "Security", "All" -Skip:( $EntraIDPlan -ne "Free" ) { +Describe "Security Defaults" -Tag "CA", "Security", "All" { It "MT.1021: Security Defaults are enabled. See https://maester.dev/docs/tests/MT.1021" -Tag "MT.1021" { - $SecurityDefaults = Invoke-MtGraphRequest -RelativeUri "policies/identitySecurityDefaultsEnforcementPolicy" -ApiVersion beta | Select-Object -ExpandProperty isEnabled - $SecurityDefaults | Should -Be $true -Because "Security Defaults are not enabled" + if ($EntraIDPlan -ne "Free") { + Add-MtTestResultDetail -SkippedBecause LicensedEntraIDPremium + } else { + $SecurityDefaults = Invoke-MtGraphRequest -RelativeUri "policies/identitySecurityDefaultsEnforcementPolicy" -ApiVersion beta | Select-Object -ExpandProperty isEnabled + $SecurityDefaults | Should -Be $true -Because "Security Defaults are not enabled" + } } } \ No newline at end of file diff --git a/tests/Maester/Entra/Test-ConditionalAccessWhatIf.Tests.ps1 b/tests/Maester/Entra/Test-ConditionalAccessWhatIf.Tests.ps1 index 1152c700..b81fdb94 100644 --- a/tests/Maester/Entra/Test-ConditionalAccessWhatIf.Tests.ps1 +++ b/tests/Maester/Entra/Test-ConditionalAccessWhatIf.Tests.ps1 @@ -26,7 +26,11 @@ Describe "Conditional Access WhatIf" -Tag "CA", "CAWhatIf", "Security", "All" -S Context "Emergency access users" -ForEach @( $EmergencyAccessUsers ) { It "MT.1034: Emergency access users should not be blocked ()" -Tag "MT.1034" { - Test-MtConditionalAccessWhatIf -UserId $id -IncludeApplications "00000002-0000-0ff1-ce00-000000000000" -ClientAppType exchangeActiveSync | Should -BeNullOrEmpty + if ( ( Get-MtLicenseInformation EntraID ) -eq "Free" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP1 + } else { + Test-MtConditionalAccessWhatIf -UserId $id -IncludeApplications "00000002-0000-0ff1-ce00-000000000000" -ClientAppType exchangeActiveSync | Should -BeNullOrEmpty + } } } diff --git a/tests/Maester/Entra/Test-PrivilegedAssignments.Tests.ps1 b/tests/Maester/Entra/Test-PrivilegedAssignments.Tests.ps1 index 9cc755bd..ce1c9a57 100644 --- a/tests/Maester/Entra/Test-PrivilegedAssignments.Tests.ps1 +++ b/tests/Maester/Entra/Test-PrivilegedAssignments.Tests.ps1 @@ -21,21 +21,37 @@ Describe "Directory Roles - Permanent assignments" -Tag "Privileged", "Security" } } -Describe "Privileged Identity Management (PIM) - Alerts" -Tag "Privileged", "Security", "All" -Skip:( $EntraIDPlan -ne "P2" ) { +Describe "Privileged Identity Management (PIM) - Alerts" -Tag "Privileged", "Security", "All" { It "MT.1029: Stale accounts are not assigned to privileged roles. See https://maester.dev/docs/tests/MT.1029" -Tag "MT.1029" { - $Check = Test-MtPimAlertsExists -AlertId "StaleSignInAlert" - $check.numberOfAffectedItems -eq "0" | Should -Be $true -Because $check.securityImpact - } - It "MT.1030: Eligible role assignments on Control Plane are in use by administrators. See https://maester.dev/docs/tests/MT.1030" -Skip:( $EntraIDPlan -ne "P2" ) -Tag "MT.1030" { - $Check = Test-MtPimAlertsExists -AlertId "RedundantAssignmentAlert" -FilteredAccessLevel "ControlPlane" - $check.numberOfAffectedItems -eq "0" | Should -Be $true -Because $check.securityImpact - } - It "MT.1031: Privileged role on Control Plane are managed by PIM only. See https://maester.dev/docs/tests/MT.1031" -Skip:( $EntraIDPlan -ne "P2" ) -Tag "MT.1031" { - $Check = Test-MtPimAlertsExists -AlertId "RolesAssignedOutsidePimAlert" -FilteredAccessLevel "ControlPlane" - $check.numberOfAffectedItems -eq "0" | Should -Be $true -Because $check.securityImpact - } - It "MT.1032: Limited number of Global Admins are assigned. See https://maester.dev/docs/tests/MT.1032" -Skip:( $EntraIDPlan -ne "P2" ) -Tag "MT.1032" { - $Check = Test-MtPimAlertsExists -AlertId "TooManyGlobalAdminsAssignedToTenantAlert" - $check.numberOfAffectedItems -eq "0" | Should -Be $true -Because $check.securityImpact + if ( ( Get-MtLicenseInformation EntraID ) -ne "P2" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP2 + } else { + $Check = Test-MtPimAlertsExists -AlertId "StaleSignInAlert" + $check.numberOfAffectedItems -eq "0" | Should -Be $true -Because $check.securityImpact + } + } + It "MT.1030: Eligible role assignments on Control Plane are in use by administrators. See https://maester.dev/docs/tests/MT.1030" -Tag "MT.1030" { + if ( ( Get-MtLicenseInformation EntraID ) -ne "P2" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP2 + } else { + $Check = Test-MtPimAlertsExists -AlertId "RedundantAssignmentAlert" -FilteredAccessLevel "ControlPlane" + $check.numberOfAffectedItems -eq "0" | Should -Be $true -Because $check.securityImpact + } + } + It "MT.1031: Privileged role on Control Plane are managed by PIM only. See https://maester.dev/docs/tests/MT.1031" -Tag "MT.1031" { + if ( ( Get-MtLicenseInformation EntraID ) -ne "P2" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP2 + } else { + $Check = Test-MtPimAlertsExists -AlertId "RolesAssignedOutsidePimAlert" -FilteredAccessLevel "ControlPlane" + $check.numberOfAffectedItems -eq "0" | Should -Be $true -Because $check.securityImpact + } + } + It "MT.1032: Limited number of Global Admins are assigned. See https://maester.dev/docs/tests/MT.1032" -Tag "MT.1032" { + if ( ( Get-MtLicenseInformation EntraID ) -ne "P2" ) { + Add-MtTestResultDetail -SkippedBecause NotLicensedEntraIDP2 + } else { + $Check = Test-MtPimAlertsExists -AlertId "TooManyGlobalAdminsAssignedToTenantAlert" + $check.numberOfAffectedItems -eq "0" | Should -Be $true -Because $check.securityImpact + } } } \ No newline at end of file From 492d7acfb215794270ea8e87d92fd60e5a03a6fe Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 27 Jun 2024 20:00:25 -0700 Subject: [PATCH 7/9] Update Get-MtSkippedReason.ps1 New skip reason in #271 --- powershell/internal/Get-MtSkippedReason.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powershell/internal/Get-MtSkippedReason.ps1 b/powershell/internal/Get-MtSkippedReason.ps1 index e93f7ddb..f6b6b219 100644 --- a/powershell/internal/Get-MtSkippedReason.ps1 +++ b/powershell/internal/Get-MtSkippedReason.ps1 @@ -11,6 +11,7 @@ function Get-MtSkippedReason { switch($SkippedBecause){ "NotConnectedAzure" { "Not connected to Azure. See [Connecting to Azure](https://maester.dev/docs/installation#optional-modules-and-permissions)" ; break} "NotConnectedExchange" { "Not connected to Exchange Online. See [Connecting to Exchange Online](https://maester.dev/docs/installation#optional-modules-and-permissions)"; break} + "NotDotGovDomain" { "This test is only for federal, executive branch, departments and agencies. To override use [Test-MtCisaDmarcAggregateCisa -Force](https://maester.dev/docs/commands/Test-MtCisaDmarcAggregateCisa)"); break} "NotLicensedEntraIDP1" { "This test is for tenants that are licensed for Entra ID P1. See [Entra ID licensing](https://learn.microsoft.com/entra/fundamentals/licensing)"; break} "NotLicensedEntraIDP2" { "This test is for tenants that are licensed for Entra ID P2. See [Entra ID licensing](https://learn.microsoft.com/entra/fundamentals/licensing)"; break} "NotLicensedEntraIDGovernance" { "This test is for tenants that are licensed for Entra ID Governance. See [Entra ID Governance licensing](https://learn.microsoft.com/entra/fundamentals/licensing#microsoft-entra-id-governance)"; break} @@ -18,4 +19,4 @@ function Get-MtSkippedReason { "LicensedEntraIDPremium" { "This test is for tenants that are not licensed for any Entra ID Premium license. See [Entra ID licensing](https://learn.microsoft.com/entra/fundamentals/licensing)"; break} default { $SkippedBecause; break} } -} \ No newline at end of file +} From 5e594073eeae6a8505b226271a48f9dfb0fc9298 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 27 Jun 2024 20:01:03 -0700 Subject: [PATCH 8/9] Update Add-MtTestResultDetail.ps1 --- powershell/public/Add-MtTestResultDetail.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powershell/public/Add-MtTestResultDetail.ps1 b/powershell/public/Add-MtTestResultDetail.ps1 index dc8df408..1d43405b 100644 --- a/powershell/public/Add-MtTestResultDetail.ps1 +++ b/powershell/public/Add-MtTestResultDetail.ps1 @@ -62,7 +62,7 @@ Function Add-MtTestResultDetail { [Parameter(Mandatory = $false)] [string] $TestName = $____Pester.CurrentTest.ExpandedName, - [ValidateSet('NotConnectedAzure', 'NotConnectedExchange', 'NotLicensedEntraIDP1', + [ValidateSet('NotConnectedAzure', 'NotConnectedExchange', 'NotDotGovDomain', 'NotLicensedEntraIDP1', 'NotLicensedEntraIDP2', 'NotLicensedEntraIDGovernance', 'NotLicensedEntraWorkloadID', "LicensedEntraIDPremium" )] [string] $SkippedBecause @@ -131,4 +131,4 @@ Function Add-MtTestResultDetail { #This needs to be set at the end. Set-ItResult -Skipped -Because $SkippedReason } -} \ No newline at end of file +} From fb5993d1e9ff3c4bcfa66dcf2497b2c1d692301a Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 27 Jun 2024 20:02:52 -0700 Subject: [PATCH 9/9] Update Get-MtSkippedReason.ps1 Extra paren --- powershell/internal/Get-MtSkippedReason.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powershell/internal/Get-MtSkippedReason.ps1 b/powershell/internal/Get-MtSkippedReason.ps1 index f6b6b219..e278259b 100644 --- a/powershell/internal/Get-MtSkippedReason.ps1 +++ b/powershell/internal/Get-MtSkippedReason.ps1 @@ -11,7 +11,7 @@ function Get-MtSkippedReason { switch($SkippedBecause){ "NotConnectedAzure" { "Not connected to Azure. See [Connecting to Azure](https://maester.dev/docs/installation#optional-modules-and-permissions)" ; break} "NotConnectedExchange" { "Not connected to Exchange Online. See [Connecting to Exchange Online](https://maester.dev/docs/installation#optional-modules-and-permissions)"; break} - "NotDotGovDomain" { "This test is only for federal, executive branch, departments and agencies. To override use [Test-MtCisaDmarcAggregateCisa -Force](https://maester.dev/docs/commands/Test-MtCisaDmarcAggregateCisa)"); break} + "NotDotGovDomain" { "This test is only for federal, executive branch, departments and agencies. To override use [Test-MtCisaDmarcAggregateCisa -Force](https://maester.dev/docs/commands/Test-MtCisaDmarcAggregateCisa)"; break} "NotLicensedEntraIDP1" { "This test is for tenants that are licensed for Entra ID P1. See [Entra ID licensing](https://learn.microsoft.com/entra/fundamentals/licensing)"; break} "NotLicensedEntraIDP2" { "This test is for tenants that are licensed for Entra ID P2. See [Entra ID licensing](https://learn.microsoft.com/entra/fundamentals/licensing)"; break} "NotLicensedEntraIDGovernance" { "This test is for tenants that are licensed for Entra ID Governance. See [Entra ID Governance licensing](https://learn.microsoft.com/entra/fundamentals/licensing#microsoft-entra-id-governance)"; break}