diff --git a/powershell/Maester.psd1 b/powershell/Maester.psd1 index a8142ff5c..dab1273e9 100644 --- a/powershell/Maester.psd1 +++ b/powershell/Maester.psd1 @@ -144,7 +144,8 @@ 'Test-MtEntraIDConnectSyncSoftHardMatching', 'Test-MtKrbtgtAzureADNotSynced', 'Test-MtExoDelicensingResiliency', 'Test-MtExoMailTip', 'Test-MtExoModernAuth', 'Test-MtExoMoeraMailActivity', 'Test-MtExoOutlookAddin', 'Test-MtExoRejectDirectSend', 'Test-MtExoSetScl', 'Test-MtFeatureUpdatePolicy', 'Test-MtGroupCreationRestricted', - 'Test-MtHighRiskAppPermissions', 'Test-MtIntuneDiagnosticSettings', 'Test-MtIntuneRbacGroupsProtected', + 'Test-MtHighRiskAppPermissions', 'Test-MtIntuneAppControl', 'Test-MtIntuneASRRules', 'Test-MtIntuneDiagnosticSettings', + 'Test-MtIntuneLAPSConfiguration', 'Test-MtIntuneManagedInstallerRules', 'Test-MtIntuneRbacGroupsProtected', 'Test-MtLimitOnMicrosoftDomainUsage', 'Test-MtManagedDeviceCleanupSettings', 'Test-MtManagementGroupWriteRequirement', 'Test-MtMdmAuthority', 'Test-MtMobileThreatDefenseConnectors', 'Test-MtMdeArchiveScanning', 'Test-MtMdeBehaviorMonitoring', 'Test-MtMdeCatchupFullScan', 'Test-MtMdeCatchupQuickScan', diff --git a/powershell/public/maester/intune/Test-MtIntuneASRRules.md b/powershell/public/maester/intune/Test-MtIntuneASRRules.md new file mode 100644 index 000000000..43742db9f --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.md @@ -0,0 +1,65 @@ +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 + +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: + +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..4725ec4a2 --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneASRRules.ps1 @@ -0,0 +1,228 @@ +function Test-MtIntuneASRRules { + <# + .SYNOPSIS + 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 + 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 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 + + 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 in Block or Audit mode across the union of all ASR policies in the tenant. + + .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 (!(Test-MtConnection Graph)) { + Add-MtTestResultDetail -SkippedBecause NotConnectedGraph + return $null + } + + 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' + } + + # 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() + # 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))" + $settingsUri = "deviceManagement/configurationPolicies('$($policy.id)')/settings?`$expand=settingDefinitions&`$top=1000" + $settingsResponse = @(Invoke-MtGraphRequest -RelativeUri $settingsUri -ApiVersion beta) + + $blockCount = 0 + $auditCount = 0 + $warnCount = 0 + $disabledCount = 0 + $notConfiguredCount = 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++ } + 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; 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 + } + } + } + } + } + + $policyResults.Add(@{ + Name = $policy.name + BlockCount = $blockCount + AuditCount = $auditCount + WarnCount = $warnCount + DisabledCount = $disabledCount + NotConfiguredCount = $notConfiguredCount + TotalRules = $ruleDetails.Count + Rules = $ruleDetails + }) + } + + # 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 | Baseline |`n| --- | --- | --- |`n" + foreach ($r in $p.Rules) { + $baselineMark = if ($r.IsBaseline) { 'Yes' } else { '' } + $testResultMarkdown += "| $($r.Name) | $($r.Mode) | $baselineMark |`n" + } + $testResultMarkdown += "`n" + } + + 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 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) 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 { + $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 + } + } 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..b0330346d --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneAppControl.md @@ -0,0 +1,39 @@ +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 (`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) + + +%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..dde99f775 --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneAppControl.ps1 @@ -0,0 +1,193 @@ +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 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 + + 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 in enforce mode. + + .LINK + https://maester.dev/docs/commands/Test-MtIntuneAppControl + #> + [CmdletBinding()] + [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 + } + + 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' + # 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() + + 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' + PolicyXml = 'N/A' + AuditMode = 'Not configured' + ManagedInstaller = 'Not configured' + GoodReputation = 'Not configured' + Enforcing = $false + HasActiveControl = $false + } + + foreach ($setting in $settingsResponse) { + $defId = $setting.settingInstance.settingDefinitionId + + 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)" + + # Child settings are nested under the build options choice + foreach ($child in $setting.settingInstance.choiceSettingValue.children) { + 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)" + } + } + } + } + } + + $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 += "**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.PolicyXml) | $($p.AuditMode) | $($p.ManagedInstaller) | $($p.GoodReputation) |`n" + } + + 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." + } + + $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 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 + } + } 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..f762f2fad --- /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**: 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** 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: + +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..824ee8943 --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneLAPSConfiguration.ps1 @@ -0,0 +1,216 @@ +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. + + 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). + + 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 meets the secure baseline (Entra ID backup, complexity >= 4, length >= 14, post-auth action configured). + + .LINK + https://maester.dev/docs/commands/Test-MtIntuneLAPSConfiguration + #> + [CmdletBinding()] + [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 + } + + 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)' + } + + # 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() + $hasCompliantPolicy = $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' + Compliant = 'No' + } + + $hasEntra = $false + $hasComplexity = $false + $hasLength = $false + $hasPostAuth = $false + + 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') { $hasEntra = $true } + break + } + } + 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] + } + } + foreach ($suffix in $minComplexitySuffixes) { + if ($val -and $val.EndsWith($suffix)) { $hasComplexity = $true; break } + } + Write-Verbose " PasswordComplexity: $($policyDetail.PasswordComplexity)" + } + + if ($defId -eq $passwordLengthId) { + $lengthVal = [int]($setting.settingInstance.simpleSettingValue.value) + $policyDetail.PasswordLength = "$lengthVal characters" + if ($lengthVal -ge $minPasswordLength) { $hasLength = $true } + 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' } + foreach ($suffix in $validPostAuthSuffixes) { + if ($val -and $val.EndsWith($suffix)) { $hasPostAuth = $true; break } + } + 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) { + # 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)" + } + } + + 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 += "**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) | $($p.Compliant) |`n" + } + + 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 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 + } + } 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..3ec1a72b0 --- /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 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. + +#### 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) + + +%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..43cdee7a6 --- /dev/null +++ b/powershell/public/maester/intune/Test-MtIntuneManagedInstallerRules.ps1 @@ -0,0 +1,193 @@ +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 is in **enforce mode** (audit mode disabled) AND has + 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 and an active control. + + .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 (!(Test-MtConnection Graph)) { + Add-MtTestResultDetail -SkippedBecause NotConnectedGraph + return $null + } + + 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' + $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() + + 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' + 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 $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) { + 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)" + } + $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)" + } + } + } + } + } + + $policyResults.Add($policyDetail) + } + + # 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 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.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 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 }) + $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" + } + 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 + } + } 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-MtIntunePlatform.Tests.ps1 b/tests/Maester/Intune/Test-MtIntunePlatform.Tests.ps1 index 0fa9fb966..79a71995e 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.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.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.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.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 e055960b7..bcb66e066 100644 --- a/tests/maester-config.json +++ b/tests/maester-config.json @@ -1474,6 +1474,26 @@ "Severity": "High", "Title": "Sample Submission should send safe samples automatically" }, + { + "Id": "MT.1177", + "Severity": "High", + "Title": "Ensure LAPS Configuration Policy is properly set" + }, + { + "Id": "MT.1178", + "Severity": "High", + "Title": "Ensure ASR Rules are configured correctly" + }, + { + "Id": "MT.1179", + "Severity": "High", + "Title": "Ensure App Control for Business is enabled" + }, + { + "Id": "MT.1180", + "Severity": "Medium", + "Title": "Ensure Managed Installer Rules are configured correctly" + }, { "Id": "ORCA.100", "Severity": "Medium", diff --git a/website/docs/tests/maester/MT.1177.md b/website/docs/tests/maester/MT.1177.md new file mode 100644 index 000000000..1152a15ee --- /dev/null +++ b/website/docs/tests/maester/MT.1177.md @@ -0,0 +1,96 @@ +--- +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" +--- + + + +# MT.1177 - Ensure LAPS Configuration Policy is properly set + +## Overview + +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**: 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** 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: + +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) + +## 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) + +## Source + +- 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 new file mode 100644 index 000000000..3b936d46d --- /dev/null +++ b/website/docs/tests/maester/MT.1178.md @@ -0,0 +1,133 @@ +--- +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" +--- + + + +# MT.1178 - Ensure ASR Rules are configured correctly + +## Overview + +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 + +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: + +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) + +## 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 +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) + +## Source + +- 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 new file mode 100644 index 000000000..f9e4a6566 --- /dev/null +++ b/website/docs/tests/maester/MT.1179.md @@ -0,0 +1,96 @@ +--- +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" +--- + + + +# MT.1179 - Ensure App Control for Business is enabled + +## 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 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) + +## Test Metadata + +| 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 | + +## Remediation + +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) + +## Source + +- 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 new file mode 100644 index 000000000..fb7a94dcc --- /dev/null +++ b/website/docs/tests/maester/MT.1180.md @@ -0,0 +1,90 @@ +--- +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" +--- + + + +# MT.1180 - Ensure Managed Installer Rules are configured correctly + +## 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 +- 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. + +#### 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) + +## Test Metadata + +| 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**. +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/~/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 + +- Pester test: `tests\Maester\Intune\Test-MtIntunePlatform.Tests.ps1` +- PowerShell source: `powershell\public\maester\intune\Test-MtIntuneManagedInstallerRules.ps1`