From 8a46050f635038607ef1338d9f48913afd70549f Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 17 Apr 2026 19:42:11 +0000 Subject: [PATCH 01/14] Added Intune tests for LAPS Configuration, ASR Rules, App Control, and Managed Installer Rules. --- functions/Test-MtIntuneASRRules.ps1 | 20 +++++++++++++++++++ functions/Test-MtIntuneAppControl.ps1 | 20 +++++++++++++++++++ functions/Test-MtIntuneLAPSConfiguration.ps1 | 20 +++++++++++++++++++ .../Test-MtIntuneManagedInstallerRules.ps1 | 20 +++++++++++++++++++ .../Intune/Test-MtIntuneASRRules.Tests.ps1 | 8 ++++++++ .../Intune/Test-MtIntuneAppControl.Tests.ps1 | 8 ++++++++ .../Test-MtIntuneLAPSConfiguration.Tests.ps1 | 8 ++++++++ .../Test-MtIntuneManagedInstaller.Tests.ps1 | 8 ++++++++ 8 files changed, 112 insertions(+) create mode 100644 functions/Test-MtIntuneASRRules.ps1 create mode 100644 functions/Test-MtIntuneAppControl.ps1 create mode 100644 functions/Test-MtIntuneLAPSConfiguration.ps1 create mode 100644 functions/Test-MtIntuneManagedInstallerRules.ps1 create mode 100644 tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 create mode 100644 tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 create mode 100644 tests/Maester/Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 create mode 100644 tests/Maester/Intune/Test-MtIntuneManagedInstaller.Tests.ps1 diff --git a/functions/Test-MtIntuneASRRules.ps1 b/functions/Test-MtIntuneASRRules.ps1 new file mode 100644 index 000000000..81dc7b894 --- /dev/null +++ b/functions/Test-MtIntuneASRRules.ps1 @@ -0,0 +1,20 @@ +function Test-MtIntuneASRRules { + param ( + [string]$ApiVersion = "v1.0" + ) + + $uri = "/deviceManagement/intents" + $response = Invoke-MtGraphRequest -RelativeUri $uri -ApiVersion $ApiVersion + + if ($response -and $response.value) { + foreach ($intent in $response.value) { + if ($intent.displayName -like "*ASR*" -and $intent.odataType -eq "#microsoft.graph.deviceManagementIntent") { + Add-MtTestResultDetail -Name "ASR Rules Found" -Detail $intent.displayName + return $true + } + } + } + + Add-MtTestResultDetail -Name "ASR Rules Missing" -Detail "No ASR Rules found." + return $false +} \ No newline at end of file diff --git a/functions/Test-MtIntuneAppControl.ps1 b/functions/Test-MtIntuneAppControl.ps1 new file mode 100644 index 000000000..9b43de200 --- /dev/null +++ b/functions/Test-MtIntuneAppControl.ps1 @@ -0,0 +1,20 @@ +function Test-MtIntuneAppControl { + param ( + [string]$ApiVersion = "v1.0" + ) + + $uri = "/deviceManagement/configurationPolicies" + $response = Invoke-MtGraphRequest -RelativeUri $uri -ApiVersion $ApiVersion + + if ($response -and $response.value) { + foreach ($policy in $response.value) { + if ($policy.displayName -like "*App Control*" -and $policy.odataType -eq "#microsoft.graph.deviceManagementConfigurationPolicy") { + Add-MtTestResultDetail -Name "App Control Policy Found" -Detail $policy.displayName + return $true + } + } + } + + Add-MtTestResultDetail -Name "App Control Policy Missing" -Detail "No App Control for Business policy found." + return $false +} \ No newline at end of file diff --git a/functions/Test-MtIntuneLAPSConfiguration.ps1 b/functions/Test-MtIntuneLAPSConfiguration.ps1 new file mode 100644 index 000000000..6e7e9e109 --- /dev/null +++ b/functions/Test-MtIntuneLAPSConfiguration.ps1 @@ -0,0 +1,20 @@ +function Test-MtIntuneLAPSConfiguration { + param ( + [string]$ApiVersion = "v1.0" + ) + + $uri = "/deviceManagement/configurationPolicies" + $response = Invoke-MtGraphRequest -RelativeUri $uri -ApiVersion $ApiVersion + + if ($response -and $response.value) { + foreach ($policy in $response.value) { + if ($policy.displayName -like "*LAPS*" -and $policy.odataType -eq "#microsoft.graph.deviceManagementConfigurationPolicy") { + Add-MtTestResultDetail -Name "LAPS Policy Found" -Detail $policy.displayName + return $true + } + } + } + + Add-MtTestResultDetail -Name "LAPS Policy Missing" -Detail "No LAPS Configuration policy found." + return $false +} \ No newline at end of file diff --git a/functions/Test-MtIntuneManagedInstallerRules.ps1 b/functions/Test-MtIntuneManagedInstallerRules.ps1 new file mode 100644 index 000000000..86c630a9d --- /dev/null +++ b/functions/Test-MtIntuneManagedInstallerRules.ps1 @@ -0,0 +1,20 @@ +function Test-MtIntuneManagedInstallerRules { + param ( + [string]$ApiVersion = "v1.0" + ) + + $uri = "/deviceManagement/intents" + $response = Invoke-MtGraphRequest -RelativeUri $uri -ApiVersion $ApiVersion + + if ($response -and $response.value) { + foreach ($intent in $response.value) { + if ($intent.displayName -like "*Managed Installer*" -and $intent.odataType -eq "#microsoft.graph.deviceManagementIntent") { + Add-MtTestResultDetail -Name "Managed Installer Rules Found" -Detail $intent.displayName + return $true + } + } + } + + Add-MtTestResultDetail -Name "Managed Installer Rules Missing" -Detail "No Managed Installer Rules found." + return $false +} \ No newline at end of file diff --git a/tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 b/tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 new file mode 100644 index 000000000..91a0332f1 --- /dev/null +++ b/tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 @@ -0,0 +1,8 @@ +Describe "Maester/Intune" -Tag "Maester", "Intune", "ASR" { + It "MT.1201: Ensure ASR Rules are configured correctly" -Tag "MT.1201" { + $result = Test-MtIntuneASRRules + if ($null -ne $result) { + $result | Should -Be $true -Because "Attack Surface Reduction (ASR) Rules are configured to block threats." + } + } +} \ No newline at end of file diff --git a/tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 b/tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 new file mode 100644 index 000000000..d22804bf6 --- /dev/null +++ b/tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 @@ -0,0 +1,8 @@ +Describe "Maester/Intune" -Tag "Maester", "Intune", "AppControl" { + It "MT.1202: Ensure App Control for Business is enabled" -Tag "MT.1202" { + $result = Test-MtIntuneAppControl + if ($null -ne $result) { + $result | Should -Be $true -Because "App Control for Business is enabled in Intune." + } + } +} \ No newline at end of file diff --git a/tests/Maester/Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 b/tests/Maester/Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 new file mode 100644 index 000000000..07207b12b --- /dev/null +++ b/tests/Maester/Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 @@ -0,0 +1,8 @@ +Describe "Maester/Intune" -Tag "Maester", "Intune", "LAPS" { + It "MT.1200: Ensure LAPS Configuration Policy is properly set" -Tag "MT.1200" { + $result = Test-MtIntuneLAPSConfiguration + if ($null -ne $result) { + $result | Should -Be $true -Because "a LAPS Configuration policy is properly set in Intune." + } + } +} \ No newline at end of file diff --git a/tests/Maester/Intune/Test-MtIntuneManagedInstaller.Tests.ps1 b/tests/Maester/Intune/Test-MtIntuneManagedInstaller.Tests.ps1 new file mode 100644 index 000000000..68b30143e --- /dev/null +++ b/tests/Maester/Intune/Test-MtIntuneManagedInstaller.Tests.ps1 @@ -0,0 +1,8 @@ +Describe "Maester/Intune" -Tag "Maester", "Intune", "ManagedInstaller" { + It "MT.1203: Ensure Managed Installer Rules are configured correctly" -Tag "MT.1203" { + $result = Test-MtIntuneManagedInstallerRules + if ($null -ne $result) { + $result | Should -Be $true -Because "Managed Installer Rules are configured to enforce security." + } + } +} \ No newline at end of file From c6be2a7f9f8b85065e61df2d092b886efe14f1ad Mon Sep 17 00:00:00 2001 From: Ofir Gavish Date: Sun, 19 Apr 2026 00:12:39 +0300 Subject: [PATCH 02/14] Add MT.1124-MT.1127: LAPS, ASR Rules, App Control, Managed Installer tests - MT.1124: Ensure LAPS policy backs up passwords to Entra ID - MT.1125: Ensure ASR rules configured in Block/Audit mode - MT.1126: Ensure App Control for Business is configured - MT.1127: Ensure Managed Installer enabled in App Control Changes: - Move functions from functions/ to powershell/public/maester/intune/ - Add companion .md remediation files for each test - Add FunctionsToExport entries to Maester.psd1 - Renumber from MT.1200-1203 to MT.1124-1127 - PSScriptAnalyzer clean, pester.ps1 5230/5230 passed --- functions/Test-MtIntuneASRRules.ps1 | 20 -- functions/Test-MtIntuneAppControl.ps1 | 20 -- functions/Test-MtIntuneLAPSConfiguration.ps1 | 20 -- .../Test-MtIntuneManagedInstallerRules.ps1 | 20 -- powershell/Maester.psd1 | 4 + .../maester/intune/Test-MtIntuneASRRules.md | 56 ++++++ .../maester/intune/Test-MtIntuneASRRules.ps1 | 167 ++++++++++++++++ .../maester/intune/Test-MtIntuneAppControl.md | 38 ++++ .../intune/Test-MtIntuneAppControl.ps1 | 148 +++++++++++++++ .../intune/Test-MtIntuneLAPSConfiguration.md | 39 ++++ .../intune/Test-MtIntuneLAPSConfiguration.ps1 | 178 ++++++++++++++++++ .../Test-MtIntuneManagedInstallerRules.md | 38 ++++ .../Test-MtIntuneManagedInstallerRules.ps1 | 130 +++++++++++++ .../Intune/Test-MtIntuneASRRules.Tests.ps1 | 2 +- .../Intune/Test-MtIntuneAppControl.Tests.ps1 | 2 +- .../Test-MtIntuneLAPSConfiguration.Tests.ps1 | 2 +- .../Test-MtIntuneManagedInstaller.Tests.ps1 | 2 +- 17 files changed, 802 insertions(+), 84 deletions(-) delete mode 100644 functions/Test-MtIntuneASRRules.ps1 delete mode 100644 functions/Test-MtIntuneAppControl.ps1 delete mode 100644 functions/Test-MtIntuneLAPSConfiguration.ps1 delete mode 100644 functions/Test-MtIntuneManagedInstallerRules.ps1 create mode 100644 powershell/public/maester/intune/Test-MtIntuneASRRules.md create mode 100644 powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 create mode 100644 powershell/public/maester/intune/Test-MtIntuneAppControl.md create mode 100644 powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 create mode 100644 powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.md create mode 100644 powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 create mode 100644 powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.md create mode 100644 powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 diff --git a/functions/Test-MtIntuneASRRules.ps1 b/functions/Test-MtIntuneASRRules.ps1 deleted file mode 100644 index 81dc7b894..000000000 --- a/functions/Test-MtIntuneASRRules.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -function Test-MtIntuneASRRules { - param ( - [string]$ApiVersion = "v1.0" - ) - - $uri = "/deviceManagement/intents" - $response = Invoke-MtGraphRequest -RelativeUri $uri -ApiVersion $ApiVersion - - if ($response -and $response.value) { - foreach ($intent in $response.value) { - if ($intent.displayName -like "*ASR*" -and $intent.odataType -eq "#microsoft.graph.deviceManagementIntent") { - Add-MtTestResultDetail -Name "ASR Rules Found" -Detail $intent.displayName - return $true - } - } - } - - Add-MtTestResultDetail -Name "ASR Rules Missing" -Detail "No ASR Rules found." - return $false -} \ No newline at end of file diff --git a/functions/Test-MtIntuneAppControl.ps1 b/functions/Test-MtIntuneAppControl.ps1 deleted file mode 100644 index 9b43de200..000000000 --- a/functions/Test-MtIntuneAppControl.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -function Test-MtIntuneAppControl { - param ( - [string]$ApiVersion = "v1.0" - ) - - $uri = "/deviceManagement/configurationPolicies" - $response = Invoke-MtGraphRequest -RelativeUri $uri -ApiVersion $ApiVersion - - if ($response -and $response.value) { - foreach ($policy in $response.value) { - if ($policy.displayName -like "*App Control*" -and $policy.odataType -eq "#microsoft.graph.deviceManagementConfigurationPolicy") { - Add-MtTestResultDetail -Name "App Control Policy Found" -Detail $policy.displayName - return $true - } - } - } - - Add-MtTestResultDetail -Name "App Control Policy Missing" -Detail "No App Control for Business policy found." - return $false -} \ No newline at end of file diff --git a/functions/Test-MtIntuneLAPSConfiguration.ps1 b/functions/Test-MtIntuneLAPSConfiguration.ps1 deleted file mode 100644 index 6e7e9e109..000000000 --- a/functions/Test-MtIntuneLAPSConfiguration.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -function Test-MtIntuneLAPSConfiguration { - param ( - [string]$ApiVersion = "v1.0" - ) - - $uri = "/deviceManagement/configurationPolicies" - $response = Invoke-MtGraphRequest -RelativeUri $uri -ApiVersion $ApiVersion - - if ($response -and $response.value) { - foreach ($policy in $response.value) { - if ($policy.displayName -like "*LAPS*" -and $policy.odataType -eq "#microsoft.graph.deviceManagementConfigurationPolicy") { - Add-MtTestResultDetail -Name "LAPS Policy Found" -Detail $policy.displayName - return $true - } - } - } - - Add-MtTestResultDetail -Name "LAPS Policy Missing" -Detail "No LAPS Configuration policy found." - return $false -} \ No newline at end of file diff --git a/functions/Test-MtIntuneManagedInstallerRules.ps1 b/functions/Test-MtIntuneManagedInstallerRules.ps1 deleted file mode 100644 index 86c630a9d..000000000 --- a/functions/Test-MtIntuneManagedInstallerRules.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -function Test-MtIntuneManagedInstallerRules { - param ( - [string]$ApiVersion = "v1.0" - ) - - $uri = "/deviceManagement/intents" - $response = Invoke-MtGraphRequest -RelativeUri $uri -ApiVersion $ApiVersion - - if ($response -and $response.value) { - foreach ($intent in $response.value) { - if ($intent.displayName -like "*Managed Installer*" -and $intent.odataType -eq "#microsoft.graph.deviceManagementIntent") { - Add-MtTestResultDetail -Name "Managed Installer Rules Found" -Detail $intent.displayName - return $true - } - } - } - - Add-MtTestResultDetail -Name "Managed Installer Rules Missing" -Detail "No Managed Installer Rules found." - return $false -} \ No newline at end of file diff --git a/powershell/Maester.psd1 b/powershell/Maester.psd1 index 016409af1..dbcd7a5cc 100644 --- a/powershell/Maester.psd1 +++ b/powershell/Maester.psd1 @@ -182,6 +182,10 @@ 'Test-MtCertificateConnectors', 'Test-MtFeatureUpdatePolicy', 'Test-MtIntuneDiagnosticSettings', 'Test-MtIntuneRbacGroupsProtected', 'Test-MtBitLockerFullDiskEncryption', + 'Test-MtIntuneLAPSConfiguration', + 'Test-MtIntuneASRRules', + 'Test-MtIntuneAppControl', + 'Test-MtIntuneManagedInstallerRules', 'Test-MtMdmAuthority', 'Test-MtMobileThreatDefenseConnectors', 'Test-MtTenantCustomization', 'Test-MtWindowsDataProcessor', 'Test-MtXspmCriticalCredsOnDevicesWithNonCriticalAccounts', diff --git a/powershell/public/maester/intune/Test-MtIntuneASRRules.md b/powershell/public/maester/intune/Test-MtIntuneASRRules.md new file mode 100644 index 000000000..f9605be18 --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.md @@ -0,0 +1,56 @@ +Ensure at least one Intune Attack Surface Reduction (ASR) policy has rules configured in **Block** or **Audit** mode. + +ASR rules reduce the attack surface of applications by preventing behaviors commonly abused by malware and threat actors. These rules target specific techniques such as: + +- **Office macros** spawning child processes or injecting code into other processes +- **Credential theft** from LSASS (Local Security Authority Subsystem Service) +- **Script-based attacks** using obfuscated JavaScript, VBScript, or PowerShell +- **Email-borne threats** executing content from Outlook or webmail +- **Ransomware** advanced protection heuristics +- **USB-based attacks** running untrusted unsigned processes +- **Persistence** through WMI event subscriptions + +Each ASR rule can operate in one of three modes: + +- **Block**: Actively prevents the behavior (recommended for production after testing) +- **Audit**: Logs the event without blocking (recommended for initial rollout) +- **Disabled**: Rule is not active + +The test passes if at least one ASR policy has at least one rule configured in **Block** or **Audit** mode. Policies with all rules in **Audit** mode will trigger an informational note recommending a transition to **Block** mode. + +#### Remediation action: + +1. Navigate to [Microsoft Intune admin center](https://intune.microsoft.com). +2. Go to **Endpoint security** > **Attack surface reduction**. +3. Click **+ Create policy**. +4. Set **Platform** to **Windows 10 and later** and **Profile** to **Attack Surface Reduction Rules**. +5. Enter a policy name (e.g., "ASR Rules - Audit Mode"). +6. Configure individual ASR rules — start with **Audit** mode for all rules: + - Block abuse of exploited vulnerable signed drivers + - Block Adobe Reader from creating child processes + - Block all Office applications from creating child processes + - Block credential stealing from Windows LSASS + - Block executable content from email client and webmail + - Block executable files unless they meet prevalence, age, or trusted list criteria + - Block execution of potentially obfuscated scripts + - Block JavaScript or VBScript from launching downloaded executable content + - Block Office applications from creating executable content + - Block Office applications from injecting code into other processes + - Block Office communication app from creating child processes + - Block persistence through WMI event subscription + - Block process creations originating from PSExec and WMI commands + - Block untrusted and unsigned processes that run from USB + - Block Win32 API calls from Office macros + - Use advanced protection against ransomware +7. Assign the policy to your device groups and click **Create**. +8. Monitor audit events in **Microsoft Defender for Endpoint** > **Reports** > **Attack surface reduction rules** for 2–4 weeks before transitioning rules to **Block** mode. + +#### Related links + +- [Microsoft Intune - Attack Surface Reduction](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/asr) +- [Microsoft Learn - ASR rules reference](https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-reference) +- [Microsoft Learn - Enable ASR rules in Intune](https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/enable-attack-surface-reduction) +- [Microsoft Learn - ASR rules deployment guide](https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment) + + +%TestResult% diff --git a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 new file mode 100644 index 000000000..e1c488760 --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 @@ -0,0 +1,167 @@ +function Test-MtIntuneASRRules { + <# + .SYNOPSIS + Ensure at least one Intune Attack Surface Reduction (ASR) policy has rules configured in Block or Audit mode. + + .DESCRIPTION + Checks Intune Endpoint Security Attack Surface Reduction policies (configurationPolicies API) for + ASR rule configurations. + + ASR rules reduce the attack surface of applications by preventing behaviors commonly abused by malware, + such as Office macros spawning child processes, credential theft from LSASS, or execution of obfuscated scripts. + + Each ASR rule can be set to one of three modes: + - Block: Actively prevents the behavior (recommended for production) + - Audit: Logs the event without blocking (recommended for testing) + - Disabled/Not configured: Rule is inactive + + This test queries Endpoint Security ASR policies filtered by templateFamily 'endpointSecurityAttackSurfaceReduction' + and inspects each rule's enforcement state. The test passes if at least one ASR policy has one or more rules + configured in Block or Audit mode. + + .EXAMPLE + Test-MtIntuneASRRules + + Returns true if at least one ASR policy has rules configured in Block or Audit mode. + + .LINK + https://maester.dev/docs/commands/Test-MtIntuneASRRules + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = 'ASR Rules is the official Microsoft product name')] + [CmdletBinding()] + [OutputType([bool])] + param() + + if (-not (Get-MtLicenseInformation -Product Intune)) { + Add-MtTestResultDetail -SkippedBecause NotLicensedIntune + return $null + } + + try { + Write-Verbose "Querying Intune ASR policies..." + $asrPolicies = @(Invoke-MtGraphRequest -RelativeUri "deviceManagement/configurationPolicies?`$filter=templateReference/templateFamily eq 'endpointSecurityAttackSurfaceReduction'&`$select=id,name,description,templateReference" -ApiVersion beta) + + Write-Verbose "Found $($asrPolicies.Count) ASR policies." + + if ($asrPolicies.Count -eq 0) { + $testResultMarkdown = "No Endpoint Security Attack Surface Reduction policies found in Intune.`n`n" + $testResultMarkdown += "Create an ASR policy under **Endpoint Security > Attack Surface Reduction** with " + $testResultMarkdown += "ASR rules enabled in **Audit** or **Block** mode to protect against common attack techniques." + Add-MtTestResultDetail -Result $testResultMarkdown + return $false + } + + # Friendly names for ASR rules (extracted from setting definition IDs) + $asrRuleNames = @{ + 'blockexecutionofpotentiallyobfuscatedscripts' = 'Block execution of potentially obfuscated scripts' + 'blockwin32apicallsfromofficemacros' = 'Block Win32 API calls from Office macros' + 'blockexecutablefilesrunningunlesstheymeetprevalenceagetrustedlistcriterion' = 'Block executable files unless they meet prevalence/age/trusted list criteria' + 'blockofficecommunicationappfromcreatingchildprocesses' = 'Block Office communication app from creating child processes' + 'blockallofficeapplicationsfromcreatingchildprocesses' = 'Block all Office applications from creating child processes' + 'blockadobereaderfromcreatingchildprocesses' = 'Block Adobe Reader from creating child processes' + 'blockcredentialstealingfromwindowslocalsecurityauthoritysubsystem' = 'Block credential stealing from LSASS' + 'blockjavascriptorvbscriptfromlaunchingdownloadedexecutablecontent' = 'Block JavaScript/VBScript from launching downloaded executable content' + 'blockuntrustedunsignedprocessesthatrunfromusb' = 'Block untrusted/unsigned processes from USB' + 'blockpersistencethroughwmieventsubscription' = 'Block persistence through WMI event subscription' + 'blockuseofcopiedorimpersonatedsystemtools' = 'Block use of copied or impersonated system tools' + 'blockabuseofexploitedvulnerablesigneddrivers' = 'Block abuse of exploited vulnerable signed drivers' + 'blockprocesscreationsfrompsexecandwmicommands' = 'Block process creations from PSExec and WMI commands' + 'blockofficeapplicationsfromcreatingexecutablecontent' = 'Block Office applications from creating executable content' + 'blockofficeapplicationsfrominjectingcodeintootherprocesses' = 'Block Office applications from injecting code into other processes' + 'blockrebootingmachineinsafemode' = 'Block rebooting machine in Safe Mode' + 'useadvancedprotectionagainstransomware' = 'Use advanced protection against ransomware' + 'blockexecutablecontentfromemailclientandwebmail' = 'Block executable content from email client and webmail' + 'blockwebshellcreationforservers' = 'Block webshell creation for servers' + } + + $policyResults = [System.Collections.Generic.List[hashtable]]::new() + $hasActiveRules = $false + + foreach ($policy in $asrPolicies) { + Write-Verbose "Checking ASR policy: $($policy.name) ($($policy.id))" + $settingsUri = "deviceManagement/configurationPolicies('$($policy.id)')/settings?`$expand=settingDefinitions&`$top=1000" + $settingsResponse = @(Invoke-MtGraphRequest -RelativeUri $settingsUri -ApiVersion beta) + + $blockCount = 0 + $auditCount = 0 + $disabledCount = 0 + $ruleDetails = [System.Collections.Generic.List[hashtable]]::new() + + foreach ($setting in $settingsResponse) { + $defId = $setting.settingInstance.settingDefinitionId + if ($defId -ne 'device_vendor_msft_policy_config_defender_attacksurfacereductionrules') { continue } + + # ASR rules are stored in a groupSettingCollectionValue + foreach ($group in $setting.settingInstance.groupSettingCollectionValue) { + foreach ($child in $group.children) { + $childDefId = $child.settingDefinitionId + $val = $child.choiceSettingValue.value + + # Extract rule name from definition ID + $ruleSuffix = $childDefId -replace '^device_vendor_msft_policy_config_defender_attacksurfacereductionrules_', '' + $friendlyName = if ($asrRuleNames.ContainsKey($ruleSuffix)) { $asrRuleNames[$ruleSuffix] } else { $ruleSuffix } + + # Determine enforcement mode from value suffix + $mode = 'Not configured' + if ($val -like '*_block') { $mode = 'Block'; $blockCount++; $hasActiveRules = $true } + elseif ($val -like '*_audit') { $mode = 'Audit'; $auditCount++; $hasActiveRules = $true } + elseif ($val -like '*_warn') { $mode = 'Warn'; $hasActiveRules = $true } + elseif ($val -like '*_off') { $mode = 'Disabled'; $disabledCount++ } + + Write-Verbose " Rule: $friendlyName = $mode" + $ruleDetails.Add(@{ Name = $friendlyName; Mode = $mode }) + } + } + } + + $policyResults.Add(@{ + Name = $policy.name + BlockCount = $blockCount + AuditCount = $auditCount + DisabledCount = $disabledCount + TotalRules = $ruleDetails.Count + Rules = $ruleDetails + }) + } + + # Build result markdown + $testResultMarkdown = "Found $($asrPolicies.Count) Attack Surface Reduction policy/policies in Intune.`n`n" + + foreach ($p in $policyResults) { + $testResultMarkdown += "### $($p.Name)`n" + $testResultMarkdown += "**$($p.TotalRules) rules:** $($p.BlockCount) Block, $($p.AuditCount) Audit, $($p.DisabledCount) Disabled`n`n" + $testResultMarkdown += "| Rule | Mode |`n| --- | --- |`n" + foreach ($r in $p.Rules) { + $testResultMarkdown += "| $($r.Name) | $($r.Mode) |`n" + } + $testResultMarkdown += "`n" + } + + if ($hasActiveRules) { + $testResultMarkdown += "**Result:** At least one ASR policy has rules in **Block** or **Audit** mode." + + # Warn about audit-only policies + $auditOnly = @($policyResults | Where-Object { $_.BlockCount -eq 0 -and $_.AuditCount -gt 0 }) + if ($auditOnly.Count -gt 0) { + $testResultMarkdown += "`n`n> **Note:** $($auditOnly.Count) policy/policies have rules in **Audit mode only**. " + $testResultMarkdown += "Consider transitioning tested rules to **Block** mode for active protection." + } + + Add-MtTestResultDetail -Result $testResultMarkdown + return $true + } else { + $testResultMarkdown += "**Result:** No ASR rules are configured in Block or Audit mode.`n`n" + $testResultMarkdown += "> **Risk:** Without active ASR rules, endpoints are vulnerable to common attack techniques " + $testResultMarkdown += "such as Office macro abuse, credential theft, and script-based attacks." + Add-MtTestResultDetail -Result $testResultMarkdown + return $false + } + } catch { + if ($_.Exception.Response -and $_.Exception.Response.StatusCode -in @(401, 403)) { + Add-MtTestResultDetail -SkippedBecause NotAuthorized + } else { + Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_ + } + return $null + } +} diff --git a/powershell/public/maester/intune/Test-MtIntuneAppControl.md b/powershell/public/maester/intune/Test-MtIntuneAppControl.md new file mode 100644 index 000000000..613a1045d --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneAppControl.md @@ -0,0 +1,38 @@ +Ensure at least one Intune **App Control for Business** (formerly Windows Defender Application Control / WDAC) policy is configured. + +App Control for Business restricts which applications and drivers are allowed to run on Windows devices using code integrity policies. This is one of the most effective defenses against malware, ransomware, and unauthorized software because it blocks untrusted executables from running at all — even if they bypass antivirus detection. + +Key settings this test evaluates: + +- **Build Options**: Whether the policy uses built-in controls or a custom uploaded policy +- **Audit Mode**: Whether the policy is in audit mode (logging only) or enforce mode (blocking) +- **Managed Installer**: Whether apps deployed via Intune/SCCM are automatically trusted +- **Intelligent Security Graph (ISG) Reputation**: Whether apps with good reputation scores are trusted + +The test passes if at least one App Control for Business policy exists with built-in controls or a custom policy configured. Policies in **Audit mode** will trigger an informational note recommending a transition to **Enforce mode** after validation. + +#### Remediation action: + +1. Navigate to [Microsoft Intune admin center](https://intune.microsoft.com). +2. Go to **Endpoint security** > **Application control**. +3. Click **+ Create policy**. +4. Set **Platform** to **Windows 10 and later** and **Profile** to **App Control for Business**. +5. Enter a policy name (e.g., "App Control - Audit Mode"). +6. Configure the following settings: + - **App Control for Business**: Select **Built-in controls** + - **Audit mode**: **Enabled** (start in audit mode to identify blocked apps) + - **Trust apps from managed installer**: **Enabled** (trusts Intune-deployed apps) + - **Trust apps with good reputation**: **Disabled** (optional — ISG adds convenience but reduces strictness) +7. Assign the policy to a test device group first. +8. Monitor blocked/audited apps in **Microsoft Defender for Endpoint** > **Reports** > **Application control**. +9. After validating that legitimate apps are not being blocked, transition to **Enforce mode**. + +#### Related links + +- [Microsoft Intune - Application Control](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/asr) +- [Microsoft Learn - App Control for Business in Intune](https://learn.microsoft.com/en-us/mem/intune/protect/endpoint-security-app-control-policy) +- [Microsoft Learn - Application Control for Windows](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/appcontrol) +- [Microsoft Learn - Managed Installer and ISG options](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/configure-appcontrol-managed-installer) + + +%TestResult% diff --git a/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 b/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 new file mode 100644 index 000000000..d80d68573 --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 @@ -0,0 +1,148 @@ +function Test-MtIntuneAppControl { + <# + .SYNOPSIS + Ensure at least one Intune App Control for Business policy is configured. + + .DESCRIPTION + Checks Intune Endpoint Security Application Control policies (configurationPolicies API) for + App Control for Business (formerly WDAC) configurations. + + App Control for Business restricts which applications and drivers are allowed to run on Windows devices, + using code integrity policies to block untrusted executables. This is one of the most effective defenses + against malware, ransomware, and unauthorized software. + + Key settings evaluated: + - BuildOptions: Whether built-in controls are selected + - AuditMode: Whether the policy is in audit mode (logging only) or enforce mode + - TrustAppsFromManagedInstaller: Whether apps deployed via Intune/SCCM are automatically trusted + - TrustAppsWithGoodReputation: Whether ISG (Intelligent Security Graph) reputation is used + + The test passes if at least one App Control for Business policy exists with built-in controls configured. + + .EXAMPLE + Test-MtIntuneAppControl + + Returns true if at least one App Control for Business policy is configured. + + .LINK + https://maester.dev/docs/commands/Test-MtIntuneAppControl + #> + [CmdletBinding()] + [OutputType([bool])] + param() + + if (-not (Get-MtLicenseInformation -Product Intune)) { + Add-MtTestResultDetail -SkippedBecause NotLicensedIntune + return $null + } + + try { + Write-Verbose "Querying Intune App Control for Business policies..." + $appControlPolicies = @(Invoke-MtGraphRequest -RelativeUri "deviceManagement/configurationPolicies?`$filter=templateReference/templateFamily eq 'endpointSecurityApplicationControl'&`$select=id,name,description,templateReference" -ApiVersion beta) + + Write-Verbose "Found $($appControlPolicies.Count) App Control policies." + + if ($appControlPolicies.Count -eq 0) { + $testResultMarkdown = "No Endpoint Security App Control for Business policies found in Intune.`n`n" + $testResultMarkdown += "Create an App Control policy under **Endpoint Security > Application Control** to restrict " + $testResultMarkdown += "which applications and drivers are allowed to run on managed devices." + Add-MtTestResultDetail -Result $testResultMarkdown + return $false + } + + # Setting definition IDs + $buildOptionsId = 'device_vendor_msft_policy_config_applicationcontrolv2_buildoptions' + $auditModeId = 'device_vendor_msft_policy_config_applicationcontrolv2_auditmode' + $managedInstallerId = 'device_vendor_msft_policy_config_applicationcontrolv2_trustappsfrommanagedinstaller' + $goodReputationId = 'device_vendor_msft_policy_config_applicationcontrolv2_trustappswithgoodreputation' + + $policyResults = [System.Collections.Generic.List[hashtable]]::new() + $hasConfiguredPolicy = $false + + foreach ($policy in $appControlPolicies) { + Write-Verbose "Checking App Control policy: $($policy.name) ($($policy.id))" + $settingsUri = "deviceManagement/configurationPolicies('$($policy.id)')/settings?`$expand=settingDefinitions&`$top=1000" + $settingsResponse = @(Invoke-MtGraphRequest -RelativeUri $settingsUri -ApiVersion beta) + + $policyDetail = @{ + Name = $policy.name + BuildOptions = 'Not configured' + AuditMode = 'Not configured' + ManagedInstaller = 'Not configured' + GoodReputation = 'Not configured' + } + + foreach ($setting in $settingsResponse) { + $defId = $setting.settingInstance.settingDefinitionId + + if ($defId -eq $buildOptionsId) { + $val = $setting.settingInstance.choiceSettingValue.value + if ($val -like '*built_in_controls_selected') { + $policyDetail.BuildOptions = 'Built-in controls' + $hasConfiguredPolicy = $true + } elseif ($val -like '*upload_policy_selected') { + $policyDetail.BuildOptions = 'Custom policy upload' + $hasConfiguredPolicy = $true + } + Write-Verbose " BuildOptions: $($policyDetail.BuildOptions)" + + # Child settings are nested under the build options choice + foreach ($child in $setting.settingInstance.choiceSettingValue.children) { + if ($child.settingDefinitionId -eq $auditModeId) { + $childVal = $child.choiceSettingValue.value + $policyDetail.AuditMode = if ($childVal -like '*_enabled') { 'Enabled (audit only)' } else { 'Disabled (enforcing)' } + Write-Verbose " AuditMode: $($policyDetail.AuditMode)" + } + if ($child.settingDefinitionId -eq $managedInstallerId) { + $childVal = $child.choiceSettingValue.value + $policyDetail.ManagedInstaller = if ($childVal -like '*_enabled') { 'Enabled' } else { 'Disabled' } + Write-Verbose " ManagedInstaller: $($policyDetail.ManagedInstaller)" + } + if ($child.settingDefinitionId -eq $goodReputationId) { + $childVal = $child.choiceSettingValue.value + $policyDetail.GoodReputation = if ($childVal -like '*_enabled') { 'Enabled' } else { 'Disabled' } + Write-Verbose " GoodReputation: $($policyDetail.GoodReputation)" + } + } + } + } + + $policyResults.Add($policyDetail) + } + + # Build result markdown + $testResultMarkdown = "Found $($appControlPolicies.Count) App Control for Business policy/policies in Intune.`n`n" + $testResultMarkdown += "| Policy | Build Options | Audit Mode | Managed Installer | ISG Reputation |`n" + $testResultMarkdown += "| --- | --- | --- | --- | --- |`n" + foreach ($p in $policyResults) { + $testResultMarkdown += "| $($p.Name) | $($p.BuildOptions) | $($p.AuditMode) | $($p.ManagedInstaller) | $($p.GoodReputation) |`n" + } + + if ($hasConfiguredPolicy) { + $testResultMarkdown += "`n**Result:** At least one App Control for Business policy is configured." + + # Warn about audit-only policies + $auditOnly = @($policyResults | Where-Object { $_.AuditMode -eq 'Enabled (audit only)' }) + if ($auditOnly.Count -gt 0 -and $auditOnly.Count -eq $policyResults.Count) { + $testResultMarkdown += "`n`n> **Note:** All App Control policies are in **Audit mode**. " + $testResultMarkdown += "Consider transitioning to **Enforce mode** after validating that legitimate applications are not blocked." + } + + Add-MtTestResultDetail -Result $testResultMarkdown + return $true + } else { + $testResultMarkdown += "`n**Result:** No App Control policies have active configurations.`n`n" + $testResultMarkdown += "> **Risk:** Without App Control, any executable can run on managed devices, " + $testResultMarkdown += "leaving them vulnerable to malware, ransomware, and unauthorized software." + Add-MtTestResultDetail -Result $testResultMarkdown + return $false + } + } catch { + if ($_.Exception.Response -and $_.Exception.Response.StatusCode -in @(401, 403)) { + Add-MtTestResultDetail -SkippedBecause NotAuthorized + } else { + Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_ + } + return $null + } +} diff --git a/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.md b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.md new file mode 100644 index 000000000..78eacbb42 --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.md @@ -0,0 +1,39 @@ +Ensure at least one Intune LAPS policy is configured to **back up local admin passwords to Microsoft Entra ID**. + +Windows LAPS (Local Administrator Password Solution) automatically rotates and backs up local administrator passwords on managed devices. Without LAPS, local admin accounts often share the same password across all devices — if one device is compromised, an attacker can move laterally to every other device using the same credentials. + +Key settings this test evaluates: + +- **Backup Directory**: Must be set to **Azure AD only** (Entra ID) to store passwords in the cloud where they can be retrieved by authorized admins. +- **Password Complexity**: Recommended setting is large + small letters + numbers + special characters. +- **Password Length**: Recommended minimum of 14 characters. +- **Post-Authentication Actions**: What happens after the LAPS password is used (reset, logoff, terminate processes). +- **Automatic Account Management**: Whether LAPS auto-manages the local admin account lifecycle. + +The test passes if at least one LAPS policy has **Backup Directory** set to **Azure AD only** (Entra ID). + +#### Remediation action: + +1. Navigate to [Microsoft Intune admin center](https://intune.microsoft.com). +2. Go to **Endpoint security** > **Account protection**. +3. Click **+ Create policy**. +4. Set **Platform** to **Windows 10 and later** and **Profile** to **Local admin password solution (Windows LAPS)**. +5. Enter a policy name (e.g., "LAPS - Entra ID Backup"). +6. Configure the following settings: + - **Backup Directory**: **Azure AD only** + - **Password Complexity**: **Large letters + small letters + numbers + special characters** + - **Password Length**: **21** (or at least 14) + - **Post-Authentication Actions**: **Reset password and logoff** + - **Post-Authentication Reset Delay**: **1 hour** + - **Administrator Account Name**: Leave default or specify custom account +7. Assign the policy to your device groups and click **Create**. + +#### Related links + +- [Microsoft Intune - Endpoint Security Account Protection](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/accountprotection) +- [Microsoft Learn - Windows LAPS with Microsoft Intune](https://learn.microsoft.com/en-us/mem/intune/protect/windows-laps-overview) +- [Microsoft Learn - Windows LAPS CSP reference](https://learn.microsoft.com/en-us/windows/client-management/mdm/laps-csp) +- [CIS Benchmark - Ensure LAPS is configured for local admin accounts](https://www.cisecurity.org/benchmark/microsoft_intune_for_windows) + + +%TestResult% diff --git a/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 new file mode 100644 index 000000000..4b059db5c --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 @@ -0,0 +1,178 @@ +function Test-MtIntuneLAPSConfiguration { + <# + .SYNOPSIS + Ensure at least one Intune LAPS policy is configured to back up local admin passwords to Entra ID. + + .DESCRIPTION + Checks Intune Endpoint Security Account Protection policies (configurationPolicies API) for Windows LAPS + profiles that back up local administrator passwords to Microsoft Entra ID (Azure AD). + + Windows LAPS (Local Administrator Password Solution) automatically rotates and backs up local admin + passwords, preventing lateral movement attacks that exploit shared or stale local admin credentials. + + Key settings evaluated: + - BackupDirectory: Must be set to 1 (Entra ID) to store passwords in the cloud. + - PasswordComplexity: Recommended 4 (large letters + small letters + numbers + special characters) or 8. + - PasswordLength: Recommended >= 14 characters. + - AutomaticAccountManagementEnabled: Whether LAPS auto-manages the local admin account. + + The test passes if at least one LAPS policy is configured with BackupDirectory set to Entra ID. + + .EXAMPLE + Test-MtIntuneLAPSConfiguration + + Returns true if at least one LAPS policy backs up passwords to Entra ID. + + .LINK + https://maester.dev/docs/commands/Test-MtIntuneLAPSConfiguration + #> + [CmdletBinding()] + [OutputType([bool])] + param() + + if (-not (Get-MtLicenseInformation -Product Intune)) { + Add-MtTestResultDetail -SkippedBecause NotLicensedIntune + return $null + } + + try { + # LAPS policies live under the endpointSecurityAccountProtection template family. + # We filter by the LAPS-specific templateId prefix to exclude WHfB and other account protection policies. + Write-Verbose "Querying Intune LAPS configuration policies..." + $accountProtectionPolicies = @(Invoke-MtGraphRequest -RelativeUri "deviceManagement/configurationPolicies?`$filter=templateReference/templateFamily eq 'endpointSecurityAccountProtection'&`$select=id,name,description,templateReference" -ApiVersion beta) + + # Filter to LAPS policies only (templateId starts with the LAPS template) + $lapsTemplateId = 'adc46e5a-f4aa-4ff6-aeff-4f27bc525796' + $lapsPolicies = @($accountProtectionPolicies | Where-Object { $_.templateReference.templateId -like "$lapsTemplateId*" }) + + Write-Verbose "Found $($accountProtectionPolicies.Count) Account Protection policies, $($lapsPolicies.Count) are LAPS policies." + + if ($lapsPolicies.Count -eq 0) { + $testResultMarkdown = "No Windows LAPS policies found in Intune.`n`n" + $testResultMarkdown += "Create a LAPS policy under **Endpoint Security > Account Protection** with " + $testResultMarkdown += "**Backup Directory** set to **Azure AD only** to store local admin passwords in the cloud." + Add-MtTestResultDetail -Result $testResultMarkdown + return $false + } + + # LAPS setting definition IDs + $backupDirectoryId = 'device_vendor_msft_laps_policies_backupdirectory' + $passwordComplexityId = 'device_vendor_msft_laps_policies_passwordcomplexity' + $passwordLengthId = 'device_vendor_msft_laps_policies_passwordlength' + $postAuthActionsId = 'device_vendor_msft_laps_policies_postauthenticationactions' + $postAuthDelayId = 'device_vendor_msft_laps_policies_postauthenticationresetdelay' + $autoAccountMgmtId = 'device_vendor_msft_laps_policies_automaticaccountmanagementenabled' + + $backupDirectoryLabels = @{ + '_0' = 'Disabled' + '_1' = 'Azure AD only' + '_2' = 'Active Directory' + } + + $complexityLabels = @{ + '_1' = 'Large letters' + '_2' = 'Large + small letters' + '_3' = 'Large + small + numbers' + '_4' = 'Large + small + numbers + special' + '_8' = 'Large + small + numbers + special (improved)' + } + + $policyResults = [System.Collections.Generic.List[hashtable]]::new() + $hasEntraBackup = $false + + foreach ($policy in $lapsPolicies) { + Write-Verbose "Checking LAPS policy: $($policy.name) ($($policy.id))" + $settingsUri = "deviceManagement/configurationPolicies('$($policy.id)')/settings?`$expand=settingDefinitions&`$top=1000" + $settingsResponse = @(Invoke-MtGraphRequest -RelativeUri $settingsUri -ApiVersion beta) + + $policyDetail = @{ + Name = $policy.name + BackupDirectory = 'Not configured' + PasswordComplexity = 'Not configured' + PasswordLength = 'Not configured' + PostAuthActions = 'Not configured' + PostAuthDelay = 'Not configured' + AutoAccountMgmt = 'Not configured' + } + + foreach ($setting in $settingsResponse) { + $defId = $setting.settingInstance.settingDefinitionId + + if ($defId -eq $backupDirectoryId) { + $val = $setting.settingInstance.choiceSettingValue.value + foreach ($suffix in $backupDirectoryLabels.Keys) { + if ($val.EndsWith($suffix)) { + $policyDetail.BackupDirectory = $backupDirectoryLabels[$suffix] + if ($suffix -eq '_1') { $hasEntraBackup = $true } + } + } + Write-Verbose " BackupDirectory: $($policyDetail.BackupDirectory)" + } + + if ($defId -eq $passwordComplexityId) { + $val = $setting.settingInstance.choiceSettingValue.value + foreach ($suffix in $complexityLabels.Keys) { + if ($val.EndsWith($suffix)) { + $policyDetail.PasswordComplexity = $complexityLabels[$suffix] + } + } + Write-Verbose " PasswordComplexity: $($policyDetail.PasswordComplexity)" + } + + if ($defId -eq $passwordLengthId) { + $policyDetail.PasswordLength = "$($setting.settingInstance.simpleSettingValue.value) characters" + Write-Verbose " PasswordLength: $($policyDetail.PasswordLength)" + } + + if ($defId -eq $postAuthActionsId) { + $val = $setting.settingInstance.choiceSettingValue.value + if ($val -like '*_1') { $policyDetail.PostAuthActions = 'Reset password' } + elseif ($val -like '*_3') { $policyDetail.PostAuthActions = 'Reset password + logoff' } + elseif ($val -like '*_5') { $policyDetail.PostAuthActions = 'Reset password + reboot' } + elseif ($val -like '*_11') { $policyDetail.PostAuthActions = 'Reset password + logoff + terminate processes' } + Write-Verbose " PostAuthActions: $($policyDetail.PostAuthActions)" + } + + if ($defId -eq $postAuthDelayId) { + $policyDetail.PostAuthDelay = "$($setting.settingInstance.simpleSettingValue.value) hour(s)" + Write-Verbose " PostAuthDelay: $($policyDetail.PostAuthDelay)" + } + + if ($defId -eq $autoAccountMgmtId) { + $val = $setting.settingInstance.choiceSettingValue.value + $policyDetail.AutoAccountMgmt = if ($val -like '*_true') { 'Enabled' } else { 'Disabled' } + Write-Verbose " AutoAccountMgmt: $($policyDetail.AutoAccountMgmt)" + } + } + + $policyResults.Add($policyDetail) + } + + # Build result markdown + $testResultMarkdown = "Found $($lapsPolicies.Count) Windows LAPS policy/policies in Intune.`n`n" + $testResultMarkdown += "| Policy | Backup Directory | Complexity | Length | Post-Auth Actions | Auto Account Mgmt |`n" + $testResultMarkdown += "| --- | --- | --- | --- | --- | --- |`n" + foreach ($p in $policyResults) { + $testResultMarkdown += "| $($p.Name) | $($p.BackupDirectory) | $($p.PasswordComplexity) | $($p.PasswordLength) | $($p.PostAuthActions) | $($p.AutoAccountMgmt) |`n" + } + + if ($hasEntraBackup) { + $testResultMarkdown += "`n**Result:** At least one LAPS policy backs up passwords to **Entra ID**." + Add-MtTestResultDetail -Result $testResultMarkdown + return $true + } else { + $testResultMarkdown += "`n**Result:** No LAPS policy is configured to back up passwords to **Entra ID**.`n`n" + $testResultMarkdown += "> **Risk:** Without cloud backup of local admin passwords, compromised or forgotten local admin credentials " + $testResultMarkdown += "cannot be recovered or rotated centrally, increasing lateral movement risk." + Add-MtTestResultDetail -Result $testResultMarkdown + return $false + } + } catch { + if ($_.Exception.Response -and $_.Exception.Response.StatusCode -in @(401, 403)) { + Add-MtTestResultDetail -SkippedBecause NotAuthorized + } else { + Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_ + } + return $null + } +} diff --git a/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.md b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.md new file mode 100644 index 000000000..933d7b598 --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.md @@ -0,0 +1,38 @@ +Ensure at least one Intune App Control for Business policy has **Managed Installer** enabled. + +When Managed Installer is enabled in an App Control for Business policy, applications deployed through Intune (or SCCM) are automatically trusted and allowed to run without needing explicit allow rules in the code integrity policy. This dramatically simplifies App Control deployment in enterprise environments. + +**Without Managed Installer:** +- Every application must have an explicit allow rule in the App Control policy +- Line-of-business (LOB) apps deployed via Intune may be blocked unexpectedly +- Help desk tickets increase due to false positives from legitimate software being blocked +- IT teams must maintain complex allow lists that change with every app update + +**With Managed Installer:** +- Apps deployed through Intune are automatically whitelisted at install time +- Only user-installed, sideloaded, or internet-downloaded apps are subject to policy restrictions +- Reduces false positives while maintaining security against unauthorized software +- Simplifies ongoing policy maintenance + +The test passes if at least one App Control for Business policy has the **Trust apps from managed installer** setting enabled. + +#### Remediation action: + +1. Navigate to [Microsoft Intune admin center](https://intune.microsoft.com). +2. Go to **Endpoint security** > **Application control**. +3. Edit an existing App Control for Business policy (or create a new one). +4. Under **App Control for Business**, select **Built-in controls**. +5. Set **Trust apps from managed installer** to **Enabled**. +6. Save and assign the policy to your device groups. + +> **Note:** Managed Installer works by tagging files written by the Intune Management Extension (IME) process. The App Control policy then trusts any file that was installed by a tagged managed installer process. This is transparent to end users. + +#### Related links + +- [Microsoft Intune - Application Control](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/asr) +- [Microsoft Learn - Configure Managed Installer in Intune](https://learn.microsoft.com/en-us/mem/intune/protect/endpoint-security-app-control-policy) +- [Microsoft Learn - Automatically allow apps deployed by a managed installer](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/configure-appcontrol-managed-installer) +- [Microsoft Learn - App Control for Business overview](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/appcontrol) + + +%TestResult% diff --git a/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 new file mode 100644 index 000000000..0d3e0bc9f --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 @@ -0,0 +1,130 @@ +function Test-MtIntuneManagedInstallerRules { + <# + .SYNOPSIS + Ensure at least one Intune App Control for Business policy has Managed Installer enabled. + + .DESCRIPTION + Checks Intune Endpoint Security Application Control policies (configurationPolicies API) for + the "Trust apps from managed installer" setting. + + When Managed Installer is enabled in an App Control for Business policy, applications deployed through + Intune (or SCCM) are automatically trusted and allowed to run without needing explicit allow rules. + This simplifies App Control deployment by ensuring IT-managed software isn't blocked. + + Without Managed Installer: + - Every application must have an explicit allow rule in the policy + - LOB apps deployed via Intune may be blocked unexpectedly + - Help desk tickets increase due to false positives + + With Managed Installer: + - Apps deployed through Intune are automatically whitelisted + - Only user-installed or sideloaded apps are subject to policy restrictions + - Reduces false positives while maintaining security + + The test passes if at least one App Control policy has the "Trust apps from managed installer" setting enabled. + + .EXAMPLE + Test-MtIntuneManagedInstallerRules + + Returns true if at least one App Control policy has Managed Installer enabled. + + .LINK + https://maester.dev/docs/commands/Test-MtIntuneManagedInstallerRules + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = 'Managed Installer Rules is the official Microsoft product name')] + [CmdletBinding()] + [OutputType([bool])] + param() + + if (-not (Get-MtLicenseInformation -Product Intune)) { + Add-MtTestResultDetail -SkippedBecause NotLicensedIntune + return $null + } + + try { + Write-Verbose "Querying Intune App Control for Business policies for Managed Installer setting..." + $appControlPolicies = @(Invoke-MtGraphRequest -RelativeUri "deviceManagement/configurationPolicies?`$filter=templateReference/templateFamily eq 'endpointSecurityApplicationControl'&`$select=id,name,description,templateReference" -ApiVersion beta) + + Write-Verbose "Found $($appControlPolicies.Count) App Control policies." + + if ($appControlPolicies.Count -eq 0) { + $testResultMarkdown = "No Endpoint Security App Control for Business policies found in Intune.`n`n" + $testResultMarkdown += "Create an App Control policy under **Endpoint Security > Application Control** with " + $testResultMarkdown += "**Trust apps from managed installer** enabled to automatically trust Intune-deployed apps." + Add-MtTestResultDetail -Result $testResultMarkdown + return $false + } + + $managedInstallerId = 'device_vendor_msft_policy_config_applicationcontrolv2_trustappsfrommanagedinstaller' + + $policyResults = [System.Collections.Generic.List[hashtable]]::new() + $hasManagedInstaller = $false + + foreach ($policy in $appControlPolicies) { + Write-Verbose "Checking App Control policy: $($policy.name) ($($policy.id))" + $settingsUri = "deviceManagement/configurationPolicies('$($policy.id)')/settings?`$expand=settingDefinitions&`$top=1000" + $settingsResponse = @(Invoke-MtGraphRequest -RelativeUri $settingsUri -ApiVersion beta) + + $policyDetail = @{ + Name = $policy.name + ManagedInstaller = 'Not configured' + AuditMode = 'Not configured' + } + + foreach ($setting in $settingsResponse) { + $defId = $setting.settingInstance.settingDefinitionId + + if ($defId -eq 'device_vendor_msft_policy_config_applicationcontrolv2_buildoptions') { + foreach ($child in $setting.settingInstance.choiceSettingValue.children) { + if ($child.settingDefinitionId -eq $managedInstallerId) { + $childVal = $child.choiceSettingValue.value + if ($childVal -like '*_enabled') { + $policyDetail.ManagedInstaller = 'Enabled' + $hasManagedInstaller = $true + } else { + $policyDetail.ManagedInstaller = 'Disabled' + } + Write-Verbose " ManagedInstaller: $($policyDetail.ManagedInstaller)" + } + if ($child.settingDefinitionId -eq 'device_vendor_msft_policy_config_applicationcontrolv2_auditmode') { + $childVal = $child.choiceSettingValue.value + $policyDetail.AuditMode = if ($childVal -like '*_enabled') { 'Audit' } else { 'Enforce' } + Write-Verbose " AuditMode: $($policyDetail.AuditMode)" + } + } + } + } + + $policyResults.Add($policyDetail) + } + + # Build result markdown + $testResultMarkdown = "Found $($appControlPolicies.Count) App Control for Business policy/policies in Intune.`n`n" + $testResultMarkdown += "| Policy | Managed Installer | Enforcement Mode |`n" + $testResultMarkdown += "| --- | --- | --- |`n" + foreach ($p in $policyResults) { + $testResultMarkdown += "| $($p.Name) | $($p.ManagedInstaller) | $($p.AuditMode) |`n" + } + + if ($hasManagedInstaller) { + $testResultMarkdown += "`n**Result:** At least one App Control policy has **Managed Installer** enabled." + $testResultMarkdown += " Applications deployed through Intune/SCCM will be automatically trusted." + Add-MtTestResultDetail -Result $testResultMarkdown + return $true + } else { + $testResultMarkdown += "`n**Result:** No App Control policies have **Managed Installer** enabled.`n`n" + $testResultMarkdown += "> **Risk:** Without Managed Installer, applications deployed via Intune may be blocked by App Control policies. " + $testResultMarkdown += "This leads to false positives and help desk tickets. Enable 'Trust apps from managed installer' to " + $testResultMarkdown += "automatically trust IT-deployed software." + Add-MtTestResultDetail -Result $testResultMarkdown + return $false + } + } catch { + if ($_.Exception.Response -and $_.Exception.Response.StatusCode -in @(401, 403)) { + Add-MtTestResultDetail -SkippedBecause NotAuthorized + } else { + Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_ + } + return $null + } +} diff --git a/tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 b/tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 index 91a0332f1..e36c6497b 100644 --- a/tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 +++ b/tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 @@ -1,5 +1,5 @@ Describe "Maester/Intune" -Tag "Maester", "Intune", "ASR" { - It "MT.1201: Ensure ASR Rules are configured correctly" -Tag "MT.1201" { + It "MT.1125: Ensure ASR Rules are configured correctly" -Tag "MT.1125" { $result = Test-MtIntuneASRRules if ($null -ne $result) { $result | Should -Be $true -Because "Attack Surface Reduction (ASR) Rules are configured to block threats." diff --git a/tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 b/tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 index d22804bf6..68d64a71e 100644 --- a/tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 +++ b/tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 @@ -1,5 +1,5 @@ Describe "Maester/Intune" -Tag "Maester", "Intune", "AppControl" { - It "MT.1202: Ensure App Control for Business is enabled" -Tag "MT.1202" { + It "MT.1126: Ensure App Control for Business is enabled" -Tag "MT.1126" { $result = Test-MtIntuneAppControl if ($null -ne $result) { $result | Should -Be $true -Because "App Control for Business is enabled in Intune." diff --git a/tests/Maester/Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 b/tests/Maester/Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 index 07207b12b..879e04c26 100644 --- a/tests/Maester/Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 +++ b/tests/Maester/Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 @@ -1,5 +1,5 @@ Describe "Maester/Intune" -Tag "Maester", "Intune", "LAPS" { - It "MT.1200: Ensure LAPS Configuration Policy is properly set" -Tag "MT.1200" { + It "MT.1124: Ensure LAPS Configuration Policy is properly set" -Tag "MT.1124" { $result = Test-MtIntuneLAPSConfiguration if ($null -ne $result) { $result | Should -Be $true -Because "a LAPS Configuration policy is properly set in Intune." diff --git a/tests/Maester/Intune/Test-MtIntuneManagedInstaller.Tests.ps1 b/tests/Maester/Intune/Test-MtIntuneManagedInstaller.Tests.ps1 index 68b30143e..e1c55e44d 100644 --- a/tests/Maester/Intune/Test-MtIntuneManagedInstaller.Tests.ps1 +++ b/tests/Maester/Intune/Test-MtIntuneManagedInstaller.Tests.ps1 @@ -1,5 +1,5 @@ Describe "Maester/Intune" -Tag "Maester", "Intune", "ManagedInstaller" { - It "MT.1203: Ensure Managed Installer Rules are configured correctly" -Tag "MT.1203" { + It "MT.1127: Ensure Managed Installer Rules are configured correctly" -Tag "MT.1127" { $result = Test-MtIntuneManagedInstallerRules if ($null -ne $result) { $result | Should -Be $true -Because "Managed Installer Rules are configured to enforce security." From 171f51181784c15434716255cd292a7fb34baf50 Mon Sep 17 00:00:00 2001 From: Ofir Gavish Date: Sun, 19 Apr 2026 00:20:59 +0300 Subject: [PATCH 03/14] Consolidate 4 Intune tests into Test-MtIntunePlatform.Tests.ps1 Move MT.1124-1127 It blocks into the shared Describe block in Test-MtIntunePlatform.Tests.ps1, matching the upstream Maester pattern. Remove the 4 separate .Tests.ps1 files. --- tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 | 8 -------- tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 | 8 -------- .../Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 | 8 -------- .../Intune/Test-MtIntuneManagedInstaller.Tests.ps1 | 8 -------- 4 files changed, 32 deletions(-) delete mode 100644 tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 delete mode 100644 tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 delete mode 100644 tests/Maester/Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 delete mode 100644 tests/Maester/Intune/Test-MtIntuneManagedInstaller.Tests.ps1 diff --git a/tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 b/tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 deleted file mode 100644 index e36c6497b..000000000 --- a/tests/Maester/Intune/Test-MtIntuneASRRules.Tests.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -Describe "Maester/Intune" -Tag "Maester", "Intune", "ASR" { - It "MT.1125: Ensure ASR Rules are configured correctly" -Tag "MT.1125" { - $result = Test-MtIntuneASRRules - if ($null -ne $result) { - $result | Should -Be $true -Because "Attack Surface Reduction (ASR) Rules are configured to block threats." - } - } -} \ No newline at end of file diff --git a/tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 b/tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 deleted file mode 100644 index 68d64a71e..000000000 --- a/tests/Maester/Intune/Test-MtIntuneAppControl.Tests.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -Describe "Maester/Intune" -Tag "Maester", "Intune", "AppControl" { - It "MT.1126: Ensure App Control for Business is enabled" -Tag "MT.1126" { - $result = Test-MtIntuneAppControl - if ($null -ne $result) { - $result | Should -Be $true -Because "App Control for Business is enabled in Intune." - } - } -} \ No newline at end of file diff --git a/tests/Maester/Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 b/tests/Maester/Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 deleted file mode 100644 index 879e04c26..000000000 --- a/tests/Maester/Intune/Test-MtIntuneLAPSConfiguration.Tests.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -Describe "Maester/Intune" -Tag "Maester", "Intune", "LAPS" { - It "MT.1124: Ensure LAPS Configuration Policy is properly set" -Tag "MT.1124" { - $result = Test-MtIntuneLAPSConfiguration - if ($null -ne $result) { - $result | Should -Be $true -Because "a LAPS Configuration policy is properly set in Intune." - } - } -} \ No newline at end of file diff --git a/tests/Maester/Intune/Test-MtIntuneManagedInstaller.Tests.ps1 b/tests/Maester/Intune/Test-MtIntuneManagedInstaller.Tests.ps1 deleted file mode 100644 index e1c55e44d..000000000 --- a/tests/Maester/Intune/Test-MtIntuneManagedInstaller.Tests.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -Describe "Maester/Intune" -Tag "Maester", "Intune", "ManagedInstaller" { - It "MT.1127: Ensure Managed Installer Rules are configured correctly" -Tag "MT.1127" { - $result = Test-MtIntuneManagedInstallerRules - if ($null -ne $result) { - $result | Should -Be $true -Because "Managed Installer Rules are configured to enforce security." - } - } -} \ No newline at end of file From 5951654b7b76318e44fe2152fb7f8cb7c5c9c497 Mon Sep 17 00:00:00 2001 From: Ofir Gavish Date: Wed, 22 Apr 2026 23:20:32 +0300 Subject: [PATCH 04/14] Renumber tests to MT.1148-MT.1151 (MT.1123-1146 reserved by PR #1017, MT.1147 by f-bader) --- .../Intune/Test-MtIntunePlatform.Tests.ps1 | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 b/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 index 0fa9fb966..5eb1eac6d 100644 --- a/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 +++ b/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 @@ -68,4 +68,32 @@ Describe "Maester/Intune" -Tag "Maester", "Intune" { $result | Should -Be $true -Because "at least one Intune Endpoint Security Disk encryption policy enforces BitLocker full disk encryption." } } + + It "MT.1148: Ensure LAPS Configuration Policy is properly set" -Tag "MT.1148" { + $result = Test-MtIntuneLAPSConfiguration + if ($null -ne $result) { + $result | Should -Be $true -Because "a LAPS Configuration policy is properly set in Intune." + } + } + + It "MT.1149: Ensure ASR Rules are configured correctly" -Tag "MT.1149" { + $result = Test-MtIntuneASRRules + if ($null -ne $result) { + $result | Should -Be $true -Because "Attack Surface Reduction (ASR) Rules are configured to block threats." + } + } + + It "MT.1150: Ensure App Control for Business is enabled" -Tag "MT.1150" { + $result = Test-MtIntuneAppControl + if ($null -ne $result) { + $result | Should -Be $true -Because "App Control for Business is enabled in Intune." + } + } + + It "MT.1151: Ensure Managed Installer Rules are configured correctly" -Tag "MT.1151" { + $result = Test-MtIntuneManagedInstallerRules + if ($null -ne $result) { + $result | Should -Be $true -Because "Managed Installer Rules are configured to enforce security." + } + } } From e51b7b9547d20c799b7bde017fade1d348dd75f5 Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:23:45 -0400 Subject: [PATCH 05/14] Update tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 b/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 index 5eb1eac6d..00ef338bf 100644 --- a/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 +++ b/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 @@ -93,7 +93,7 @@ Describe "Maester/Intune" -Tag "Maester", "Intune" { It "MT.1151: Ensure Managed Installer Rules are configured correctly" -Tag "MT.1151" { $result = Test-MtIntuneManagedInstallerRules if ($null -ne $result) { - $result | Should -Be $true -Because "Managed Installer Rules are configured to enforce security." + $result | Should -Be $true -Because "'Trust apps from managed installer' is enabled in at least one App Control policy." } } } From 12ecb81a8659625b87ba4500de00df146763c818 Mon Sep 17 00:00:00 2001 From: Ofir Gavish Date: Sat, 25 Apr 2026 00:33:50 +0300 Subject: [PATCH 06/14] Address Copilot review feedback on PR #1676 - ASR test: align Because text with Block/Audit acceptance criteria - Managed Installer test: clarify Because text to specific MI requirement - ASR.md: document Warn as a fourth supported mode (informational only) - ASR.ps1: do not treat Warn as an active rule for pass criteria - AppControl.md / ManagedInstallerRules.md: fix portal link to Application control blade - maester-config.json: add MT.1148-MT.1151 severity/title entries --- .../maester/intune/Test-MtIntuneASRRules.md | 5 +++-- .../maester/intune/Test-MtIntuneASRRules.ps1 | 2 +- .../maester/intune/Test-MtIntuneAppControl.md | 2 +- .../Test-MtIntuneManagedInstallerRules.md | 2 +- .../Intune/Test-MtIntunePlatform.Tests.ps1 | 2 +- tests/maester-config.json | 20 +++++++++++++++++++ 6 files changed, 27 insertions(+), 6 deletions(-) diff --git a/powershell/public/maester/intune/Test-MtIntuneASRRules.md b/powershell/public/maester/intune/Test-MtIntuneASRRules.md index f9605be18..bffcc9a3e 100644 --- a/powershell/public/maester/intune/Test-MtIntuneASRRules.md +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.md @@ -10,13 +10,14 @@ ASR rules reduce the attack surface of applications by preventing behaviors comm - **USB-based attacks** running untrusted unsigned processes - **Persistence** through WMI event subscriptions -Each ASR rule can operate in one of three modes: +Each ASR rule can operate in one of four modes: - **Block**: Actively prevents the behavior (recommended for production after testing) - **Audit**: Logs the event without blocking (recommended for initial rollout) +- **Warn**: Warns the user before allowing the behavior to proceed - **Disabled**: Rule is not active -The test passes if at least one ASR policy has at least one rule configured in **Block** or **Audit** mode. Policies with all rules in **Audit** mode will trigger an informational note recommending a transition to **Block** mode. +The test passes if at least one ASR policy has at least one rule configured in **Block** or **Audit** mode. **Warn** is a supported ASR rule state, but it does not satisfy this control's pass criteria. Policies with all rules in **Audit** mode will trigger an informational note recommending a transition to **Block** mode. #### Remediation action: diff --git a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 index e1c488760..f1d65872c 100644 --- a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 @@ -105,7 +105,7 @@ $mode = 'Not configured' if ($val -like '*_block') { $mode = 'Block'; $blockCount++; $hasActiveRules = $true } elseif ($val -like '*_audit') { $mode = 'Audit'; $auditCount++; $hasActiveRules = $true } - elseif ($val -like '*_warn') { $mode = 'Warn'; $hasActiveRules = $true } + elseif ($val -like '*_warn') { $mode = 'Warn' } elseif ($val -like '*_off') { $mode = 'Disabled'; $disabledCount++ } Write-Verbose " Rule: $friendlyName = $mode" diff --git a/powershell/public/maester/intune/Test-MtIntuneAppControl.md b/powershell/public/maester/intune/Test-MtIntuneAppControl.md index 613a1045d..051f015aa 100644 --- a/powershell/public/maester/intune/Test-MtIntuneAppControl.md +++ b/powershell/public/maester/intune/Test-MtIntuneAppControl.md @@ -29,7 +29,7 @@ The test passes if at least one App Control for Business policy exists with buil #### Related links -- [Microsoft Intune - Application Control](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/asr) +- [Microsoft Intune - Application Control](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/applicationControl) - [Microsoft Learn - App Control for Business in Intune](https://learn.microsoft.com/en-us/mem/intune/protect/endpoint-security-app-control-policy) - [Microsoft Learn - Application Control for Windows](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/appcontrol) - [Microsoft Learn - Managed Installer and ISG options](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/configure-appcontrol-managed-installer) diff --git a/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.md b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.md index 933d7b598..60f54ad5d 100644 --- a/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.md +++ b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.md @@ -29,7 +29,7 @@ The test passes if at least one App Control for Business policy has the **Trust #### Related links -- [Microsoft Intune - Application Control](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/asr) +- [Microsoft Intune - Application Control](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/applicationControl) - [Microsoft Learn - Configure Managed Installer in Intune](https://learn.microsoft.com/en-us/mem/intune/protect/endpoint-security-app-control-policy) - [Microsoft Learn - Automatically allow apps deployed by a managed installer](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/configure-appcontrol-managed-installer) - [Microsoft Learn - App Control for Business overview](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/appcontrol) diff --git a/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 b/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 index 00ef338bf..781e0359b 100644 --- a/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 +++ b/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 @@ -79,7 +79,7 @@ Describe "Maester/Intune" -Tag "Maester", "Intune" { It "MT.1149: Ensure ASR Rules are configured correctly" -Tag "MT.1149" { $result = Test-MtIntuneASRRules if ($null -ne $result) { - $result | Should -Be $true -Because "Attack Surface Reduction (ASR) Rules are configured to block threats." + $result | Should -Be $true -Because "Attack Surface Reduction (ASR) Rules are configured in Block or Audit mode." } } diff --git a/tests/maester-config.json b/tests/maester-config.json index f39a92653..d66316ec7 100644 --- a/tests/maester-config.json +++ b/tests/maester-config.json @@ -1354,6 +1354,26 @@ "Severity": "High", "Title": "Ensure BitLocker full disk encryption is configured via Intune" }, + { + "Id": "MT.1148", + "Severity": "High", + "Title": "Ensure LAPS Configuration Policy is properly set" + }, + { + "Id": "MT.1149", + "Severity": "High", + "Title": "Ensure ASR Rules are configured correctly" + }, + { + "Id": "MT.1150", + "Severity": "High", + "Title": "Ensure App Control for Business is enabled" + }, + { + "Id": "MT.1151", + "Severity": "Medium", + "Title": "Ensure Managed Installer Rules are configured correctly" + }, { "Id": "ORCA.100", "Severity": "Medium", From 1ee263b59aa8222b4d6bc10c89d0a9b2d40103ef Mon Sep 17 00:00:00 2001 From: Ofir Gavish Date: Sat, 25 Apr 2026 23:24:47 +0300 Subject: [PATCH 07/14] Address @SamErde review feedback on PR #1676 - Add website docs pages MT.1148-MT.1151 (blocking item) - Add Test-MtConnection Graph guard to all 4 functions - Fix LAPS AutoAccountMgmt to handle both simpleSettingValue and choiceSettingValue - Add 'See https://maester.dev/docs/tests/MT.XXXX' links to all 4 It block titles - Prefix pass result messages with 'Well done.' per Maester style guide - Add break after BackupDirectory suffix match in LAPS test - Track and display Warn rule count in ASR policy summary --- .../maester/intune/Test-MtIntuneASRRules.ps1 | 13 +++-- .../intune/Test-MtIntuneAppControl.ps1 | 7 ++- .../intune/Test-MtIntuneLAPSConfiguration.ps1 | 17 +++++-- .../Test-MtIntuneManagedInstallerRules.ps1 | 7 ++- .../Intune/Test-MtIntunePlatform.Tests.ps1 | 8 +-- website/docs/tests/maester/MT.1148.md | 42 ++++++++++++++++ website/docs/tests/maester/MT.1149.md | 49 +++++++++++++++++++ website/docs/tests/maester/MT.1150.md | 45 +++++++++++++++++ website/docs/tests/maester/MT.1151.md | 45 +++++++++++++++++ 9 files changed, 221 insertions(+), 12 deletions(-) create mode 100644 website/docs/tests/maester/MT.1148.md create mode 100644 website/docs/tests/maester/MT.1149.md create mode 100644 website/docs/tests/maester/MT.1150.md create mode 100644 website/docs/tests/maester/MT.1151.md diff --git a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 index f1d65872c..dc0f8c465 100644 --- a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 @@ -32,6 +32,11 @@ [OutputType([bool])] param() + if (!(Test-MtConnection Graph)) { + Add-MtTestResultDetail -SkippedBecause NotConnectedGraph + return $null + } + if (-not (Get-MtLicenseInformation -Product Intune)) { Add-MtTestResultDetail -SkippedBecause NotLicensedIntune return $null @@ -84,6 +89,7 @@ $blockCount = 0 $auditCount = 0 + $warnCount = 0 $disabledCount = 0 $ruleDetails = [System.Collections.Generic.List[hashtable]]::new() @@ -105,7 +111,7 @@ $mode = 'Not configured' if ($val -like '*_block') { $mode = 'Block'; $blockCount++; $hasActiveRules = $true } elseif ($val -like '*_audit') { $mode = 'Audit'; $auditCount++; $hasActiveRules = $true } - elseif ($val -like '*_warn') { $mode = 'Warn' } + elseif ($val -like '*_warn') { $mode = 'Warn'; $warnCount++ } elseif ($val -like '*_off') { $mode = 'Disabled'; $disabledCount++ } Write-Verbose " Rule: $friendlyName = $mode" @@ -118,6 +124,7 @@ Name = $policy.name BlockCount = $blockCount AuditCount = $auditCount + WarnCount = $warnCount DisabledCount = $disabledCount TotalRules = $ruleDetails.Count Rules = $ruleDetails @@ -129,7 +136,7 @@ foreach ($p in $policyResults) { $testResultMarkdown += "### $($p.Name)`n" - $testResultMarkdown += "**$($p.TotalRules) rules:** $($p.BlockCount) Block, $($p.AuditCount) Audit, $($p.DisabledCount) Disabled`n`n" + $testResultMarkdown += "**$($p.TotalRules) rules:** $($p.BlockCount) Block, $($p.AuditCount) Audit, $($p.WarnCount) Warn, $($p.DisabledCount) Disabled`n`n" $testResultMarkdown += "| Rule | Mode |`n| --- | --- |`n" foreach ($r in $p.Rules) { $testResultMarkdown += "| $($r.Name) | $($r.Mode) |`n" @@ -138,7 +145,7 @@ } if ($hasActiveRules) { - $testResultMarkdown += "**Result:** At least one ASR policy has rules in **Block** or **Audit** mode." + $testResultMarkdown += "**Result:** Well done. At least one ASR policy has rules in **Block** or **Audit** mode." # Warn about audit-only policies $auditOnly = @($policyResults | Where-Object { $_.BlockCount -eq 0 -and $_.AuditCount -gt 0 }) diff --git a/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 b/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 index d80d68573..56bfffa80 100644 --- a/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 @@ -31,6 +31,11 @@ [OutputType([bool])] param() + if (!(Test-MtConnection Graph)) { + Add-MtTestResultDetail -SkippedBecause NotConnectedGraph + return $null + } + if (-not (Get-MtLicenseInformation -Product Intune)) { Add-MtTestResultDetail -SkippedBecause NotLicensedIntune return $null @@ -119,7 +124,7 @@ } if ($hasConfiguredPolicy) { - $testResultMarkdown += "`n**Result:** At least one App Control for Business policy is configured." + $testResultMarkdown += "`n**Result:** Well done. At least one App Control for Business policy is configured." # Warn about audit-only policies $auditOnly = @($policyResults | Where-Object { $_.AuditMode -eq 'Enabled (audit only)' }) diff --git a/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 index 4b059db5c..f11319669 100644 --- a/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 @@ -30,6 +30,11 @@ [OutputType([bool])] param() + if (!(Test-MtConnection Graph)) { + Add-MtTestResultDetail -SkippedBecause NotConnectedGraph + return $null + } + if (-not (Get-MtLicenseInformation -Product Intune)) { Add-MtTestResultDetail -SkippedBecause NotLicensedIntune return $null @@ -104,6 +109,7 @@ if ($val.EndsWith($suffix)) { $policyDetail.BackupDirectory = $backupDirectoryLabels[$suffix] if ($suffix -eq '_1') { $hasEntraBackup = $true } + break } } Write-Verbose " BackupDirectory: $($policyDetail.BackupDirectory)" @@ -139,8 +145,13 @@ } if ($defId -eq $autoAccountMgmtId) { - $val = $setting.settingInstance.choiceSettingValue.value - $policyDetail.AutoAccountMgmt = if ($val -like '*_true') { 'Enabled' } else { 'Disabled' } + # The Automatic Account Management toggle may be returned as a simpleSettingValue + # (boolean true/false) or as a choiceSettingValue depending on how the policy was + # authored. Handle both shapes to avoid silently reporting 'Disabled' when enabled. + $simpleVal = $setting.settingInstance.simpleSettingValue.value + $choiceVal = $setting.settingInstance.choiceSettingValue.value + $val = if ($null -ne $simpleVal) { $simpleVal } else { $choiceVal } + $policyDetail.AutoAccountMgmt = if ($val -eq $true -or $val -like '*_true') { 'Enabled' } else { 'Disabled' } Write-Verbose " AutoAccountMgmt: $($policyDetail.AutoAccountMgmt)" } } @@ -157,7 +168,7 @@ } if ($hasEntraBackup) { - $testResultMarkdown += "`n**Result:** At least one LAPS policy backs up passwords to **Entra ID**." + $testResultMarkdown += "`n**Result:** Well done. At least one LAPS policy backs up passwords to **Entra ID**." Add-MtTestResultDetail -Result $testResultMarkdown return $true } else { diff --git a/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 index 0d3e0bc9f..0258ea945 100644 --- a/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 @@ -36,6 +36,11 @@ [OutputType([bool])] param() + if (!(Test-MtConnection Graph)) { + Add-MtTestResultDetail -SkippedBecause NotConnectedGraph + return $null + } + if (-not (Get-MtLicenseInformation -Product Intune)) { Add-MtTestResultDetail -SkippedBecause NotLicensedIntune return $null @@ -107,7 +112,7 @@ } if ($hasManagedInstaller) { - $testResultMarkdown += "`n**Result:** At least one App Control policy has **Managed Installer** enabled." + $testResultMarkdown += "`n**Result:** Well done. At least one App Control policy has **Managed Installer** enabled." $testResultMarkdown += " Applications deployed through Intune/SCCM will be automatically trusted." Add-MtTestResultDetail -Result $testResultMarkdown return $true diff --git a/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 b/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 index 781e0359b..9909968a2 100644 --- a/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 +++ b/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 @@ -69,28 +69,28 @@ Describe "Maester/Intune" -Tag "Maester", "Intune" { } } - It "MT.1148: Ensure LAPS Configuration Policy is properly set" -Tag "MT.1148" { + It "MT.1148: Ensure LAPS Configuration Policy is properly set. See https://maester.dev/docs/tests/MT.1148" -Tag "MT.1148" { $result = Test-MtIntuneLAPSConfiguration if ($null -ne $result) { $result | Should -Be $true -Because "a LAPS Configuration policy is properly set in Intune." } } - It "MT.1149: Ensure ASR Rules are configured correctly" -Tag "MT.1149" { + It "MT.1149: Ensure ASR Rules are configured correctly. See https://maester.dev/docs/tests/MT.1149" -Tag "MT.1149" { $result = Test-MtIntuneASRRules if ($null -ne $result) { $result | Should -Be $true -Because "Attack Surface Reduction (ASR) Rules are configured in Block or Audit mode." } } - It "MT.1150: Ensure App Control for Business is enabled" -Tag "MT.1150" { + It "MT.1150: Ensure App Control for Business is enabled. See https://maester.dev/docs/tests/MT.1150" -Tag "MT.1150" { $result = Test-MtIntuneAppControl if ($null -ne $result) { $result | Should -Be $true -Because "App Control for Business is enabled in Intune." } } - It "MT.1151: Ensure Managed Installer Rules are configured correctly" -Tag "MT.1151" { + It "MT.1151: Ensure Managed Installer Rules are configured correctly. See https://maester.dev/docs/tests/MT.1151" -Tag "MT.1151" { $result = Test-MtIntuneManagedInstallerRules if ($null -ne $result) { $result | Should -Be $true -Because "'Trust apps from managed installer' is enabled in at least one App Control policy." diff --git a/website/docs/tests/maester/MT.1148.md b/website/docs/tests/maester/MT.1148.md new file mode 100644 index 000000000..2169f60fd --- /dev/null +++ b/website/docs/tests/maester/MT.1148.md @@ -0,0 +1,42 @@ +--- +title: MT.1148 - Ensure LAPS Configuration Policy is properly set +description: Checks Intune Endpoint Security Account Protection policies for Windows LAPS profiles that back up local administrator passwords to Microsoft Entra ID. +slug: /tests/MT.1148 +sidebar_class_name: hidden +--- + +# Ensure LAPS Configuration Policy is properly set + +## Description + +Checks Intune Endpoint Security Account Protection policies for Windows Local Administrator Password Solution (LAPS) profiles that back up local administrator passwords to Microsoft Entra ID. + +Windows LAPS automatically rotates and backs up local administrator passwords, preventing lateral movement attacks that exploit shared or stale local admin credentials. The test passes if at least one LAPS policy is configured with **Backup Directory** set to **Azure AD only** (`_1`). + +Key settings evaluated: + +- **Backup Directory**: Must be set to `Azure AD only` (`_1`) so passwords are stored in Entra ID and can be retrieved centrally. +- **Password Complexity**: Recommended `Large + small + numbers + special` (`_4`) or improved (`_8`). +- **Password Length**: Recommended **>= 14** characters. +- **Post-Authentication Actions**: Reset password (and optionally logoff or reboot) after the configured delay. +- **Automatic Account Management**: Whether LAPS auto-manages the local admin account. + +## How to fix + +1. Navigate to the [Microsoft Intune admin center](https://intune.microsoft.com). +2. Go to **Endpoint security** > **Account protection**. +3. Click **+ Create policy**. +4. Set **Platform** to **Windows 10 and later** and **Profile** to **Local admin password solution (Windows LAPS)**. +5. Configure: + - **Backup Directory**: **Azure AD only** + - **Password Complexity**: `Large + small + numbers + special` or improved + - **Password Length**: **14** characters or more + - **Post-Authentication Actions**: **Reset password** (or stronger) + - **Post-Authentication Reset Delay**: 12 hours or fewer +6. Assign the policy to your Windows device groups and click **Create**. + +## Learn more + +- [Windows LAPS overview](https://learn.microsoft.com/mem/intune/protect/windows-laps-overview) +- [Windows LAPS policy CSP](https://learn.microsoft.com/windows/client-management/mdm/laps-csp) +- [Account protection policies in Intune](https://learn.microsoft.com/mem/intune/protect/endpoint-security-account-protection-policy) diff --git a/website/docs/tests/maester/MT.1149.md b/website/docs/tests/maester/MT.1149.md new file mode 100644 index 000000000..a703e074b --- /dev/null +++ b/website/docs/tests/maester/MT.1149.md @@ -0,0 +1,49 @@ +--- +title: MT.1149 - Ensure ASR Rules are configured correctly +description: Checks Intune Endpoint Security Attack Surface Reduction (ASR) policies for rules configured in Block or Audit mode. +slug: /tests/MT.1149 +sidebar_class_name: hidden +--- + +# Ensure ASR Rules are configured correctly + +## Description + +Checks Intune Endpoint Security Attack Surface Reduction (ASR) policies for rules configured in **Block** or **Audit** mode. + +ASR rules reduce the attack surface of applications by preventing behaviors commonly abused by malware — such as Office macros spawning child processes, credential theft from LSASS, execution of obfuscated scripts, and email-borne threats. + +Each ASR rule can operate in one of four modes: + +- **Block** — Actively prevents the behavior (recommended for production after testing). +- **Audit** — Logs the event without blocking (recommended for initial rollout). +- **Warn** — Warns the user before allowing the behavior to proceed. +- **Disabled** — Rule is not active. + +The test passes if at least one ASR policy has at least one rule configured in **Block** or **Audit** mode. **Warn** is a supported ASR rule state but does not satisfy this control's pass criteria. Policies with all rules in **Audit** mode trigger an informational note recommending a transition to **Block** mode. + +## How to fix + +1. Navigate to the [Microsoft Intune admin center](https://intune.microsoft.com). +2. Go to **Endpoint security** > **Attack surface reduction**. +3. Click **+ Create policy**. +4. Set **Platform** to **Windows 10 and later** and **Profile** to **Attack Surface Reduction Rules**. +5. Configure individual ASR rules, starting in **Audit** mode for all rules: + - Block credential stealing from Windows LSASS + - Block all Office applications from creating child processes + - Block Win32 API calls from Office macros + - Block execution of potentially obfuscated scripts + - Block executable content from email client and webmail + - Block JavaScript or VBScript from launching downloaded executable content + - Block process creations originating from PSExec and WMI commands + - Block untrusted and unsigned processes that run from USB + - Block persistence through WMI event subscription + - Use advanced protection against ransomware +6. Assign the policy to your Windows device groups and click **Create**. +7. Monitor audit events in **Microsoft Defender for Endpoint** > **Reports** > **Attack surface reduction rules** for 2–4 weeks before transitioning rules to **Block** mode. + +## Learn more + +- [Attack surface reduction rules reference](https://learn.microsoft.com/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-reference) +- [Enable ASR rules in Intune](https://learn.microsoft.com/microsoft-365/security/defender-endpoint/enable-attack-surface-reduction) +- [ASR rules deployment guide](https://learn.microsoft.com/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment) diff --git a/website/docs/tests/maester/MT.1150.md b/website/docs/tests/maester/MT.1150.md new file mode 100644 index 000000000..22f2a3309 --- /dev/null +++ b/website/docs/tests/maester/MT.1150.md @@ -0,0 +1,45 @@ +--- +title: MT.1150 - Ensure App Control for Business is enabled +description: Checks Intune Endpoint Security Application Control policies for App Control for Business (formerly WDAC) configurations. +slug: /tests/MT.1150 +sidebar_class_name: hidden +--- + +# Ensure App Control for Business is enabled + +## Description + +Checks Intune Endpoint Security Application Control policies for **App Control for Business** (formerly Windows Defender Application Control / WDAC) configurations. + +App Control for Business restricts which applications and drivers are allowed to run on Windows devices using code integrity policies. This is one of the most effective defenses against malware, ransomware, and unauthorized software because it blocks untrusted executables from running at all — even if they bypass antivirus detection. + +Key settings evaluated: + +- **Build Options** — Whether the policy uses built-in controls (`built_in_controls_selected`) or a custom uploaded policy (`upload_policy_selected`). +- **Audit Mode** — Whether the policy is in audit mode (logging only) or enforce mode (blocking). +- **Trust apps from managed installer** — Whether apps deployed via Intune/SCCM are automatically trusted. +- **Trust apps with good reputation (ISG)** — Whether apps with good Intelligent Security Graph reputation are trusted. + +The test passes if at least one App Control for Business policy exists with build options configured. Policies still in **Audit mode** trigger an informational note recommending a transition to **Enforce mode** after validation. + +## How to fix + +1. Navigate to the [Microsoft Intune admin center](https://intune.microsoft.com). +2. Go to **Endpoint security** > **Application control**. +3. Click **+ Create policy**. +4. Set **Platform** to **Windows 10 and later** and **Profile** to **App Control for Business**. +5. Enter a policy name (for example, "App Control - Audit Mode"). +6. Configure: + - **App Control for Business**: **Built-in controls** + - **Audit mode**: **Enabled** (start in audit mode to identify blocked apps) + - **Trust apps from managed installer**: **Enabled** + - **Trust apps with good reputation**: **Disabled** (optional — ISG adds convenience but reduces strictness) +7. Assign the policy to a test device group first. +8. Monitor blocked / audited apps in **Microsoft Defender for Endpoint** > **Reports** > **Application control**. +9. After validating that legitimate apps are not being blocked, transition to **Enforce mode**. + +## Learn more + +- [App Control for Business in Intune](https://learn.microsoft.com/mem/intune/protect/endpoint-security-app-control-policy) +- [Application Control for Windows](https://learn.microsoft.com/windows/security/application-security/application-control/app-control-for-business/appcontrol) +- [Configure Managed Installer and ISG options](https://learn.microsoft.com/windows/security/application-security/application-control/app-control-for-business/design/configure-appcontrol-managed-installer) diff --git a/website/docs/tests/maester/MT.1151.md b/website/docs/tests/maester/MT.1151.md new file mode 100644 index 000000000..190c08cee --- /dev/null +++ b/website/docs/tests/maester/MT.1151.md @@ -0,0 +1,45 @@ +--- +title: MT.1151 - Ensure Managed Installer Rules are configured correctly +description: Checks Intune Endpoint Security Application Control policies for the 'Trust apps from managed installer' setting. +slug: /tests/MT.1151 +sidebar_class_name: hidden +--- + +# Ensure Managed Installer Rules are configured correctly + +## Description + +Checks Intune Endpoint Security Application Control policies for the **Trust apps from managed installer** setting. + +When Managed Installer is enabled in an App Control for Business policy, applications deployed through Intune (or SCCM) are automatically trusted and allowed to run without needing explicit allow rules in the code integrity policy. This dramatically simplifies App Control deployment in enterprise environments. + +**Without Managed Installer:** + +- Every application must have an explicit allow rule in the App Control policy. +- Line-of-business (LOB) apps deployed via Intune may be blocked unexpectedly. +- Help desk tickets increase due to false positives from legitimate software being blocked. + +**With Managed Installer:** + +- Apps deployed through Intune are automatically allow-listed at install time. +- Only user-installed, sideloaded, or internet-downloaded apps are subject to policy restrictions. +- Reduces false positives while maintaining security against unauthorized software. + +The test passes if at least one App Control for Business policy has **Trust apps from managed installer** enabled. + +## How to fix + +1. Navigate to the [Microsoft Intune admin center](https://intune.microsoft.com). +2. Go to **Endpoint security** > **Application control**. +3. Edit an existing App Control for Business policy (or create a new one). +4. Under **App Control for Business**, select **Built-in controls**. +5. Set **Trust apps from managed installer** to **Enabled**. +6. Save and assign the policy to your device groups. + +> **Note:** Managed Installer works by tagging files written by the Intune Management Extension (IME) process. The App Control policy then trusts any file that was installed by a tagged managed installer process. This is transparent to end users. + +## Learn more + +- [Configure Managed Installer in Intune](https://learn.microsoft.com/mem/intune/protect/endpoint-security-app-control-policy) +- [Automatically allow apps deployed by a managed installer](https://learn.microsoft.com/windows/security/application-security/application-control/app-control-for-business/design/configure-appcontrol-managed-installer) +- [App Control for Business overview](https://learn.microsoft.com/windows/security/application-security/application-control/app-control-for-business/appcontrol) From 43e8333015a11a5717f6a84d267adc16ea33bddc Mon Sep 17 00:00:00 2001 From: Ofir Gavish Date: Sun, 26 Apr 2026 09:49:02 +0300 Subject: [PATCH 08/14] Address Copilot follow-up review on PR #1676 - LAPS: surface PostAuthDelay in the per-policy results table (was parsed but unused) - ASR: update comment-based help to list all four supported modes (Block/Audit/Warn/Disabled), matching the implementation and the .md doc --- powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 | 3 ++- .../maester/intune/Test-MtIntuneLAPSConfiguration.ps1 | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 index dc0f8c465..a3e8e1968 100644 --- a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 @@ -10,9 +10,10 @@ ASR rules reduce the attack surface of applications by preventing behaviors commonly abused by malware, such as Office macros spawning child processes, credential theft from LSASS, or execution of obfuscated scripts. - Each ASR rule can be set to one of three modes: + Each ASR rule can be set to one of four modes: - Block: Actively prevents the behavior (recommended for production) - Audit: Logs the event without blocking (recommended for testing) + - Warn: Warns the user before allowing the behavior - Disabled/Not configured: Rule is inactive This test queries Endpoint Security ASR policies filtered by templateFamily 'endpointSecurityAttackSurfaceReduction' diff --git a/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 index f11319669..b2ec8d73d 100644 --- a/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 @@ -161,10 +161,10 @@ # Build result markdown $testResultMarkdown = "Found $($lapsPolicies.Count) Windows LAPS policy/policies in Intune.`n`n" - $testResultMarkdown += "| Policy | Backup Directory | Complexity | Length | Post-Auth Actions | Auto Account Mgmt |`n" - $testResultMarkdown += "| --- | --- | --- | --- | --- | --- |`n" + $testResultMarkdown += "| Policy | Backup Directory | Complexity | Length | Post-Auth Actions | Post-Auth Delay | Auto Account Mgmt |`n" + $testResultMarkdown += "| --- | --- | --- | --- | --- | --- | --- |`n" foreach ($p in $policyResults) { - $testResultMarkdown += "| $($p.Name) | $($p.BackupDirectory) | $($p.PasswordComplexity) | $($p.PasswordLength) | $($p.PostAuthActions) | $($p.AutoAccountMgmt) |`n" + $testResultMarkdown += "| $($p.Name) | $($p.BackupDirectory) | $($p.PasswordComplexity) | $($p.PasswordLength) | $($p.PostAuthActions) | $($p.PostAuthDelay) | $($p.AutoAccountMgmt) |`n" } if ($hasEntraBackup) { From 639d9f25e0038b715ce71fa183e461575c84a8a8 Mon Sep 17 00:00:00 2001 From: Ofir Gavish Date: Sun, 26 Apr 2026 10:15:55 +0300 Subject: [PATCH 09/14] Address Copilot follow-up: clarify ASR audit-only note text The condition matches policies with no Block rules and >=1 Audit rule (which may also include Warn/Disabled rules), so the wording 'Audit mode only' was misleading. Reworded to accurately describe what the condition checks. --- powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 index a3e8e1968..1b5a939d2 100644 --- a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 @@ -148,11 +148,11 @@ if ($hasActiveRules) { $testResultMarkdown += "**Result:** Well done. At least one ASR policy has rules in **Block** or **Audit** mode." - # Warn about audit-only policies + # Warn about policies with audit coverage but no block-mode rules $auditOnly = @($policyResults | Where-Object { $_.BlockCount -eq 0 -and $_.AuditCount -gt 0 }) if ($auditOnly.Count -gt 0) { - $testResultMarkdown += "`n`n> **Note:** $($auditOnly.Count) policy/policies have rules in **Audit mode only**. " - $testResultMarkdown += "Consider transitioning tested rules to **Block** mode for active protection." + $testResultMarkdown += "`n`n> **Note:** $($auditOnly.Count) policy/policies have no rules in **Block** mode and at least one rule in **Audit** mode. " + $testResultMarkdown += "Consider transitioning tested Audit rules to **Block** mode for active protection." } Add-MtTestResultDetail -Result $testResultMarkdown From 2a30334421ff8f5eb27e120f31b61bb5ef7baab8 Mon Sep 17 00:00:00 2001 From: Ofir Gavish <81085873+OfirGavish@users.noreply.github.com> Date: Sun, 26 Apr 2026 19:08:02 +0300 Subject: [PATCH 10/14] Update powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 b/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 index 56bfffa80..36820f090 100644 --- a/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 @@ -17,7 +17,8 @@ - TrustAppsFromManagedInstaller: Whether apps deployed via Intune/SCCM are automatically trusted - TrustAppsWithGoodReputation: Whether ISG (Intelligent Security Graph) reputation is used - The test passes if at least one App Control for Business policy exists with built-in controls configured. + The test passes if at least one App Control for Business policy exists with built-in controls configured + or a custom policy uploaded. .EXAMPLE Test-MtIntuneAppControl From 59a30989c9c86d1eb51fe76b458491af8b2968b6 Mon Sep 17 00:00:00 2001 From: Ofir Gavish Date: Wed, 29 Apr 2026 16:01:37 +0300 Subject: [PATCH 11/14] MT.1149: reconcile ASR per-policy rule counts Address Copilot follow-up review on PR #1676: The per-policy ASR summary line previously displayed TotalRules along with Block/Audit/Warn/Disabled counters, but rules whose mode value did not match any of the known suffixes ('*_block', '*_audit', '*_warn', '*_off') were silently excluded from the rollup. As a result the displayed counts could be lower than TotalRules and 'Not configured' rules were hidden from view. This change adds a NotConfiguredCount counter (incremented in the else branch alongside the existing mode detection) and surfaces it in the per-policy summary line so the four counters now reconcile with TotalRules. --- .../maester/intune/Test-MtIntuneASRRules.ps1 | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 index 1b5a939d2..238863f5b 100644 --- a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 @@ -92,6 +92,7 @@ $auditCount = 0 $warnCount = 0 $disabledCount = 0 + $notConfiguredCount = 0 $ruleDetails = [System.Collections.Generic.List[hashtable]]::new() foreach ($setting in $settingsResponse) { @@ -114,6 +115,7 @@ elseif ($val -like '*_audit') { $mode = 'Audit'; $auditCount++; $hasActiveRules = $true } elseif ($val -like '*_warn') { $mode = 'Warn'; $warnCount++ } elseif ($val -like '*_off') { $mode = 'Disabled'; $disabledCount++ } + else { $notConfiguredCount++ } Write-Verbose " Rule: $friendlyName = $mode" $ruleDetails.Add(@{ Name = $friendlyName; Mode = $mode }) @@ -122,13 +124,14 @@ } $policyResults.Add(@{ - Name = $policy.name - BlockCount = $blockCount - AuditCount = $auditCount - WarnCount = $warnCount - DisabledCount = $disabledCount - TotalRules = $ruleDetails.Count - Rules = $ruleDetails + Name = $policy.name + BlockCount = $blockCount + AuditCount = $auditCount + WarnCount = $warnCount + DisabledCount = $disabledCount + NotConfiguredCount = $notConfiguredCount + TotalRules = $ruleDetails.Count + Rules = $ruleDetails }) } @@ -137,7 +140,7 @@ foreach ($p in $policyResults) { $testResultMarkdown += "### $($p.Name)`n" - $testResultMarkdown += "**$($p.TotalRules) rules:** $($p.BlockCount) Block, $($p.AuditCount) Audit, $($p.WarnCount) Warn, $($p.DisabledCount) Disabled`n`n" + $testResultMarkdown += "**$($p.TotalRules) rules:** $($p.BlockCount) Block, $($p.AuditCount) Audit, $($p.WarnCount) Warn, $($p.DisabledCount) Disabled, $($p.NotConfiguredCount) Not configured`n`n" $testResultMarkdown += "| Rule | Mode |`n| --- | --- |`n" foreach ($r in $p.Rules) { $testResultMarkdown += "| $($r.Name) | $($r.Mode) |`n" From afb3b56f596528a1a5508940ce8760583563e936 Mon Sep 17 00:00:00 2001 From: Ofir Gavish Date: Mon, 11 May 2026 22:50:57 -0400 Subject: [PATCH 12/14] Renumber tests to MT.1177-MT.1180 Upstream merill commit 7ebade57 (Added test meta data, May 2 2026) claimed MT.1148-MT.1151 for Microsoft Defender antivirus tests: MT.1148 Archive Scanning should be enabled MT.1149 Behavior Monitoring should be enabled MT.1150 Cloud Protection should be enabled MT.1151 Email Scanning should be enabled Renumber this PR's Intune Endpoint Security tests to the next available contiguous range MT.1177-MT.1180 to resolve the add/add conflicts on website/docs/tests/maester/MT.115X.md and the title collisions in tests/maester-config.json. MT.1148 -> MT.1177 LAPS Configuration Policy MT.1149 -> MT.1178 ASR Rules MT.1150 -> MT.1179 App Control for Business MT.1151 -> MT.1180 Managed Installer --- tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 | 8 ++++---- tests/maester-config.json | 8 ++++---- website/docs/tests/maester/{MT.1148.md => MT.1177.md} | 4 ++-- website/docs/tests/maester/{MT.1149.md => MT.1178.md} | 4 ++-- website/docs/tests/maester/{MT.1150.md => MT.1179.md} | 4 ++-- website/docs/tests/maester/{MT.1151.md => MT.1180.md} | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) rename website/docs/tests/maester/{MT.1148.md => MT.1177.md} (96%) rename website/docs/tests/maester/{MT.1149.md => MT.1178.md} (97%) rename website/docs/tests/maester/{MT.1150.md => MT.1179.md} (97%) rename website/docs/tests/maester/{MT.1151.md => MT.1180.md} (96%) diff --git a/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 b/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 index 9909968a2..79a71995e 100644 --- a/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 +++ b/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 @@ -69,28 +69,28 @@ Describe "Maester/Intune" -Tag "Maester", "Intune" { } } - It "MT.1148: Ensure LAPS Configuration Policy is properly set. See https://maester.dev/docs/tests/MT.1148" -Tag "MT.1148" { + It "MT.1177: Ensure LAPS Configuration Policy is properly set. See https://maester.dev/docs/tests/MT.1177" -Tag "MT.1177" { $result = Test-MtIntuneLAPSConfiguration if ($null -ne $result) { $result | Should -Be $true -Because "a LAPS Configuration policy is properly set in Intune." } } - It "MT.1149: Ensure ASR Rules are configured correctly. See https://maester.dev/docs/tests/MT.1149" -Tag "MT.1149" { + It "MT.1178: Ensure ASR Rules are configured correctly. See https://maester.dev/docs/tests/MT.1178" -Tag "MT.1178" { $result = Test-MtIntuneASRRules if ($null -ne $result) { $result | Should -Be $true -Because "Attack Surface Reduction (ASR) Rules are configured in Block or Audit mode." } } - It "MT.1150: Ensure App Control for Business is enabled. See https://maester.dev/docs/tests/MT.1150" -Tag "MT.1150" { + It "MT.1179: Ensure App Control for Business is enabled. See https://maester.dev/docs/tests/MT.1179" -Tag "MT.1179" { $result = Test-MtIntuneAppControl if ($null -ne $result) { $result | Should -Be $true -Because "App Control for Business is enabled in Intune." } } - It "MT.1151: Ensure Managed Installer Rules are configured correctly. See https://maester.dev/docs/tests/MT.1151" -Tag "MT.1151" { + It "MT.1180: Ensure Managed Installer Rules are configured correctly. See https://maester.dev/docs/tests/MT.1180" -Tag "MT.1180" { $result = Test-MtIntuneManagedInstallerRules if ($null -ne $result) { $result | Should -Be $true -Because "'Trust apps from managed installer' is enabled in at least one App Control policy." diff --git a/tests/maester-config.json b/tests/maester-config.json index d66316ec7..2512cce10 100644 --- a/tests/maester-config.json +++ b/tests/maester-config.json @@ -1355,22 +1355,22 @@ "Title": "Ensure BitLocker full disk encryption is configured via Intune" }, { - "Id": "MT.1148", + "Id": "MT.1177", "Severity": "High", "Title": "Ensure LAPS Configuration Policy is properly set" }, { - "Id": "MT.1149", + "Id": "MT.1178", "Severity": "High", "Title": "Ensure ASR Rules are configured correctly" }, { - "Id": "MT.1150", + "Id": "MT.1179", "Severity": "High", "Title": "Ensure App Control for Business is enabled" }, { - "Id": "MT.1151", + "Id": "MT.1180", "Severity": "Medium", "Title": "Ensure Managed Installer Rules are configured correctly" }, diff --git a/website/docs/tests/maester/MT.1148.md b/website/docs/tests/maester/MT.1177.md similarity index 96% rename from website/docs/tests/maester/MT.1148.md rename to website/docs/tests/maester/MT.1177.md index 2169f60fd..bd9e62448 100644 --- a/website/docs/tests/maester/MT.1148.md +++ b/website/docs/tests/maester/MT.1177.md @@ -1,7 +1,7 @@ --- -title: MT.1148 - Ensure LAPS Configuration Policy is properly set +title: MT.1177 - Ensure LAPS Configuration Policy is properly set description: Checks Intune Endpoint Security Account Protection policies for Windows LAPS profiles that back up local administrator passwords to Microsoft Entra ID. -slug: /tests/MT.1148 +slug: /tests/MT.1177 sidebar_class_name: hidden --- diff --git a/website/docs/tests/maester/MT.1149.md b/website/docs/tests/maester/MT.1178.md similarity index 97% rename from website/docs/tests/maester/MT.1149.md rename to website/docs/tests/maester/MT.1178.md index a703e074b..36998fed0 100644 --- a/website/docs/tests/maester/MT.1149.md +++ b/website/docs/tests/maester/MT.1178.md @@ -1,7 +1,7 @@ --- -title: MT.1149 - Ensure ASR Rules are configured correctly +title: MT.1178 - Ensure ASR Rules are configured correctly description: Checks Intune Endpoint Security Attack Surface Reduction (ASR) policies for rules configured in Block or Audit mode. -slug: /tests/MT.1149 +slug: /tests/MT.1178 sidebar_class_name: hidden --- diff --git a/website/docs/tests/maester/MT.1150.md b/website/docs/tests/maester/MT.1179.md similarity index 97% rename from website/docs/tests/maester/MT.1150.md rename to website/docs/tests/maester/MT.1179.md index 22f2a3309..2f1e33fd1 100644 --- a/website/docs/tests/maester/MT.1150.md +++ b/website/docs/tests/maester/MT.1179.md @@ -1,7 +1,7 @@ --- -title: MT.1150 - Ensure App Control for Business is enabled +title: MT.1179 - Ensure App Control for Business is enabled description: Checks Intune Endpoint Security Application Control policies for App Control for Business (formerly WDAC) configurations. -slug: /tests/MT.1150 +slug: /tests/MT.1179 sidebar_class_name: hidden --- diff --git a/website/docs/tests/maester/MT.1151.md b/website/docs/tests/maester/MT.1180.md similarity index 96% rename from website/docs/tests/maester/MT.1151.md rename to website/docs/tests/maester/MT.1180.md index 190c08cee..7c0a50a37 100644 --- a/website/docs/tests/maester/MT.1151.md +++ b/website/docs/tests/maester/MT.1180.md @@ -1,7 +1,7 @@ --- -title: MT.1151 - Ensure Managed Installer Rules are configured correctly +title: MT.1180 - Ensure Managed Installer Rules are configured correctly description: Checks Intune Endpoint Security Application Control policies for the 'Trust apps from managed installer' setting. -slug: /tests/MT.1151 +slug: /tests/MT.1180 sidebar_class_name: hidden --- From 3a19a95d1df14e2029fde99ec01c6cf050b0d8ae Mon Sep 17 00:00:00 2001 From: Ofir Gavish Date: Mon, 11 May 2026 23:04:41 -0400 Subject: [PATCH 13/14] =?UTF-8?q?Address=20@SamErde=20review=20on=20PR=20#?= =?UTF-8?q?1676=20=E2=80=94=20tighten=20pass=20criteria?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MT.1177 LAPS: pass now requires Entra backup AND complexity >= _4 AND length >= 14 AND a defined post-auth action (per-policy boolean tracking, Compliant column added). MT.1178 ASR: pivot to Microsoft Defender ASR Standard Protection baseline. Pass now requires every baseline rule (Block abuse of exploited vulnerable signed drivers, Block credential stealing from LSASS, Block persistence through WMI event subscription) to be in Block or Audit mode across the union of all policies. Additional rules still listed for visibility. MT.1179 App Control: detect uploaded XML payload (simpleSettingValue under upload_policy_selected). Pass now requires at least one policy to be enforcing (audit mode disabled) AND have either built-in controls or a non-empty XML upload. Audit-only policies and empty XML uploads are reported but no longer pass. MT.1180 Managed Installer: pass now requires at least one App Control policy to be in Enforce mode AND have Managed Installer enabled. Managed Installer on an audit-only policy is reported but no longer passes, since the underlying App Control is not blocking. Website docs (MT.1177-1180.md) updated to document the new pass criteria. --- .../maester/intune/Test-MtIntuneASRRules.ps1 | 99 ++++++++++++---- .../intune/Test-MtIntuneAppControl.ps1 | 111 ++++++++++++------ .../intune/Test-MtIntuneLAPSConfiguration.ps1 | 75 ++++++++---- .../Test-MtIntuneManagedInstallerRules.ps1 | 39 ++++-- website/docs/tests/maester/MT.1177.md | 12 +- website/docs/tests/maester/MT.1178.md | 10 +- website/docs/tests/maester/MT.1179.md | 3 +- website/docs/tests/maester/MT.1180.md | 2 +- 8 files changed, 252 insertions(+), 99 deletions(-) diff --git a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 index 238863f5b..318295b4d 100644 --- a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 @@ -1,7 +1,7 @@ function Test-MtIntuneASRRules { <# .SYNOPSIS - Ensure at least one Intune Attack Surface Reduction (ASR) policy has rules configured in Block or Audit mode. + Ensure the Microsoft Defender ASR Standard Protection baseline rules are configured in Block or Audit mode. .DESCRIPTION Checks Intune Endpoint Security Attack Surface Reduction policies (configurationPolicies API) for @@ -16,14 +16,32 @@ - Warn: Warns the user before allowing the behavior - Disabled/Not configured: Rule is inactive - This test queries Endpoint Security ASR policies filtered by templateFamily 'endpointSecurityAttackSurfaceReduction' - and inspects each rule's enforcement state. The test passes if at least one ASR policy has one or more rules - configured in Block or Audit mode. + Pass criteria: + The test passes if every rule in the Microsoft Defender for Endpoint ASR Standard Protection baseline is + configured in Block or Audit mode in at least one ASR policy. The Standard Protection baseline is the + minimum recommended set Microsoft publishes for initial ASR deployment: + + 1. Block abuse of exploited vulnerable signed drivers + 2. Block credential stealing from LSASS + 3. Block persistence through WMI event subscription + + See https://learn.microsoft.com/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment-implement + + Additional ASR rules detected in tenant policies are reported for visibility but do not affect the pass/fail result. + + .EXAMPLE + Test-MtIntuneASRRules + + Returns true if every Standard Protection baseline rule is configured (Block or Audit) in at least one ASR policy + + See https://learn.microsoft.com/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment-implement + + Additional ASR rules detected in tenant policies are reported for visibility but do not affect the pass/fail result. .EXAMPLE Test-MtIntuneASRRules - Returns true if at least one ASR policy has rules configured in Block or Audit mode. + Returns true if every Standard Protection baseline rule is configured (Block or Audit) in at least one ASR policy. .LINK https://maester.dev/docs/commands/Test-MtIntuneASRRules @@ -80,8 +98,21 @@ 'blockwebshellcreationforservers' = 'Block webshell creation for servers' } + # Microsoft Standard Protection baseline (minimum recommended ASR rules per Defender deployment guide). + # Pass requires every rule in this set to be Block or Audit across the union of all ASR policies. + $standardProtectionRuleSuffixes = @{ + 'blockabuseofexploitedvulnerablesigneddrivers' = 'Block abuse of exploited vulnerable signed drivers' + 'blockcredentialstealingfromwindowslocalsecurityauthoritysubsystem' = 'Block credential stealing from LSASS' + 'blockpersistencethroughwmieventsubscription' = 'Block persistence through WMI event subscription' + } + $policyResults = [System.Collections.Generic.List[hashtable]]::new() - $hasActiveRules = $false + # Track best mode seen across the union of all policies for each baseline rule. + # Priority: Block > Audit > Warn > Disabled > Not configured. + $baselineRuleStatus = @{} + foreach ($k in $standardProtectionRuleSuffixes.Keys) { $baselineRuleStatus[$k] = 'Not configured' } + + $modeRank = @{ 'Block' = 4; 'Audit' = 3; 'Warn' = 2; 'Disabled' = 1; 'Not configured' = 0 } foreach ($policy in $asrPolicies) { Write-Verbose "Checking ASR policy: $($policy.name) ($($policy.id))" @@ -111,14 +142,22 @@ # Determine enforcement mode from value suffix $mode = 'Not configured' - if ($val -like '*_block') { $mode = 'Block'; $blockCount++; $hasActiveRules = $true } - elseif ($val -like '*_audit') { $mode = 'Audit'; $auditCount++; $hasActiveRules = $true } + if ($val -like '*_block') { $mode = 'Block'; $blockCount++ } + elseif ($val -like '*_audit') { $mode = 'Audit'; $auditCount++ } elseif ($val -like '*_warn') { $mode = 'Warn'; $warnCount++ } elseif ($val -like '*_off') { $mode = 'Disabled'; $disabledCount++ } else { $notConfiguredCount++ } Write-Verbose " Rule: $friendlyName = $mode" - $ruleDetails.Add(@{ Name = $friendlyName; Mode = $mode }) + $ruleDetails.Add(@{ Name = $friendlyName; Mode = $mode; IsBaseline = $standardProtectionRuleSuffixes.ContainsKey($ruleSuffix) }) + + # Track best mode for baseline rules across all policies + if ($standardProtectionRuleSuffixes.ContainsKey($ruleSuffix)) { + $current = $baselineRuleStatus[$ruleSuffix] + if ($modeRank[$mode] -gt $modeRank[$current]) { + $baselineRuleStatus[$ruleSuffix] = $mode + } + } } } } @@ -135,35 +174,55 @@ }) } + # Evaluate baseline coverage across the union of all policies + $baselineMissing = @() + foreach ($k in $standardProtectionRuleSuffixes.Keys) { + $mode = $baselineRuleStatus[$k] + if ($mode -ne 'Block' -and $mode -ne 'Audit') { + $baselineMissing += [pscustomobject]@{ Name = $standardProtectionRuleSuffixes[$k]; Mode = $mode } + } + } + $baselinePassed = ($baselineMissing.Count -eq 0) + # Build result markdown $testResultMarkdown = "Found $($asrPolicies.Count) Attack Surface Reduction policy/policies in Intune.`n`n" + $testResultMarkdown += "**Pass criteria:** Every rule in the Microsoft Defender ASR Standard Protection baseline must be configured in **Block** or **Audit** mode in at least one ASR policy.`n`n" + + $testResultMarkdown += "### Standard Protection baseline coverage (across all policies)`n" + $testResultMarkdown += "| Baseline rule | Best mode found |`n| --- | --- |`n" + foreach ($k in $standardProtectionRuleSuffixes.Keys) { + $testResultMarkdown += "| $($standardProtectionRuleSuffixes[$k]) | $($baselineRuleStatus[$k]) |`n" + } + $testResultMarkdown += "`n" foreach ($p in $policyResults) { $testResultMarkdown += "### $($p.Name)`n" $testResultMarkdown += "**$($p.TotalRules) rules:** $($p.BlockCount) Block, $($p.AuditCount) Audit, $($p.WarnCount) Warn, $($p.DisabledCount) Disabled, $($p.NotConfiguredCount) Not configured`n`n" - $testResultMarkdown += "| Rule | Mode |`n| --- | --- |`n" + $testResultMarkdown += "| Rule | Mode | Baseline |`n| --- | --- | --- |`n" foreach ($r in $p.Rules) { - $testResultMarkdown += "| $($r.Name) | $($r.Mode) |`n" + $baselineMark = if ($r.IsBaseline) { 'Yes' } else { '' } + $testResultMarkdown += "| $($r.Name) | $($r.Mode) | $baselineMark |`n" } $testResultMarkdown += "`n" } - if ($hasActiveRules) { - $testResultMarkdown += "**Result:** Well done. At least one ASR policy has rules in **Block** or **Audit** mode." + if ($baselinePassed) { + $testResultMarkdown += "**Result:** Well done. Every rule in the Microsoft Defender ASR Standard Protection baseline is configured in **Block** or **Audit** mode." - # Warn about policies with audit coverage but no block-mode rules - $auditOnly = @($policyResults | Where-Object { $_.BlockCount -eq 0 -and $_.AuditCount -gt 0 }) + # Warn about baseline rules that are still in Audit only across the tenant + $auditOnly = @($baselineRuleStatus.Keys | Where-Object { $baselineRuleStatus[$_] -eq 'Audit' }) if ($auditOnly.Count -gt 0) { - $testResultMarkdown += "`n`n> **Note:** $($auditOnly.Count) policy/policies have no rules in **Block** mode and at least one rule in **Audit** mode. " - $testResultMarkdown += "Consider transitioning tested Audit rules to **Block** mode for active protection." + $testResultMarkdown += "`n`n> **Note:** $($auditOnly.Count) baseline rule(s) are only in **Audit** mode. " + $testResultMarkdown += "Once you have validated impact, transition them to **Block** mode for active protection." } Add-MtTestResultDetail -Result $testResultMarkdown return $true } else { - $testResultMarkdown += "**Result:** No ASR rules are configured in Block or Audit mode.`n`n" - $testResultMarkdown += "> **Risk:** Without active ASR rules, endpoints are vulnerable to common attack techniques " - $testResultMarkdown += "such as Office macro abuse, credential theft, and script-based attacks." + $missingTable = ($baselineMissing | ForEach-Object { "- $($_.Name) (current: $($_.Mode))" }) -join "`n" + $testResultMarkdown += "**Result:** The following Standard Protection baseline rules are not in Block or Audit mode:`n`n$missingTable`n`n" + $testResultMarkdown += "> **Risk:** The Microsoft Defender ASR Standard Protection baseline is the published minimum set of rules required to mitigate " + $testResultMarkdown += "common credential theft, driver abuse, and persistence techniques. Missing rules leave endpoints exposed to these well-known attack patterns." Add-MtTestResultDetail -Result $testResultMarkdown return $false } diff --git a/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 b/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 index 36820f090..dde99f775 100644 --- a/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 @@ -12,18 +12,23 @@ against malware, ransomware, and unauthorized software. Key settings evaluated: - - BuildOptions: Whether built-in controls are selected + - BuildOptions: Whether built-in controls are selected or a custom XML policy is uploaded + - PolicyXml: For uploaded policies, whether an XML policy payload is actually present - AuditMode: Whether the policy is in audit mode (logging only) or enforce mode - TrustAppsFromManagedInstaller: Whether apps deployed via Intune/SCCM are automatically trusted - TrustAppsWithGoodReputation: Whether ISG (Intelligent Security Graph) reputation is used - The test passes if at least one App Control for Business policy exists with built-in controls configured - or a custom policy uploaded. + Pass criteria: + The test passes if at least one App Control for Business policy is **enforcing** (not audit-only) AND + has either built-in controls selected or an uploaded XML policy with a non-empty payload. + + Audit-only policies and upload-mode policies with no XML payload are reported but do not satisfy the pass + criterion, because they do not block untrusted executables. .EXAMPLE Test-MtIntuneAppControl - Returns true if at least one App Control for Business policy is configured. + Returns true if at least one App Control for Business policy is configured in enforce mode. .LINK https://maester.dev/docs/commands/Test-MtIntuneAppControl @@ -61,9 +66,10 @@ $auditModeId = 'device_vendor_msft_policy_config_applicationcontrolv2_auditmode' $managedInstallerId = 'device_vendor_msft_policy_config_applicationcontrolv2_trustappsfrommanagedinstaller' $goodReputationId = 'device_vendor_msft_policy_config_applicationcontrolv2_trustappswithgoodreputation' + # When 'Custom policy upload' is selected, the XML payload is delivered in a simpleSettingValue child setting. + $policyXmlId = 'device_vendor_msft_policy_config_applicationcontrolv2_policy' $policyResults = [System.Collections.Generic.List[hashtable]]::new() - $hasConfiguredPolicy = $false foreach ($policy in $appControlPolicies) { Write-Verbose "Checking App Control policy: $($policy.name) ($($policy.id))" @@ -73,9 +79,12 @@ $policyDetail = @{ Name = $policy.name BuildOptions = 'Not configured' + PolicyXml = 'N/A' AuditMode = 'Not configured' ManagedInstaller = 'Not configured' GoodReputation = 'Not configured' + Enforcing = $false + HasActiveControl = $false } foreach ($setting in $settingsResponse) { @@ -83,31 +92,52 @@ if ($defId -eq $buildOptionsId) { $val = $setting.settingInstance.choiceSettingValue.value - if ($val -like '*built_in_controls_selected') { + $isBuiltIn = $val -like '*built_in_controls_selected' + $isUpload = $val -like '*upload_policy_selected' + if ($isBuiltIn) { $policyDetail.BuildOptions = 'Built-in controls' - $hasConfiguredPolicy = $true - } elseif ($val -like '*upload_policy_selected') { + $policyDetail.HasActiveControl = $true + } elseif ($isUpload) { $policyDetail.BuildOptions = 'Custom policy upload' - $hasConfiguredPolicy = $true + # HasActiveControl is set later only if a non-empty XML payload is present. } Write-Verbose " BuildOptions: $($policyDetail.BuildOptions)" # Child settings are nested under the build options choice foreach ($child in $setting.settingInstance.choiceSettingValue.children) { - if ($child.settingDefinitionId -eq $auditModeId) { - $childVal = $child.choiceSettingValue.value - $policyDetail.AuditMode = if ($childVal -like '*_enabled') { 'Enabled (audit only)' } else { 'Disabled (enforcing)' } - Write-Verbose " AuditMode: $($policyDetail.AuditMode)" - } - if ($child.settingDefinitionId -eq $managedInstallerId) { - $childVal = $child.choiceSettingValue.value - $policyDetail.ManagedInstaller = if ($childVal -like '*_enabled') { 'Enabled' } else { 'Disabled' } - Write-Verbose " ManagedInstaller: $($policyDetail.ManagedInstaller)" - } - if ($child.settingDefinitionId -eq $goodReputationId) { - $childVal = $child.choiceSettingValue.value - $policyDetail.GoodReputation = if ($childVal -like '*_enabled') { 'Enabled' } else { 'Disabled' } - Write-Verbose " GoodReputation: $($policyDetail.GoodReputation)" + switch ($child.settingDefinitionId) { + $auditModeId { + $childVal = $child.choiceSettingValue.value + if ($childVal -like '*_enabled') { + $policyDetail.AuditMode = 'Enabled (audit only)' + $policyDetail.Enforcing = $false + } else { + $policyDetail.AuditMode = 'Disabled (enforcing)' + $policyDetail.Enforcing = $true + } + Write-Verbose " AuditMode: $($policyDetail.AuditMode)" + } + $managedInstallerId { + $childVal = $child.choiceSettingValue.value + $policyDetail.ManagedInstaller = if ($childVal -like '*_enabled') { 'Enabled' } else { 'Disabled' } + Write-Verbose " ManagedInstaller: $($policyDetail.ManagedInstaller)" + } + $goodReputationId { + $childVal = $child.choiceSettingValue.value + $policyDetail.GoodReputation = if ($childVal -like '*_enabled') { 'Enabled' } else { 'Disabled' } + Write-Verbose " GoodReputation: $($policyDetail.GoodReputation)" + } + $policyXmlId { + # Custom-uploaded code-integrity XML payload + $xmlVal = $child.simpleSettingValue.value + if (-not [string]::IsNullOrWhiteSpace($xmlVal)) { + $policyDetail.PolicyXml = "Present ($($xmlVal.Length) chars)" + if ($isUpload) { $policyDetail.HasActiveControl = $true } + } else { + $policyDetail.PolicyXml = 'Empty' + } + Write-Verbose " PolicyXml: $($policyDetail.PolicyXml)" + } } } } @@ -116,30 +146,39 @@ $policyResults.Add($policyDetail) } + # Pass: at least one policy with an active control AND in enforce mode. + $enforcingActive = @($policyResults | Where-Object { $_.HasActiveControl -and $_.Enforcing }) + $hasEnforcingPolicy = $enforcingActive.Count -gt 0 + # Build result markdown $testResultMarkdown = "Found $($appControlPolicies.Count) App Control for Business policy/policies in Intune.`n`n" - $testResultMarkdown += "| Policy | Build Options | Audit Mode | Managed Installer | ISG Reputation |`n" - $testResultMarkdown += "| --- | --- | --- | --- | --- |`n" + $testResultMarkdown += "**Pass criteria:** At least one App Control policy must be **enforcing** (audit mode disabled) AND have either built-in controls selected or an uploaded XML policy with a non-empty payload.`n`n" + $testResultMarkdown += "| Policy | Build Options | Policy XML | Audit Mode | Managed Installer | ISG Reputation |`n" + $testResultMarkdown += "| --- | --- | --- | --- | --- | --- |`n" foreach ($p in $policyResults) { - $testResultMarkdown += "| $($p.Name) | $($p.BuildOptions) | $($p.AuditMode) | $($p.ManagedInstaller) | $($p.GoodReputation) |`n" + $testResultMarkdown += "| $($p.Name) | $($p.BuildOptions) | $($p.PolicyXml) | $($p.AuditMode) | $($p.ManagedInstaller) | $($p.GoodReputation) |`n" } - if ($hasConfiguredPolicy) { - $testResultMarkdown += "`n**Result:** Well done. At least one App Control for Business policy is configured." + if ($hasEnforcingPolicy) { + $testResultMarkdown += "`n**Result:** Well done. $($enforcingActive.Count) App Control for Business policy/policies are configured in **enforce mode** with active controls." + + $auditOnly = @($policyResults | Where-Object { $_.HasActiveControl -and -not $_.Enforcing }) + if ($auditOnly.Count -gt 0) { + $testResultMarkdown += "`n`n> **Note:** $($auditOnly.Count) additional policy/policies are in **Audit mode** only. " + $testResultMarkdown += "Audit-only policies log untrusted executables but do not block them." + } - # Warn about audit-only policies - $auditOnly = @($policyResults | Where-Object { $_.AuditMode -eq 'Enabled (audit only)' }) - if ($auditOnly.Count -gt 0 -and $auditOnly.Count -eq $policyResults.Count) { - $testResultMarkdown += "`n`n> **Note:** All App Control policies are in **Audit mode**. " - $testResultMarkdown += "Consider transitioning to **Enforce mode** after validating that legitimate applications are not blocked." + $emptyUpload = @($policyResults | Where-Object { $_.BuildOptions -eq 'Custom policy upload' -and $_.PolicyXml -eq 'Empty' }) + if ($emptyUpload.Count -gt 0) { + $testResultMarkdown += "`n`n> **Note:** $($emptyUpload.Count) policy/policies are set to **Custom policy upload** but contain no XML payload." } Add-MtTestResultDetail -Result $testResultMarkdown return $true } else { - $testResultMarkdown += "`n**Result:** No App Control policies have active configurations.`n`n" - $testResultMarkdown += "> **Risk:** Without App Control, any executable can run on managed devices, " - $testResultMarkdown += "leaving them vulnerable to malware, ransomware, and unauthorized software." + $testResultMarkdown += "`n**Result:** No App Control for Business policy is enforcing with an active control.`n`n" + $testResultMarkdown += "> **Risk:** Audit-only policies and upload-mode policies with no XML payload do not block untrusted executables. " + $testResultMarkdown += "Without an enforcing App Control policy, any executable can run on managed devices, leaving them vulnerable to malware, ransomware, and unauthorized software." Add-MtTestResultDetail -Result $testResultMarkdown return $false } diff --git a/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 index b2ec8d73d..824ee8943 100644 --- a/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 @@ -10,18 +10,20 @@ Windows LAPS (Local Administrator Password Solution) automatically rotates and backs up local admin passwords, preventing lateral movement attacks that exploit shared or stale local admin credentials. - Key settings evaluated: - - BackupDirectory: Must be set to 1 (Entra ID) to store passwords in the cloud. - - PasswordComplexity: Recommended 4 (large letters + small letters + numbers + special characters) or 8. - - PasswordLength: Recommended >= 14 characters. - - AutomaticAccountManagementEnabled: Whether LAPS auto-manages the local admin account. + Pass criteria (all required on at least one LAPS policy): + - BackupDirectory = 1 (Entra ID) to store passwords in the cloud. + - PasswordComplexity >= 4 (large + small letters + numbers + special characters; values 4 or 8 are accepted). + - PasswordLength >= 14 characters. + - PostAuthenticationActions configured to a non-zero value (reset password, optionally logoff/reboot/terminate). - The test passes if at least one LAPS policy is configured with BackupDirectory set to Entra ID. + AutomaticAccountManagementEnabled is reported for completeness but does not affect pass/fail. + + The test passes if at least one LAPS policy meets all four criteria above. .EXAMPLE Test-MtIntuneLAPSConfiguration - Returns true if at least one LAPS policy backs up passwords to Entra ID. + Returns true if at least one LAPS policy meets the secure baseline (Entra ID backup, complexity >= 4, length >= 14, post-auth action configured). .LINK https://maester.dev/docs/commands/Test-MtIntuneLAPSConfiguration @@ -82,8 +84,13 @@ '_8' = 'Large + small + numbers + special (improved)' } + # Pass-criteria thresholds + $minPasswordLength = 14 + $minComplexitySuffixes = @('_4', '_8') # 4-class or improved 4-class + $validPostAuthSuffixes = @('_1', '_3', '_5', '_11') + $policyResults = [System.Collections.Generic.List[hashtable]]::new() - $hasEntraBackup = $false + $hasCompliantPolicy = $false foreach ($policy in $lapsPolicies) { Write-Verbose "Checking LAPS policy: $($policy.name) ($($policy.id))" @@ -91,15 +98,21 @@ $settingsResponse = @(Invoke-MtGraphRequest -RelativeUri $settingsUri -ApiVersion beta) $policyDetail = @{ - Name = $policy.name - BackupDirectory = 'Not configured' + Name = $policy.name + BackupDirectory = 'Not configured' PasswordComplexity = 'Not configured' - PasswordLength = 'Not configured' - PostAuthActions = 'Not configured' - PostAuthDelay = 'Not configured' - AutoAccountMgmt = 'Not configured' + PasswordLength = 'Not configured' + PostAuthActions = 'Not configured' + PostAuthDelay = 'Not configured' + AutoAccountMgmt = 'Not configured' + Compliant = 'No' } + $hasEntra = $false + $hasComplexity = $false + $hasLength = $false + $hasPostAuth = $false + foreach ($setting in $settingsResponse) { $defId = $setting.settingInstance.settingDefinitionId @@ -108,7 +121,7 @@ foreach ($suffix in $backupDirectoryLabels.Keys) { if ($val.EndsWith($suffix)) { $policyDetail.BackupDirectory = $backupDirectoryLabels[$suffix] - if ($suffix -eq '_1') { $hasEntraBackup = $true } + if ($suffix -eq '_1') { $hasEntra = $true } break } } @@ -122,11 +135,16 @@ $policyDetail.PasswordComplexity = $complexityLabels[$suffix] } } + foreach ($suffix in $minComplexitySuffixes) { + if ($val -and $val.EndsWith($suffix)) { $hasComplexity = $true; break } + } Write-Verbose " PasswordComplexity: $($policyDetail.PasswordComplexity)" } if ($defId -eq $passwordLengthId) { - $policyDetail.PasswordLength = "$($setting.settingInstance.simpleSettingValue.value) characters" + $lengthVal = [int]($setting.settingInstance.simpleSettingValue.value) + $policyDetail.PasswordLength = "$lengthVal characters" + if ($lengthVal -ge $minPasswordLength) { $hasLength = $true } Write-Verbose " PasswordLength: $($policyDetail.PasswordLength)" } @@ -136,6 +154,9 @@ elseif ($val -like '*_3') { $policyDetail.PostAuthActions = 'Reset password + logoff' } elseif ($val -like '*_5') { $policyDetail.PostAuthActions = 'Reset password + reboot' } elseif ($val -like '*_11') { $policyDetail.PostAuthActions = 'Reset password + logoff + terminate processes' } + foreach ($suffix in $validPostAuthSuffixes) { + if ($val -and $val.EndsWith($suffix)) { $hasPostAuth = $true; break } + } Write-Verbose " PostAuthActions: $($policyDetail.PostAuthActions)" } @@ -156,25 +177,31 @@ } } + if ($hasEntra -and $hasComplexity -and $hasLength -and $hasPostAuth) { + $policyDetail.Compliant = 'Yes' + $hasCompliantPolicy = $true + } + $policyResults.Add($policyDetail) } # Build result markdown $testResultMarkdown = "Found $($lapsPolicies.Count) Windows LAPS policy/policies in Intune.`n`n" - $testResultMarkdown += "| Policy | Backup Directory | Complexity | Length | Post-Auth Actions | Post-Auth Delay | Auto Account Mgmt |`n" - $testResultMarkdown += "| --- | --- | --- | --- | --- | --- | --- |`n" + $testResultMarkdown += "**Pass criteria:** Entra ID backup + Complexity >= 4-class + Length >= $minPasswordLength + Post-auth action configured.`n`n" + $testResultMarkdown += "| Policy | Backup Directory | Complexity | Length | Post-Auth Actions | Post-Auth Delay | Auto Account Mgmt | Meets baseline |`n" + $testResultMarkdown += "| --- | --- | --- | --- | --- | --- | --- | --- |`n" foreach ($p in $policyResults) { - $testResultMarkdown += "| $($p.Name) | $($p.BackupDirectory) | $($p.PasswordComplexity) | $($p.PasswordLength) | $($p.PostAuthActions) | $($p.PostAuthDelay) | $($p.AutoAccountMgmt) |`n" + $testResultMarkdown += "| $($p.Name) | $($p.BackupDirectory) | $($p.PasswordComplexity) | $($p.PasswordLength) | $($p.PostAuthActions) | $($p.PostAuthDelay) | $($p.AutoAccountMgmt) | $($p.Compliant) |`n" } - if ($hasEntraBackup) { - $testResultMarkdown += "`n**Result:** Well done. At least one LAPS policy backs up passwords to **Entra ID**." + if ($hasCompliantPolicy) { + $testResultMarkdown += "`n**Result:** Well done. At least one LAPS policy meets the secure baseline (Entra ID backup, complexity, length, and post-auth action)." Add-MtTestResultDetail -Result $testResultMarkdown return $true } else { - $testResultMarkdown += "`n**Result:** No LAPS policy is configured to back up passwords to **Entra ID**.`n`n" - $testResultMarkdown += "> **Risk:** Without cloud backup of local admin passwords, compromised or forgotten local admin credentials " - $testResultMarkdown += "cannot be recovered or rotated centrally, increasing lateral movement risk." + $testResultMarkdown += "`n**Result:** No LAPS policy meets all four baseline requirements (Entra ID backup, complexity >= 4-class, length >= $minPasswordLength, post-auth action).`n`n" + $testResultMarkdown += "> **Risk:** A LAPS policy that misses any of these settings provides reduced protection. Weak complexity or short passwords are easier to brute force; " + $testResultMarkdown += "without cloud backup, compromised local admin credentials cannot be rotated centrally; without a post-authentication action, a stolen password remains valid." Add-MtTestResultDetail -Result $testResultMarkdown return $false } diff --git a/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 index 0258ea945..0edaf9411 100644 --- a/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 @@ -21,12 +21,14 @@ - Only user-installed or sideloaded apps are subject to policy restrictions - Reduces false positives while maintaining security - The test passes if at least one App Control policy has the "Trust apps from managed installer" setting enabled. + The test passes if at least one App Control policy is in **enforce mode** (audit mode disabled) AND has + the "Trust apps from managed installer" setting enabled. Managed Installer enabled on an audit-only + policy does not actively trust deployed apps because the underlying App Control policy is not enforcing. .EXAMPLE Test-MtIntuneManagedInstallerRules - Returns true if at least one App Control policy has Managed Installer enabled. + Returns true if at least one enforcing App Control policy has Managed Installer enabled. .LINK https://maester.dev/docs/commands/Test-MtIntuneManagedInstallerRules @@ -63,7 +65,6 @@ $managedInstallerId = 'device_vendor_msft_policy_config_applicationcontrolv2_trustappsfrommanagedinstaller' $policyResults = [System.Collections.Generic.List[hashtable]]::new() - $hasManagedInstaller = $false foreach ($policy in $appControlPolicies) { Write-Verbose "Checking App Control policy: $($policy.name) ($($policy.id))" @@ -74,6 +75,8 @@ Name = $policy.name ManagedInstaller = 'Not configured' AuditMode = 'Not configured' + MIEnabled = $false + Enforcing = $false } foreach ($setting in $settingsResponse) { @@ -85,7 +88,7 @@ $childVal = $child.choiceSettingValue.value if ($childVal -like '*_enabled') { $policyDetail.ManagedInstaller = 'Enabled' - $hasManagedInstaller = $true + $policyDetail.MIEnabled = $true } else { $policyDetail.ManagedInstaller = 'Disabled' } @@ -93,7 +96,13 @@ } if ($child.settingDefinitionId -eq 'device_vendor_msft_policy_config_applicationcontrolv2_auditmode') { $childVal = $child.choiceSettingValue.value - $policyDetail.AuditMode = if ($childVal -like '*_enabled') { 'Audit' } else { 'Enforce' } + if ($childVal -like '*_enabled') { + $policyDetail.AuditMode = 'Audit' + $policyDetail.Enforcing = $false + } else { + $policyDetail.AuditMode = 'Enforce' + $policyDetail.Enforcing = $true + } Write-Verbose " AuditMode: $($policyDetail.AuditMode)" } } @@ -103,24 +112,32 @@ $policyResults.Add($policyDetail) } + # Pass: at least one enforcing App Control policy with Managed Installer enabled. + $enforcingMI = @($policyResults | Where-Object { $_.MIEnabled -and $_.Enforcing }) + $hasEnforcingMI = $enforcingMI.Count -gt 0 + # Build result markdown $testResultMarkdown = "Found $($appControlPolicies.Count) App Control for Business policy/policies in Intune.`n`n" + $testResultMarkdown += "**Pass criteria:** At least one App Control policy must be in **Enforce** mode AND have **Managed Installer** enabled.`n`n" $testResultMarkdown += "| Policy | Managed Installer | Enforcement Mode |`n" $testResultMarkdown += "| --- | --- | --- |`n" foreach ($p in $policyResults) { $testResultMarkdown += "| $($p.Name) | $($p.ManagedInstaller) | $($p.AuditMode) |`n" } - if ($hasManagedInstaller) { - $testResultMarkdown += "`n**Result:** Well done. At least one App Control policy has **Managed Installer** enabled." + if ($hasEnforcingMI) { + $testResultMarkdown += "`n**Result:** Well done. $($enforcingMI.Count) App Control policy/policies are in **Enforce** mode with **Managed Installer** enabled." $testResultMarkdown += " Applications deployed through Intune/SCCM will be automatically trusted." Add-MtTestResultDetail -Result $testResultMarkdown return $true } else { - $testResultMarkdown += "`n**Result:** No App Control policies have **Managed Installer** enabled.`n`n" - $testResultMarkdown += "> **Risk:** Without Managed Installer, applications deployed via Intune may be blocked by App Control policies. " - $testResultMarkdown += "This leads to false positives and help desk tickets. Enable 'Trust apps from managed installer' to " - $testResultMarkdown += "automatically trust IT-deployed software." + $auditMI = @($policyResults | Where-Object { $_.MIEnabled -and -not $_.Enforcing }) + $testResultMarkdown += "`n**Result:** No App Control policies have **Managed Installer** enabled in **Enforce** mode.`n`n" + if ($auditMI.Count -gt 0) { + $testResultMarkdown += "$($auditMI.Count) policy/policies have Managed Installer enabled but the underlying App Control policy is in **Audit** mode, so deployed apps are not actively trusted.`n`n" + } + $testResultMarkdown += "> **Risk:** Without Managed Installer on an enforcing App Control policy, applications deployed via Intune may be blocked once App Control transitions to Enforce mode. " + $testResultMarkdown += "This leads to false positives and help desk tickets. Enable 'Trust apps from managed installer' on an enforcing policy to automatically trust IT-deployed software." Add-MtTestResultDetail -Result $testResultMarkdown return $false } diff --git a/website/docs/tests/maester/MT.1177.md b/website/docs/tests/maester/MT.1177.md index bd9e62448..f474bd6db 100644 --- a/website/docs/tests/maester/MT.1177.md +++ b/website/docs/tests/maester/MT.1177.md @@ -11,15 +11,17 @@ sidebar_class_name: hidden Checks Intune Endpoint Security Account Protection policies for Windows Local Administrator Password Solution (LAPS) profiles that back up local administrator passwords to Microsoft Entra ID. -Windows LAPS automatically rotates and backs up local administrator passwords, preventing lateral movement attacks that exploit shared or stale local admin credentials. The test passes if at least one LAPS policy is configured with **Backup Directory** set to **Azure AD only** (`_1`). +Windows LAPS automatically rotates and backs up local administrator passwords, preventing lateral movement attacks that exploit shared or stale local admin credentials. Key settings evaluated: - **Backup Directory**: Must be set to `Azure AD only` (`_1`) so passwords are stored in Entra ID and can be retrieved centrally. -- **Password Complexity**: Recommended `Large + small + numbers + special` (`_4`) or improved (`_8`). -- **Password Length**: Recommended **>= 14** characters. -- **Post-Authentication Actions**: Reset password (and optionally logoff or reboot) after the configured delay. -- **Automatic Account Management**: Whether LAPS auto-manages the local admin account. +- **Password Complexity**: Must be `Large + small + numbers + special` (`_4`) or improved (`_8`). +- **Password Length**: Must be **>= 14** characters. +- **Post-Authentication Actions**: Must be set to a defined action (`_1`/`_3`/`_5`/`_11`) so the password rotates after use. +- **Automatic Account Management**: Whether LAPS auto-manages the local admin account (informational). + +The test passes if **at least one LAPS policy** meets **all** of the criteria above (Entra backup AND complexity >= `_4` AND length >= 14 AND a defined post-auth action). ## How to fix diff --git a/website/docs/tests/maester/MT.1178.md b/website/docs/tests/maester/MT.1178.md index 36998fed0..4848da3a2 100644 --- a/website/docs/tests/maester/MT.1178.md +++ b/website/docs/tests/maester/MT.1178.md @@ -20,7 +20,15 @@ Each ASR rule can operate in one of four modes: - **Warn** — Warns the user before allowing the behavior to proceed. - **Disabled** — Rule is not active. -The test passes if at least one ASR policy has at least one rule configured in **Block** or **Audit** mode. **Warn** is a supported ASR rule state but does not satisfy this control's pass criteria. Policies with all rules in **Audit** mode trigger an informational note recommending a transition to **Block** mode. +The test passes if **every rule in the Microsoft Defender ASR Standard Protection baseline** is configured in **Block** or **Audit** mode in at least one ASR policy. The Standard Protection baseline is the published minimum recommended set for initial ASR deployment: + +1. Block abuse of exploited vulnerable signed drivers +2. Block credential stealing from LSASS +3. Block persistence through WMI event subscription + +See [Microsoft Defender ASR rules deployment guide](https://learn.microsoft.com/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment-implement) for the canonical baseline definition. + +Additional ASR rules detected in tenant policies are reported for visibility but do not affect the pass/fail result. **Warn** is a supported ASR rule state but does not satisfy the baseline. Baseline rules in **Audit** mode trigger an informational note recommending a transition to **Block** mode. ## How to fix diff --git a/website/docs/tests/maester/MT.1179.md b/website/docs/tests/maester/MT.1179.md index 2f1e33fd1..91f3cfc86 100644 --- a/website/docs/tests/maester/MT.1179.md +++ b/website/docs/tests/maester/MT.1179.md @@ -16,11 +16,12 @@ App Control for Business restricts which applications and drivers are allowed to Key settings evaluated: - **Build Options** — Whether the policy uses built-in controls (`built_in_controls_selected`) or a custom uploaded policy (`upload_policy_selected`). +- **Policy XML** — For uploaded policies, whether an XML code-integrity payload is actually present (not empty). - **Audit Mode** — Whether the policy is in audit mode (logging only) or enforce mode (blocking). - **Trust apps from managed installer** — Whether apps deployed via Intune/SCCM are automatically trusted. - **Trust apps with good reputation (ISG)** — Whether apps with good Intelligent Security Graph reputation are trusted. -The test passes if at least one App Control for Business policy exists with build options configured. Policies still in **Audit mode** trigger an informational note recommending a transition to **Enforce mode** after validation. +The test passes if **at least one App Control policy is enforcing** (audit mode disabled) **AND** has either built-in controls selected or an uploaded XML policy with a non-empty payload. Audit-only policies and upload-mode policies with no XML payload are reported but do not satisfy the pass criterion, because they do not block untrusted executables. ## How to fix diff --git a/website/docs/tests/maester/MT.1180.md b/website/docs/tests/maester/MT.1180.md index 7c0a50a37..7f140d517 100644 --- a/website/docs/tests/maester/MT.1180.md +++ b/website/docs/tests/maester/MT.1180.md @@ -25,7 +25,7 @@ When Managed Installer is enabled in an App Control for Business policy, applica - Only user-installed, sideloaded, or internet-downloaded apps are subject to policy restrictions. - Reduces false positives while maintaining security against unauthorized software. -The test passes if at least one App Control for Business policy has **Trust apps from managed installer** enabled. +The test passes if **at least one App Control for Business policy is in Enforce mode** (audit mode disabled) **AND** has **Trust apps from managed installer** enabled. Managed Installer enabled on an audit-only policy does not actively trust deployed apps because the underlying App Control policy is not blocking untrusted executables. ## How to fix From 1a1ffd7ee5869999174d875181115b58125120c0 Mon Sep 17 00:00:00 2001 From: Ofir Gavish Date: Mon, 18 May 2026 22:22:38 +0300 Subject: [PATCH 14/14] Address Copilot reviewer follow-up on PR #1676 Code fixes: - Test-MtIntuneASRRules.ps1: remove duplicate .EXAMPLE block. - Test-MtIntuneManagedInstallerRules.ps1: gate pass on active App Control (built-in controls OR non-empty uploaded XML). An enforce-mode upload policy with empty XML + MI enabled no longer falsely passes. Mirrors the active-control gate used by MT.1179. Report empty-XML MI policies alongside audit-mode MI policies in failure output. Expand result table with Build Options + Policy XML columns. Update .DESCRIPTION and .EXAMPLE accordingly. Companion docs (powershell/public/maester/intune/*.md): - LAPS: pass now requires Entra backup AND complexity >= _4 AND length >= 14 AND a defined post-auth action. - ASRRules: pass now requires every Standard Protection baseline rule (drivers, LSASS, WMI persistence) in Block or Audit across the union of all ASR policies. Links to MS Defender baseline. - AppControl: pass now requires Enforce mode AND active control (built-in or non-empty uploaded XML). - ManagedInstallerRules: pass now requires Enforce mode AND MI enabled AND active control. Website docs: - Regenerated MT.1177.md, MT.1178.md, MT.1179.md, MT.1180.md via website/scripts/generate-test-docs.mjs so they reflect the authoritative companion .md content (no longer hand-authored). --- .../maester/intune/Test-MtIntuneASRRules.md | 10 +- .../maester/intune/Test-MtIntuneASRRules.ps1 | 11 +- .../maester/intune/Test-MtIntuneAppControl.md | 5 +- .../intune/Test-MtIntuneLAPSConfiguration.md | 10 +- .../Test-MtIntuneManagedInstallerRules.md | 2 +- .../Test-MtIntuneManagedInstallerRules.ps1 | 107 ++++++++++----- website/docs/tests/maester/MT.1177.md | 98 ++++++++++---- website/docs/tests/maester/MT.1178.md | 126 ++++++++++++++---- website/docs/tests/maester/MT.1179.md | 96 +++++++++---- website/docs/tests/maester/MT.1180.md | 83 +++++++++--- 10 files changed, 406 insertions(+), 142 deletions(-) diff --git a/powershell/public/maester/intune/Test-MtIntuneASRRules.md b/powershell/public/maester/intune/Test-MtIntuneASRRules.md index bffcc9a3e..43742db9f 100644 --- a/powershell/public/maester/intune/Test-MtIntuneASRRules.md +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.md @@ -17,7 +17,15 @@ Each ASR rule can operate in one of four modes: - **Warn**: Warns the user before allowing the behavior to proceed - **Disabled**: Rule is not active -The test passes if at least one ASR policy has at least one rule configured in **Block** or **Audit** mode. **Warn** is a supported ASR rule state, but it does not satisfy this control's pass criteria. Policies with all rules in **Audit** mode will trigger an informational note recommending a transition to **Block** mode. +The test passes if **every rule in the Microsoft Defender ASR Standard Protection baseline** is configured in **Block** or **Audit** mode across the union of all ASR policies in the tenant. The Standard Protection baseline is the minimum recommended set Microsoft publishes for initial ASR deployment: + +1. Block abuse of exploited vulnerable signed drivers +2. Block credential stealing from LSASS +3. Block persistence through WMI event subscription + +See the [Microsoft Defender ASR rules deployment guide](https://learn.microsoft.com/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment-implement) for the canonical baseline definition. + +Additional ASR rules detected in tenant policies are reported for visibility but do not affect the pass/fail result. **Warn** is a supported ASR rule state but does not satisfy the baseline. Baseline rules in **Audit** mode will trigger an informational note recommending a transition to **Block** mode. #### Remediation action: diff --git a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 index 318295b4d..4725ec4a2 100644 --- a/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 @@ -32,16 +32,7 @@ .EXAMPLE Test-MtIntuneASRRules - Returns true if every Standard Protection baseline rule is configured (Block or Audit) in at least one ASR policy - - See https://learn.microsoft.com/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment-implement - - Additional ASR rules detected in tenant policies are reported for visibility but do not affect the pass/fail result. - - .EXAMPLE - Test-MtIntuneASRRules - - Returns true if every Standard Protection baseline rule is configured (Block or Audit) in at least one ASR policy. + Returns true if every Standard Protection baseline rule is configured in Block or Audit mode across the union of all ASR policies in the tenant. .LINK https://maester.dev/docs/commands/Test-MtIntuneASRRules diff --git a/powershell/public/maester/intune/Test-MtIntuneAppControl.md b/powershell/public/maester/intune/Test-MtIntuneAppControl.md index 051f015aa..b0330346d 100644 --- a/powershell/public/maester/intune/Test-MtIntuneAppControl.md +++ b/powershell/public/maester/intune/Test-MtIntuneAppControl.md @@ -4,12 +4,13 @@ App Control for Business restricts which applications and drivers are allowed to Key settings this test evaluates: -- **Build Options**: Whether the policy uses built-in controls or a custom uploaded policy +- **Build Options**: Whether the policy uses built-in controls (`built_in_controls_selected`) or a custom uploaded policy (`upload_policy_selected`) +- **Policy XML**: For uploaded policies, whether an XML code-integrity payload is actually present (not empty) - **Audit Mode**: Whether the policy is in audit mode (logging only) or enforce mode (blocking) - **Managed Installer**: Whether apps deployed via Intune/SCCM are automatically trusted - **Intelligent Security Graph (ISG) Reputation**: Whether apps with good reputation scores are trusted -The test passes if at least one App Control for Business policy exists with built-in controls or a custom policy configured. Policies in **Audit mode** will trigger an informational note recommending a transition to **Enforce mode** after validation. +The test passes if **at least one App Control for Business policy is enforcing** (audit mode disabled) **AND** has either built-in controls selected or an uploaded XML policy with a non-empty payload. Audit-only policies and upload-mode policies with no XML payload are reported but do not satisfy the pass criterion, because they do not block untrusted executables. #### Remediation action: diff --git a/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.md b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.md index 78eacbb42..f762f2fad 100644 --- a/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.md +++ b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.md @@ -5,12 +5,12 @@ Windows LAPS (Local Administrator Password Solution) automatically rotates and b Key settings this test evaluates: - **Backup Directory**: Must be set to **Azure AD only** (Entra ID) to store passwords in the cloud where they can be retrieved by authorized admins. -- **Password Complexity**: Recommended setting is large + small letters + numbers + special characters. -- **Password Length**: Recommended minimum of 14 characters. -- **Post-Authentication Actions**: What happens after the LAPS password is used (reset, logoff, terminate processes). -- **Automatic Account Management**: Whether LAPS auto-manages the local admin account lifecycle. +- **Password Complexity**: Must be `Large + small + numbers + special` (`_4`) or improved (`_8`). +- **Password Length**: Must be **>= 14** characters. +- **Post-Authentication Actions**: Must be set to a defined action (`_1` reset / `_3` reset+logoff / `_5` reset+reboot / `_11` disabled-with-rotation) so the password rotates after use. +- **Automatic Account Management**: Whether LAPS auto-manages the local admin account lifecycle (informational). -The test passes if at least one LAPS policy has **Backup Directory** set to **Azure AD only** (Entra ID). +The test passes if **at least one LAPS policy** meets **all** of the criteria above (Entra backup AND complexity >= `_4` AND length >= 14 AND a defined post-auth action). A policy that only sets Backup Directory is no longer sufficient. #### Remediation action: diff --git a/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.md b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.md index 60f54ad5d..3ec1a72b0 100644 --- a/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.md +++ b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.md @@ -14,7 +14,7 @@ When Managed Installer is enabled in an App Control for Business policy, applica - Reduces false positives while maintaining security against unauthorized software - Simplifies ongoing policy maintenance -The test passes if at least one App Control for Business policy has the **Trust apps from managed installer** setting enabled. +The test passes if **at least one App Control for Business policy is in Enforce mode** (audit mode disabled) **AND** has **Trust apps from managed installer** enabled **AND** has an active control (built-in controls selected OR a non-empty uploaded XML payload). Managed Installer enabled on an audit-only App Control, or on an enforce-mode upload policy with an empty XML payload, does not actively trust deployed apps because the underlying App Control policy is not blocking anything. This mirrors the active-control gate used by MT.1179. #### Remediation action: diff --git a/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 index 0edaf9411..43cdee7a6 100644 --- a/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 +++ b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 @@ -22,13 +22,16 @@ - Reduces false positives while maintaining security The test passes if at least one App Control policy is in **enforce mode** (audit mode disabled) AND has - the "Trust apps from managed installer" setting enabled. Managed Installer enabled on an audit-only - policy does not actively trust deployed apps because the underlying App Control policy is not enforcing. + the "Trust apps from managed installer" setting enabled AND has an **active control** (built-in controls + selected OR a non-empty uploaded XML payload). Managed Installer enabled on an audit-only policy, or on + an enforce-mode upload policy with an empty XML payload, does not actively trust deployed apps because + the underlying App Control policy is not blocking anything. This mirrors the active-control gate used by + MT.1179. .EXAMPLE Test-MtIntuneManagedInstallerRules - Returns true if at least one enforcing App Control policy has Managed Installer enabled. + Returns true if at least one enforcing App Control policy has Managed Installer enabled and an active control. .LINK https://maester.dev/docs/commands/Test-MtIntuneManagedInstallerRules @@ -63,6 +66,10 @@ } $managedInstallerId = 'device_vendor_msft_policy_config_applicationcontrolv2_trustappsfrommanagedinstaller' + $buildOptionsId = 'device_vendor_msft_policy_config_applicationcontrolv2_buildoptions' + $auditModeId = 'device_vendor_msft_policy_config_applicationcontrolv2_auditmode' + # When 'Custom policy upload' is selected, the XML payload is delivered in a simpleSettingValue child setting. + $policyXmlId = 'device_vendor_msft_policy_config_applicationcontrolv2_policy' $policyResults = [System.Collections.Generic.List[hashtable]]::new() @@ -73,37 +80,64 @@ $policyDetail = @{ Name = $policy.name + BuildOptions = 'Not configured' + PolicyXml = 'N/A' ManagedInstaller = 'Not configured' AuditMode = 'Not configured' MIEnabled = $false Enforcing = $false + HasActiveControl = $false } foreach ($setting in $settingsResponse) { $defId = $setting.settingInstance.settingDefinitionId - if ($defId -eq 'device_vendor_msft_policy_config_applicationcontrolv2_buildoptions') { + if ($defId -eq $buildOptionsId) { + $val = $setting.settingInstance.choiceSettingValue.value + $isBuiltIn = $val -like '*built_in_controls_selected' + $isUpload = $val -like '*upload_policy_selected' + if ($isBuiltIn) { + $policyDetail.BuildOptions = 'Built-in controls' + $policyDetail.HasActiveControl = $true + } elseif ($isUpload) { + $policyDetail.BuildOptions = 'Custom policy upload' + # HasActiveControl is set later only if a non-empty XML payload is present. + } + Write-Verbose " BuildOptions: $($policyDetail.BuildOptions)" + foreach ($child in $setting.settingInstance.choiceSettingValue.children) { - if ($child.settingDefinitionId -eq $managedInstallerId) { - $childVal = $child.choiceSettingValue.value - if ($childVal -like '*_enabled') { - $policyDetail.ManagedInstaller = 'Enabled' - $policyDetail.MIEnabled = $true - } else { - $policyDetail.ManagedInstaller = 'Disabled' + switch ($child.settingDefinitionId) { + $managedInstallerId { + $childVal = $child.choiceSettingValue.value + if ($childVal -like '*_enabled') { + $policyDetail.ManagedInstaller = 'Enabled' + $policyDetail.MIEnabled = $true + } else { + $policyDetail.ManagedInstaller = 'Disabled' + } + Write-Verbose " ManagedInstaller: $($policyDetail.ManagedInstaller)" } - Write-Verbose " ManagedInstaller: $($policyDetail.ManagedInstaller)" - } - if ($child.settingDefinitionId -eq 'device_vendor_msft_policy_config_applicationcontrolv2_auditmode') { - $childVal = $child.choiceSettingValue.value - if ($childVal -like '*_enabled') { - $policyDetail.AuditMode = 'Audit' - $policyDetail.Enforcing = $false - } else { - $policyDetail.AuditMode = 'Enforce' - $policyDetail.Enforcing = $true + $auditModeId { + $childVal = $child.choiceSettingValue.value + if ($childVal -like '*_enabled') { + $policyDetail.AuditMode = 'Audit' + $policyDetail.Enforcing = $false + } else { + $policyDetail.AuditMode = 'Enforce' + $policyDetail.Enforcing = $true + } + Write-Verbose " AuditMode: $($policyDetail.AuditMode)" + } + $policyXmlId { + $xmlVal = $child.simpleSettingValue.value + if (-not [string]::IsNullOrWhiteSpace($xmlVal)) { + $policyDetail.PolicyXml = "Present ($($xmlVal.Length) chars)" + if ($isUpload) { $policyDetail.HasActiveControl = $true } + } else { + $policyDetail.PolicyXml = 'Empty' + } + Write-Verbose " PolicyXml: $($policyDetail.PolicyXml)" } - Write-Verbose " AuditMode: $($policyDetail.AuditMode)" } } } @@ -112,32 +146,39 @@ $policyResults.Add($policyDetail) } - # Pass: at least one enforcing App Control policy with Managed Installer enabled. - $enforcingMI = @($policyResults | Where-Object { $_.MIEnabled -and $_.Enforcing }) + # Pass: at least one enforcing App Control policy with Managed Installer enabled AND an active control + # (built-in controls selected OR a non-empty uploaded XML payload). An enforce-mode policy with an empty + # XML payload is not actively blocking anything, so Managed Installer on it does not represent real + # trust enforcement. This mirrors the active-control gate used by MT.1179. + $enforcingMI = @($policyResults | Where-Object { $_.MIEnabled -and $_.Enforcing -and $_.HasActiveControl }) $hasEnforcingMI = $enforcingMI.Count -gt 0 # Build result markdown $testResultMarkdown = "Found $($appControlPolicies.Count) App Control for Business policy/policies in Intune.`n`n" - $testResultMarkdown += "**Pass criteria:** At least one App Control policy must be in **Enforce** mode AND have **Managed Installer** enabled.`n`n" - $testResultMarkdown += "| Policy | Managed Installer | Enforcement Mode |`n" - $testResultMarkdown += "| --- | --- | --- |`n" + $testResultMarkdown += "**Pass criteria:** At least one App Control policy must be in **Enforce** mode AND have **Managed Installer** enabled AND have an active control (built-in controls selected OR a non-empty uploaded XML payload).`n`n" + $testResultMarkdown += "| Policy | Build Options | Policy XML | Managed Installer | Enforcement Mode |`n" + $testResultMarkdown += "| --- | --- | --- | --- | --- |`n" foreach ($p in $policyResults) { - $testResultMarkdown += "| $($p.Name) | $($p.ManagedInstaller) | $($p.AuditMode) |`n" + $testResultMarkdown += "| $($p.Name) | $($p.BuildOptions) | $($p.PolicyXml) | $($p.ManagedInstaller) | $($p.AuditMode) |`n" } if ($hasEnforcingMI) { - $testResultMarkdown += "`n**Result:** Well done. $($enforcingMI.Count) App Control policy/policies are in **Enforce** mode with **Managed Installer** enabled." + $testResultMarkdown += "`n**Result:** Well done. $($enforcingMI.Count) App Control policy/policies are in **Enforce** mode with **Managed Installer** enabled and an active control." $testResultMarkdown += " Applications deployed through Intune/SCCM will be automatically trusted." Add-MtTestResultDetail -Result $testResultMarkdown return $true } else { - $auditMI = @($policyResults | Where-Object { $_.MIEnabled -and -not $_.Enforcing }) - $testResultMarkdown += "`n**Result:** No App Control policies have **Managed Installer** enabled in **Enforce** mode.`n`n" + $auditMI = @($policyResults | Where-Object { $_.MIEnabled -and -not $_.Enforcing }) + $emptyXmlMI = @($policyResults | Where-Object { $_.MIEnabled -and $_.Enforcing -and -not $_.HasActiveControl }) + $testResultMarkdown += "`n**Result:** No App Control policies have **Managed Installer** enabled in **Enforce** mode with an active control.`n`n" if ($auditMI.Count -gt 0) { $testResultMarkdown += "$($auditMI.Count) policy/policies have Managed Installer enabled but the underlying App Control policy is in **Audit** mode, so deployed apps are not actively trusted.`n`n" } - $testResultMarkdown += "> **Risk:** Without Managed Installer on an enforcing App Control policy, applications deployed via Intune may be blocked once App Control transitions to Enforce mode. " - $testResultMarkdown += "This leads to false positives and help desk tickets. Enable 'Trust apps from managed installer' on an enforcing policy to automatically trust IT-deployed software." + if ($emptyXmlMI.Count -gt 0) { + $testResultMarkdown += "$($emptyXmlMI.Count) policy/policies are in **Enforce** mode with Managed Installer enabled, but the uploaded XML payload is empty so the App Control policy is not actively blocking anything.`n`n" + } + $testResultMarkdown += "> **Risk:** Without Managed Installer on an enforcing App Control policy that has an active control, applications deployed via Intune may be blocked once App Control transitions to active enforcement. " + $testResultMarkdown += "This leads to false positives and help desk tickets. Enable 'Trust apps from managed installer' on an enforcing policy with built-in controls or a non-empty uploaded XML to automatically trust IT-deployed software." Add-MtTestResultDetail -Result $testResultMarkdown return $false } diff --git a/website/docs/tests/maester/MT.1177.md b/website/docs/tests/maester/MT.1177.md index f474bd6db..1152a15ee 100644 --- a/website/docs/tests/maester/MT.1177.md +++ b/website/docs/tests/maester/MT.1177.md @@ -1,44 +1,96 @@ --- -title: MT.1177 - Ensure LAPS Configuration Policy is properly set -description: Checks Intune Endpoint Security Account Protection policies for Windows LAPS profiles that back up local administrator passwords to Microsoft Entra ID. +title: "MT.1177 - Ensure LAPS Configuration Policy is properly set" +description: "Ensure at least one Intune LAPS policy is configured to **back up local admin passwords to Microsoft Entra ID**. Windows LAPS (Local Administrator Password Solution) automatically rotates and backs up local administrator passwords on managed devices. Without LAPS, local admin accounts often share t…" slug: /tests/MT.1177 +className: generated-test-doc sidebar_class_name: hidden +hide_table_of_contents: true +keywords: + - "Maester" + - "Microsoft 365 security" + - "MT.1177" + - "High" + - "Intune" --- -# Ensure LAPS Configuration Policy is properly set + -## Description +# MT.1177 - Ensure LAPS Configuration Policy is properly set -Checks Intune Endpoint Security Account Protection policies for Windows Local Administrator Password Solution (LAPS) profiles that back up local administrator passwords to Microsoft Entra ID. +## Overview -Windows LAPS automatically rotates and backs up local administrator passwords, preventing lateral movement attacks that exploit shared or stale local admin credentials. +Ensure at least one Intune LAPS policy is configured to **back up local admin passwords to Microsoft Entra ID**. -Key settings evaluated: +Windows LAPS (Local Administrator Password Solution) automatically rotates and backs up local administrator passwords on managed devices. Without LAPS, local admin accounts often share the same password across all devices — if one device is compromised, an attacker can move laterally to every other device using the same credentials. -- **Backup Directory**: Must be set to `Azure AD only` (`_1`) so passwords are stored in Entra ID and can be retrieved centrally. +Key settings this test evaluates: + +- **Backup Directory**: Must be set to **Azure AD only** (Entra ID) to store passwords in the cloud where they can be retrieved by authorized admins. - **Password Complexity**: Must be `Large + small + numbers + special` (`_4`) or improved (`_8`). - **Password Length**: Must be **>= 14** characters. -- **Post-Authentication Actions**: Must be set to a defined action (`_1`/`_3`/`_5`/`_11`) so the password rotates after use. -- **Automatic Account Management**: Whether LAPS auto-manages the local admin account (informational). +- **Post-Authentication Actions**: Must be set to a defined action (`_1` reset / `_3` reset+logoff / `_5` reset+reboot / `_11` disabled-with-rotation) so the password rotates after use. +- **Automatic Account Management**: Whether LAPS auto-manages the local admin account lifecycle (informational). -The test passes if **at least one LAPS policy** meets **all** of the criteria above (Entra backup AND complexity >= `_4` AND length >= 14 AND a defined post-auth action). +The test passes if **at least one LAPS policy** meets **all** of the criteria above (Entra backup AND complexity >= `_4` AND length >= 14 AND a defined post-auth action). A policy that only sets Backup Directory is no longer sufficient. -## How to fix +#### Remediation action: -1. Navigate to the [Microsoft Intune admin center](https://intune.microsoft.com). +1. Navigate to [Microsoft Intune admin center](https://intune.microsoft.com). 2. Go to **Endpoint security** > **Account protection**. 3. Click **+ Create policy**. 4. Set **Platform** to **Windows 10 and later** and **Profile** to **Local admin password solution (Windows LAPS)**. -5. Configure: +5. Enter a policy name (e.g., "LAPS - Entra ID Backup"). +6. Configure the following settings: - **Backup Directory**: **Azure AD only** - - **Password Complexity**: `Large + small + numbers + special` or improved - - **Password Length**: **14** characters or more - - **Post-Authentication Actions**: **Reset password** (or stronger) - - **Post-Authentication Reset Delay**: 12 hours or fewer -6. Assign the policy to your Windows device groups and click **Create**. + - **Password Complexity**: **Large letters + small letters + numbers + special characters** + - **Password Length**: **21** (or at least 14) + - **Post-Authentication Actions**: **Reset password and logoff** + - **Post-Authentication Reset Delay**: **1 hour** + - **Administrator Account Name**: Leave default or specify custom account +7. Assign the policy to your device groups and click **Create**. + +#### Related links + +- [Microsoft Intune - Endpoint Security Account Protection](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/accountprotection) +- [Microsoft Learn - Windows LAPS with Microsoft Intune](https://learn.microsoft.com/en-us/mem/intune/protect/windows-laps-overview) +- [Microsoft Learn - Windows LAPS CSP reference](https://learn.microsoft.com/en-us/windows/client-management/mdm/laps-csp) +- [CIS Benchmark - Ensure LAPS is configured for local admin accounts](https://www.cisecurity.org/benchmark/microsoft_intune_for_windows) + +## Test Metadata + +| Field | Value | +| --- | --- | +| Test ID | MT.1177 | +| Severity | High | +| Suite | Maester | +| Category | Intune | +| PowerShell test | [Test-MtIntuneLAPSConfiguration](/docs/commands/Test-MtIntuneLAPSConfiguration) | +| Tags | Intune, Maester, MT.1177 | + +## Remediation + +1. Navigate to [Microsoft Intune admin center](https://intune.microsoft.com). +2. Go to **Endpoint security** > **Account protection**. +3. Click **+ Create policy**. +4. Set **Platform** to **Windows 10 and later** and **Profile** to **Local admin password solution (Windows LAPS)**. +5. Enter a policy name (e.g., "LAPS - Entra ID Backup"). +6. Configure the following settings: + - **Backup Directory**: **Azure AD only** + - **Password Complexity**: **Large letters + small letters + numbers + special characters** + - **Password Length**: **21** (or at least 14) + - **Post-Authentication Actions**: **Reset password and logoff** + - **Post-Authentication Reset Delay**: **1 hour** + - **Administrator Account Name**: Leave default or specify custom account +7. Assign the policy to your device groups and click **Create**. + +## Related Links + +- [Microsoft Intune - Endpoint Security Account Protection](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/accountprotection) +- [Microsoft Learn - Windows LAPS with Microsoft Intune](https://learn.microsoft.com/en-us/mem/intune/protect/windows-laps-overview) +- [Microsoft Learn - Windows LAPS CSP reference](https://learn.microsoft.com/en-us/windows/client-management/mdm/laps-csp) +- [CIS Benchmark - Ensure LAPS is configured for local admin accounts](https://www.cisecurity.org/benchmark/microsoft_intune_for_windows) -## Learn more +## Source -- [Windows LAPS overview](https://learn.microsoft.com/mem/intune/protect/windows-laps-overview) -- [Windows LAPS policy CSP](https://learn.microsoft.com/windows/client-management/mdm/laps-csp) -- [Account protection policies in Intune](https://learn.microsoft.com/mem/intune/protect/endpoint-security-account-protection-policy) +- Pester test: `tests\Maester\Intune\Test-MtIntunePlatform.Tests.ps1` +- PowerShell source: `powershell\public\maester\intune\Test-MtIntuneLAPSConfiguration.ps1` diff --git a/website/docs/tests/maester/MT.1178.md b/website/docs/tests/maester/MT.1178.md index 4848da3a2..3b936d46d 100644 --- a/website/docs/tests/maester/MT.1178.md +++ b/website/docs/tests/maester/MT.1178.md @@ -1,57 +1,133 @@ --- -title: MT.1178 - Ensure ASR Rules are configured correctly -description: Checks Intune Endpoint Security Attack Surface Reduction (ASR) policies for rules configured in Block or Audit mode. +title: "MT.1178 - Ensure ASR Rules are configured correctly" +description: "Ensure at least one Intune Attack Surface Reduction (ASR) policy has rules configured in **Block** or **Audit** mode. ASR rules reduce the attack surface of applications by preventing behaviors commonly abused by malware and threat actors. These rules target specific techniques such as: - **Office…" slug: /tests/MT.1178 +className: generated-test-doc sidebar_class_name: hidden +hide_table_of_contents: true +keywords: + - "Maester" + - "Microsoft 365 security" + - "MT.1178" + - "High" + - "Intune" --- -# Ensure ASR Rules are configured correctly + -## Description +# MT.1178 - Ensure ASR Rules are configured correctly -Checks Intune Endpoint Security Attack Surface Reduction (ASR) policies for rules configured in **Block** or **Audit** mode. +## Overview -ASR rules reduce the attack surface of applications by preventing behaviors commonly abused by malware — such as Office macros spawning child processes, credential theft from LSASS, execution of obfuscated scripts, and email-borne threats. +Ensure at least one Intune Attack Surface Reduction (ASR) policy has rules configured in **Block** or **Audit** mode. + +ASR rules reduce the attack surface of applications by preventing behaviors commonly abused by malware and threat actors. These rules target specific techniques such as: + +- **Office macros** spawning child processes or injecting code into other processes +- **Credential theft** from LSASS (Local Security Authority Subsystem Service) +- **Script-based attacks** using obfuscated JavaScript, VBScript, or PowerShell +- **Email-borne threats** executing content from Outlook or webmail +- **Ransomware** advanced protection heuristics +- **USB-based attacks** running untrusted unsigned processes +- **Persistence** through WMI event subscriptions Each ASR rule can operate in one of four modes: -- **Block** — Actively prevents the behavior (recommended for production after testing). -- **Audit** — Logs the event without blocking (recommended for initial rollout). -- **Warn** — Warns the user before allowing the behavior to proceed. -- **Disabled** — Rule is not active. +- **Block**: Actively prevents the behavior (recommended for production after testing) +- **Audit**: Logs the event without blocking (recommended for initial rollout) +- **Warn**: Warns the user before allowing the behavior to proceed +- **Disabled**: Rule is not active -The test passes if **every rule in the Microsoft Defender ASR Standard Protection baseline** is configured in **Block** or **Audit** mode in at least one ASR policy. The Standard Protection baseline is the published minimum recommended set for initial ASR deployment: +The test passes if **every rule in the Microsoft Defender ASR Standard Protection baseline** is configured in **Block** or **Audit** mode across the union of all ASR policies in the tenant. The Standard Protection baseline is the minimum recommended set Microsoft publishes for initial ASR deployment: 1. Block abuse of exploited vulnerable signed drivers 2. Block credential stealing from LSASS 3. Block persistence through WMI event subscription -See [Microsoft Defender ASR rules deployment guide](https://learn.microsoft.com/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment-implement) for the canonical baseline definition. +See the [Microsoft Defender ASR rules deployment guide](https://learn.microsoft.com/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment-implement) for the canonical baseline definition. -Additional ASR rules detected in tenant policies are reported for visibility but do not affect the pass/fail result. **Warn** is a supported ASR rule state but does not satisfy the baseline. Baseline rules in **Audit** mode trigger an informational note recommending a transition to **Block** mode. +Additional ASR rules detected in tenant policies are reported for visibility but do not affect the pass/fail result. **Warn** is a supported ASR rule state but does not satisfy the baseline. Baseline rules in **Audit** mode will trigger an informational note recommending a transition to **Block** mode. -## How to fix +#### Remediation action: -1. Navigate to the [Microsoft Intune admin center](https://intune.microsoft.com). +1. Navigate to [Microsoft Intune admin center](https://intune.microsoft.com). 2. Go to **Endpoint security** > **Attack surface reduction**. 3. Click **+ Create policy**. 4. Set **Platform** to **Windows 10 and later** and **Profile** to **Attack Surface Reduction Rules**. -5. Configure individual ASR rules, starting in **Audit** mode for all rules: - - Block credential stealing from Windows LSASS +5. Enter a policy name (e.g., "ASR Rules - Audit Mode"). +6. Configure individual ASR rules — start with **Audit** mode for all rules: + - Block abuse of exploited vulnerable signed drivers + - Block Adobe Reader from creating child processes - Block all Office applications from creating child processes - - Block Win32 API calls from Office macros - - Block execution of potentially obfuscated scripts + - Block credential stealing from Windows LSASS - Block executable content from email client and webmail + - Block executable files unless they meet prevalence, age, or trusted list criteria + - Block execution of potentially obfuscated scripts - Block JavaScript or VBScript from launching downloaded executable content + - Block Office applications from creating executable content + - Block Office applications from injecting code into other processes + - Block Office communication app from creating child processes + - Block persistence through WMI event subscription - Block process creations originating from PSExec and WMI commands - Block untrusted and unsigned processes that run from USB + - Block Win32 API calls from Office macros + - Use advanced protection against ransomware +7. Assign the policy to your device groups and click **Create**. +8. Monitor audit events in **Microsoft Defender for Endpoint** > **Reports** > **Attack surface reduction rules** for 2–4 weeks before transitioning rules to **Block** mode. + +#### Related links + +- [Microsoft Intune - Attack Surface Reduction](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/asr) +- [Microsoft Learn - ASR rules reference](https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-reference) +- [Microsoft Learn - Enable ASR rules in Intune](https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/enable-attack-surface-reduction) +- [Microsoft Learn - ASR rules deployment guide](https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment) + +## Test Metadata + +| Field | Value | +| --- | --- | +| Test ID | MT.1178 | +| Severity | High | +| Suite | Maester | +| Category | Intune | +| PowerShell test | [Test-MtIntuneASRRules](/docs/commands/Test-MtIntuneASRRules) | +| Tags | Intune, Maester, MT.1178 | + +## Remediation + +1. Navigate to [Microsoft Intune admin center](https://intune.microsoft.com). +2. Go to **Endpoint security** > **Attack surface reduction**. +3. Click **+ Create policy**. +4. Set **Platform** to **Windows 10 and later** and **Profile** to **Attack Surface Reduction Rules**. +5. Enter a policy name (e.g., "ASR Rules - Audit Mode"). +6. Configure individual ASR rules — start with **Audit** mode for all rules: + - Block abuse of exploited vulnerable signed drivers + - Block Adobe Reader from creating child processes + - Block all Office applications from creating child processes + - Block credential stealing from Windows LSASS + - Block executable content from email client and webmail + - Block executable files unless they meet prevalence, age, or trusted list criteria + - Block execution of potentially obfuscated scripts + - Block JavaScript or VBScript from launching downloaded executable content + - Block Office applications from creating executable content + - Block Office applications from injecting code into other processes + - Block Office communication app from creating child processes - Block persistence through WMI event subscription + - Block process creations originating from PSExec and WMI commands + - Block untrusted and unsigned processes that run from USB + - Block Win32 API calls from Office macros - Use advanced protection against ransomware -6. Assign the policy to your Windows device groups and click **Create**. -7. Monitor audit events in **Microsoft Defender for Endpoint** > **Reports** > **Attack surface reduction rules** for 2–4 weeks before transitioning rules to **Block** mode. +7. Assign the policy to your device groups and click **Create**. +8. Monitor audit events in **Microsoft Defender for Endpoint** > **Reports** > **Attack surface reduction rules** for 2–4 weeks before transitioning rules to **Block** mode. + +## Related Links + +- [Microsoft Intune - Attack Surface Reduction](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/asr) +- [Microsoft Learn - ASR rules reference](https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-reference) +- [Microsoft Learn - Enable ASR rules in Intune](https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/enable-attack-surface-reduction) +- [Microsoft Learn - ASR rules deployment guide](https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment) -## Learn more +## Source -- [Attack surface reduction rules reference](https://learn.microsoft.com/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-reference) -- [Enable ASR rules in Intune](https://learn.microsoft.com/microsoft-365/security/defender-endpoint/enable-attack-surface-reduction) -- [ASR rules deployment guide](https://learn.microsoft.com/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment) +- Pester test: `tests\Maester\Intune\Test-MtIntunePlatform.Tests.ps1` +- PowerShell source: `powershell\public\maester\intune\Test-MtIntuneASRRules.ps1` diff --git a/website/docs/tests/maester/MT.1179.md b/website/docs/tests/maester/MT.1179.md index 91f3cfc86..f9e4a6566 100644 --- a/website/docs/tests/maester/MT.1179.md +++ b/website/docs/tests/maester/MT.1179.md @@ -1,46 +1,96 @@ --- -title: MT.1179 - Ensure App Control for Business is enabled -description: Checks Intune Endpoint Security Application Control policies for App Control for Business (formerly WDAC) configurations. +title: "MT.1179 - Ensure App Control for Business is enabled" +description: "Ensure at least one Intune **App Control for Business** (formerly Windows Defender Application Control / WDAC) policy is configured. App Control for Business restricts which applications and drivers are allowed to run on Windows devices using code integrity policies. This is one of the most effecti…" slug: /tests/MT.1179 +className: generated-test-doc sidebar_class_name: hidden +hide_table_of_contents: true +keywords: + - "Maester" + - "Microsoft 365 security" + - "MT.1179" + - "High" + - "Intune" --- -# Ensure App Control for Business is enabled + -## Description +# MT.1179 - Ensure App Control for Business is enabled -Checks Intune Endpoint Security Application Control policies for **App Control for Business** (formerly Windows Defender Application Control / WDAC) configurations. +## Overview + +Ensure at least one Intune **App Control for Business** (formerly Windows Defender Application Control / WDAC) policy is configured. App Control for Business restricts which applications and drivers are allowed to run on Windows devices using code integrity policies. This is one of the most effective defenses against malware, ransomware, and unauthorized software because it blocks untrusted executables from running at all — even if they bypass antivirus detection. -Key settings evaluated: +Key settings this test evaluates: + +- **Build Options**: Whether the policy uses built-in controls (`built_in_controls_selected`) or a custom uploaded policy (`upload_policy_selected`) +- **Policy XML**: For uploaded policies, whether an XML code-integrity payload is actually present (not empty) +- **Audit Mode**: Whether the policy is in audit mode (logging only) or enforce mode (blocking) +- **Managed Installer**: Whether apps deployed via Intune/SCCM are automatically trusted +- **Intelligent Security Graph (ISG) Reputation**: Whether apps with good reputation scores are trusted + +The test passes if **at least one App Control for Business policy is enforcing** (audit mode disabled) **AND** has either built-in controls selected or an uploaded XML policy with a non-empty payload. Audit-only policies and upload-mode policies with no XML payload are reported but do not satisfy the pass criterion, because they do not block untrusted executables. + +#### Remediation action: + +1. Navigate to [Microsoft Intune admin center](https://intune.microsoft.com). +2. Go to **Endpoint security** > **Application control**. +3. Click **+ Create policy**. +4. Set **Platform** to **Windows 10 and later** and **Profile** to **App Control for Business**. +5. Enter a policy name (e.g., "App Control - Audit Mode"). +6. Configure the following settings: + - **App Control for Business**: Select **Built-in controls** + - **Audit mode**: **Enabled** (start in audit mode to identify blocked apps) + - **Trust apps from managed installer**: **Enabled** (trusts Intune-deployed apps) + - **Trust apps with good reputation**: **Disabled** (optional — ISG adds convenience but reduces strictness) +7. Assign the policy to a test device group first. +8. Monitor blocked/audited apps in **Microsoft Defender for Endpoint** > **Reports** > **Application control**. +9. After validating that legitimate apps are not being blocked, transition to **Enforce mode**. + +#### Related links + +- [Microsoft Intune - Application Control](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/applicationControl) +- [Microsoft Learn - App Control for Business in Intune](https://learn.microsoft.com/en-us/mem/intune/protect/endpoint-security-app-control-policy) +- [Microsoft Learn - Application Control for Windows](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/appcontrol) +- [Microsoft Learn - Managed Installer and ISG options](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/configure-appcontrol-managed-installer) -- **Build Options** — Whether the policy uses built-in controls (`built_in_controls_selected`) or a custom uploaded policy (`upload_policy_selected`). -- **Policy XML** — For uploaded policies, whether an XML code-integrity payload is actually present (not empty). -- **Audit Mode** — Whether the policy is in audit mode (logging only) or enforce mode (blocking). -- **Trust apps from managed installer** — Whether apps deployed via Intune/SCCM are automatically trusted. -- **Trust apps with good reputation (ISG)** — Whether apps with good Intelligent Security Graph reputation are trusted. +## Test Metadata -The test passes if **at least one App Control policy is enforcing** (audit mode disabled) **AND** has either built-in controls selected or an uploaded XML policy with a non-empty payload. Audit-only policies and upload-mode policies with no XML payload are reported but do not satisfy the pass criterion, because they do not block untrusted executables. +| Field | Value | +| --- | --- | +| Test ID | MT.1179 | +| Severity | High | +| Suite | Maester | +| Category | Intune | +| PowerShell test | [Test-MtIntuneAppControl](/docs/commands/Test-MtIntuneAppControl) | +| Tags | Intune, Maester, MT.1179 | -## How to fix +## Remediation -1. Navigate to the [Microsoft Intune admin center](https://intune.microsoft.com). +1. Navigate to [Microsoft Intune admin center](https://intune.microsoft.com). 2. Go to **Endpoint security** > **Application control**. 3. Click **+ Create policy**. 4. Set **Platform** to **Windows 10 and later** and **Profile** to **App Control for Business**. -5. Enter a policy name (for example, "App Control - Audit Mode"). -6. Configure: - - **App Control for Business**: **Built-in controls** +5. Enter a policy name (e.g., "App Control - Audit Mode"). +6. Configure the following settings: + - **App Control for Business**: Select **Built-in controls** - **Audit mode**: **Enabled** (start in audit mode to identify blocked apps) - - **Trust apps from managed installer**: **Enabled** + - **Trust apps from managed installer**: **Enabled** (trusts Intune-deployed apps) - **Trust apps with good reputation**: **Disabled** (optional — ISG adds convenience but reduces strictness) 7. Assign the policy to a test device group first. -8. Monitor blocked / audited apps in **Microsoft Defender for Endpoint** > **Reports** > **Application control**. +8. Monitor blocked/audited apps in **Microsoft Defender for Endpoint** > **Reports** > **Application control**. 9. After validating that legitimate apps are not being blocked, transition to **Enforce mode**. -## Learn more +## Related Links + +- [Microsoft Intune - Application Control](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/applicationControl) +- [Microsoft Learn - App Control for Business in Intune](https://learn.microsoft.com/en-us/mem/intune/protect/endpoint-security-app-control-policy) +- [Microsoft Learn - Application Control for Windows](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/appcontrol) +- [Microsoft Learn - Managed Installer and ISG options](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/configure-appcontrol-managed-installer) + +## Source -- [App Control for Business in Intune](https://learn.microsoft.com/mem/intune/protect/endpoint-security-app-control-policy) -- [Application Control for Windows](https://learn.microsoft.com/windows/security/application-security/application-control/app-control-for-business/appcontrol) -- [Configure Managed Installer and ISG options](https://learn.microsoft.com/windows/security/application-security/application-control/app-control-for-business/design/configure-appcontrol-managed-installer) +- Pester test: `tests\Maester\Intune\Test-MtIntunePlatform.Tests.ps1` +- PowerShell source: `powershell\public\maester\intune\Test-MtIntuneAppControl.ps1` diff --git a/website/docs/tests/maester/MT.1180.md b/website/docs/tests/maester/MT.1180.md index 7f140d517..fb7a94dcc 100644 --- a/website/docs/tests/maester/MT.1180.md +++ b/website/docs/tests/maester/MT.1180.md @@ -1,35 +1,74 @@ --- -title: MT.1180 - Ensure Managed Installer Rules are configured correctly -description: Checks Intune Endpoint Security Application Control policies for the 'Trust apps from managed installer' setting. +title: "MT.1180 - Ensure Managed Installer Rules are configured correctly" +description: "Ensure at least one Intune App Control for Business policy has **Managed Installer** enabled. When Managed Installer is enabled in an App Control for Business policy, applications deployed through Intune (or SCCM) are automatically trusted and allowed to run without needing explicit allow rules in…" slug: /tests/MT.1180 +className: generated-test-doc sidebar_class_name: hidden +hide_table_of_contents: true +keywords: + - "Maester" + - "Microsoft 365 security" + - "MT.1180" + - "Medium" + - "Intune" --- -# Ensure Managed Installer Rules are configured correctly + -## Description +# MT.1180 - Ensure Managed Installer Rules are configured correctly -Checks Intune Endpoint Security Application Control policies for the **Trust apps from managed installer** setting. +## Overview + +Ensure at least one Intune App Control for Business policy has **Managed Installer** enabled. When Managed Installer is enabled in an App Control for Business policy, applications deployed through Intune (or SCCM) are automatically trusted and allowed to run without needing explicit allow rules in the code integrity policy. This dramatically simplifies App Control deployment in enterprise environments. **Without Managed Installer:** - -- Every application must have an explicit allow rule in the App Control policy. -- Line-of-business (LOB) apps deployed via Intune may be blocked unexpectedly. -- Help desk tickets increase due to false positives from legitimate software being blocked. +- Every application must have an explicit allow rule in the App Control policy +- Line-of-business (LOB) apps deployed via Intune may be blocked unexpectedly +- Help desk tickets increase due to false positives from legitimate software being blocked +- IT teams must maintain complex allow lists that change with every app update **With Managed Installer:** +- Apps deployed through Intune are automatically whitelisted at install time +- Only user-installed, sideloaded, or internet-downloaded apps are subject to policy restrictions +- Reduces false positives while maintaining security against unauthorized software +- Simplifies ongoing policy maintenance + +The test passes if **at least one App Control for Business policy is in Enforce mode** (audit mode disabled) **AND** has **Trust apps from managed installer** enabled **AND** has an active control (built-in controls selected OR a non-empty uploaded XML payload). Managed Installer enabled on an audit-only App Control, or on an enforce-mode upload policy with an empty XML payload, does not actively trust deployed apps because the underlying App Control policy is not blocking anything. This mirrors the active-control gate used by MT.1179. + +#### Remediation action: + +1. Navigate to [Microsoft Intune admin center](https://intune.microsoft.com). +2. Go to **Endpoint security** > **Application control**. +3. Edit an existing App Control for Business policy (or create a new one). +4. Under **App Control for Business**, select **Built-in controls**. +5. Set **Trust apps from managed installer** to **Enabled**. +6. Save and assign the policy to your device groups. + +> **Note:** Managed Installer works by tagging files written by the Intune Management Extension (IME) process. The App Control policy then trusts any file that was installed by a tagged managed installer process. This is transparent to end users. -- Apps deployed through Intune are automatically allow-listed at install time. -- Only user-installed, sideloaded, or internet-downloaded apps are subject to policy restrictions. -- Reduces false positives while maintaining security against unauthorized software. +#### Related links -The test passes if **at least one App Control for Business policy is in Enforce mode** (audit mode disabled) **AND** has **Trust apps from managed installer** enabled. Managed Installer enabled on an audit-only policy does not actively trust deployed apps because the underlying App Control policy is not blocking untrusted executables. +- [Microsoft Intune - Application Control](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/applicationControl) +- [Microsoft Learn - Configure Managed Installer in Intune](https://learn.microsoft.com/en-us/mem/intune/protect/endpoint-security-app-control-policy) +- [Microsoft Learn - Automatically allow apps deployed by a managed installer](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/configure-appcontrol-managed-installer) +- [Microsoft Learn - App Control for Business overview](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/appcontrol) -## How to fix +## Test Metadata -1. Navigate to the [Microsoft Intune admin center](https://intune.microsoft.com). +| Field | Value | +| --- | --- | +| Test ID | MT.1180 | +| Severity | Medium | +| Suite | Maester | +| Category | Intune | +| PowerShell test | [Test-MtIntuneManagedInstallerRules](/docs/commands/Test-MtIntuneManagedInstallerRules) | +| Tags | Intune, Maester, MT.1180 | + +## Remediation + +1. Navigate to [Microsoft Intune admin center](https://intune.microsoft.com). 2. Go to **Endpoint security** > **Application control**. 3. Edit an existing App Control for Business policy (or create a new one). 4. Under **App Control for Business**, select **Built-in controls**. @@ -38,8 +77,14 @@ The test passes if **at least one App Control for Business policy is in Enforce > **Note:** Managed Installer works by tagging files written by the Intune Management Extension (IME) process. The App Control policy then trusts any file that was installed by a tagged managed installer process. This is transparent to end users. -## Learn more +## Related Links + +- [Microsoft Intune - Application Control](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/applicationControl) +- [Microsoft Learn - Configure Managed Installer in Intune](https://learn.microsoft.com/en-us/mem/intune/protect/endpoint-security-app-control-policy) +- [Microsoft Learn - Automatically allow apps deployed by a managed installer](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/configure-appcontrol-managed-installer) +- [Microsoft Learn - App Control for Business overview](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/appcontrol) + +## Source -- [Configure Managed Installer in Intune](https://learn.microsoft.com/mem/intune/protect/endpoint-security-app-control-policy) -- [Automatically allow apps deployed by a managed installer](https://learn.microsoft.com/windows/security/application-security/application-control/app-control-for-business/design/configure-appcontrol-managed-installer) -- [App Control for Business overview](https://learn.microsoft.com/windows/security/application-security/application-control/app-control-for-business/appcontrol) +- Pester test: `tests\Maester\Intune\Test-MtIntunePlatform.Tests.ps1` +- PowerShell source: `powershell\public\maester\intune\Test-MtIntuneManagedInstallerRules.ps1`