tests/Test-Assessment.41054.ps1
|
<#
.SYNOPSIS Controlled folder access is enabled in block mode. .DESCRIPTION Evaluates the pinned controlled folder access Secure Score control by joining its control profile to the latest Microsoft Secure Score snapshot and comparing the achieved score with its maximum score. A missing profile, no applicable devices, or an ignored control produces an Investigate result. .NOTES Test ID: 41054 Workshop Task ID: SECOPS-054 Category: Endpoint threat protection Pillar: SecOps Required Module: Microsoft.Graph.Authentication Required Connection: Microsoft Graph #> function Test-Assessment-41054 { [ZtTest( Category = 'Endpoint threat protection', CompatibleLicense = ('WINDEFATP', 'MDE_LITE'), ImplementationCost = 'Low', Pillar = 'SecOps', RiskLevel = 'High', Service = ('Graph'), SfiPillar = 'Monitor and detect cyberthreats', TenantType = ('Workforce'), TestId = 41054, Title = 'Controlled folder access is enabled in block mode', UserImpact = 'Medium' )] [CmdletBinding()] param() #region Data Collection Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose $activity = 'Checking controlled folder access in Microsoft Secure Score' $controlId = 'scid_2021' $controlProfileError = $null $secureScoreError = $null # Q1: Read the pinned MDATP Secure Score control profile. Write-ZtProgress -Activity $activity -Status 'Getting the controlled folder access Secure Score control profile' $controlProfiles = @() try { $controlProfileFilter = "service eq 'MDATP' and id eq '$controlId'" $controlProfiles = @(Invoke-ZtGraphRequest -RelativeUri 'security/secureScoreControlProfiles' -Filter $controlProfileFilter -ApiVersion beta -ErrorAction Stop) } catch { $controlProfileError = $_ Write-PSFMessage "Failed to retrieve the controlled folder access Secure Score control profile: $_" -Tag Test -Level Warning } # Q2: Read the latest Secure Score snapshot; -DisablePaging returns the wrapper object. Write-ZtProgress -Activity $activity -Status 'Getting the latest Secure Score snapshot' $latestSecureScore = $null try { $secureScoresResponse = Invoke-ZtGraphRequest -RelativeUri 'security/secureScores' -Top 1 -ApiVersion beta -DisablePaging -ErrorAction Stop $secureScores = @($secureScoresResponse.value) if ($secureScores.Count -gt 0) { $latestSecureScore = $secureScores[0] } } catch { $secureScoreError = $_ Write-PSFMessage "Failed to retrieve the latest Secure Score snapshot: $_" -Tag Test -Level Warning } #endregion Data Collection #region Assessment Logic $passed = $false $customStatus = $null $queryErrors = @($controlProfileError, $secureScoreError | Where-Object { $null -ne $_ }) if ($queryErrors.Count -gt 0) { $queryError = $queryErrors[0] $httpStatus = Get-ZtHttpStatusCode -ErrorRecord $queryError $errorMessage = switch ($httpStatus) { { $_ -in (401, 403) } { 'Microsoft Graph permission issue. Grant SecurityEvents.Read.All; for delegated runs assign Security Reader or Security Administrator.'; break } 404 { 'The Microsoft Graph endpoint or required resource is unavailable or was not found.'; break } 429 { 'Microsoft Graph throttled the request. Retry after the Retry-After interval.'; break } { $_ -ge 500 -and $_ -le 599 } { "Microsoft Graph returned a transient service error (HTTP $_). Please try again."; break } default { 'Microsoft Graph returned an error and the controlled folder access Secure Score control could not be evaluated.' } } $params = @{ TestId = '41054' Title = 'Controlled folder access is enabled in block mode' Status = $false Result = "⚠️ $errorMessage" CustomStatus = 'Investigate' } Add-ZtTestResultDetail @params return } if ($null -eq $latestSecureScore) { $params = @{ TestId = '41054' Title = 'Controlled folder access is enabled in block mode' Status = $false Result = '⚠️ The controlled folder access Secure Score control could not be evaluated because no latest Secure Score snapshot was returned; verify SecurityEvents.Read.All is granted, Secure Score data is flowing, and at least one MDE device is onboarded.' CustomStatus = 'Investigate' } Add-ZtTestResultDetail @params return } $controlProfile = $controlProfiles | Select-Object -First 1 $matchingScore = @($latestSecureScore.controlScores | Where-Object controlName -eq $controlId | Select-Object -First 1) $matchingScore = if ($matchingScore.Count -gt 0) { $matchingScore[0] } else { $null } $latestStateUpdate = @() if ($null -ne $controlProfile) { $latestStateUpdate = @($controlProfile.controlStateUpdates | Sort-Object { if ($_.updatedDateTime) { [datetime]$_.updatedDateTime } else { [datetime]::MinValue } } -Descending | Select-Object -First 1) } $controlState = if ($latestStateUpdate.Count -gt 0 -and -not [string]::IsNullOrWhiteSpace($latestStateUpdate[0].state)) { $latestStateUpdate[0].state } else { 'N/A' } $isIgnored = $latestStateUpdate.Count -gt 0 -and $latestStateUpdate[0].state -eq 'ignored' $score = if ($null -ne $matchingScore -and $null -ne $matchingScore.score) { $matchingScore.score } else { $null } $maxScore = if ($null -ne $controlProfile -and $null -ne $controlProfile.maxScore) { $controlProfile.maxScore } else { $null } $scoreValue = $null $scoreIsNumeric = $false if ($null -ne $score) { try { $scoreValue = [double]$score $scoreIsNumeric = $true } catch { } } $maxScoreValue = $null $maxScoreIsNumeric = $false if ($null -ne $maxScore) { try { $maxScoreValue = [double]$maxScore $maxScoreIsNumeric = $true } catch { } } $statusReason = $null $status = if ($null -eq $controlProfile) { $statusReason = 'profile not found' 'N/A' } elseif ($null -eq $matchingScore) { $statusReason = 'no applicable devices' 'N/A' } elseif ($isIgnored) { 'Skipped' } elseif (-not $scoreIsNumeric -or -not $maxScoreIsNumeric) { 'Investigate' } elseif ($scoreValue -ge $maxScoreValue) { 'Pass' } else { 'Fail' } $controlTitle = if ($null -ne $controlProfile -and -not [string]::IsNullOrWhiteSpace($controlProfile.title)) { $controlProfile.title } else { $controlId } $evaluationResult = [PSCustomObject]@{ ControlId = $controlId ControlTitle = $controlTitle ActionUrl = if ($null -ne $controlProfile) { $controlProfile.actionUrl } else { $null } Score = if ($null -ne $score) { $score } else { 'N/A' } MaxScore = if ($null -ne $maxScore) { $maxScore } else { 'N/A' } ImplementationStatus = if ($null -ne $matchingScore -and -not [string]::IsNullOrWhiteSpace($matchingScore.implementationStatus)) { $matchingScore.implementationStatus } else { 'N/A' } State = $controlState LastModifiedDateTime = if ($null -ne $controlProfile) { $controlProfile.lastModifiedDateTime } else { $null } Status = $status StatusReason = $statusReason } if ($status -eq 'Fail') { $testResultMarkdown = "❌ Controlled folder access is below its Secure Score target on one or more applicable devices (for example, disabled or in audit-only mode).`n`n%TestResult%" } elseif ($status -eq 'Pass') { $passed = $true $testResultMarkdown = "✅ Controlled folder access is enabled in block mode.`n`n%TestResult%" } else { $customStatus = 'Investigate' $testResultMarkdown = "⚠️ The controlled folder access Secure Score control could not be evaluated (no pinned profile, no applicable devices, an ignored control, no latest snapshot, or a Microsoft Graph error); verify SecurityEvents.Read.All is granted, Secure Score data is flowing, and at least one MDE device is onboarded.`n`n%TestResult%" } #endregion Assessment Logic #region Report Generation $statusDisplay = switch ($evaluationResult.Status) { 'Pass' { '✅ Pass' } 'Fail' { '❌ Fail' } 'Investigate' { '⚠️ Investigate' } 'N/A' { "N/A ($($evaluationResult.StatusReason))" } default { 'Skipped' } } $lastModified = if ($evaluationResult.LastModifiedDateTime) { Get-FormattedDate -DateString $evaluationResult.LastModifiedDateTime } else { 'N/A' } $safeControlTitle = Get-SafeMarkdown -Text $evaluationResult.ControlTitle $controlDisplay = if (-not [string]::IsNullOrWhiteSpace($evaluationResult.ActionUrl)) { "[$safeControlTitle]($($evaluationResult.ActionUrl)) ($($evaluationResult.ControlId))" } elseif ($evaluationResult.ControlTitle -ne $evaluationResult.ControlId) { "$safeControlTitle ($($evaluationResult.ControlId))" } else { $safeControlTitle } $countLine = 'Total pinned controlled folder access controls: 1' $secureScoreLink = '[Microsoft Secure Score](https://security.microsoft.com/securescore)' $controlTable = @" | Control title (id) | Score | Max score | Implementation status | State | Last modified | Status | | :----------------- | ----: | --------: | :-------------------- | :---- | :------------ | :----- | | $controlDisplay | $($evaluationResult.Score) | $($evaluationResult.MaxScore) | $($evaluationResult.ImplementationStatus) | $($evaluationResult.State) | $lastModified | $statusDisplay | "@ $formatTemplate = @' {0} {1} {2} '@ $mdInfo = $formatTemplate -f $countLine, $secureScoreLink, $controlTable $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo #endregion Report Generation $params = @{ TestId = '41054' Title = 'Controlled folder access is enabled in block mode' Status = $passed Result = $testResultMarkdown } if ($customStatus) { $params.CustomStatus = $customStatus } Add-ZtTestResultDetail @params } |