SHELL/5.3.3.ps1
|
$CheckId = "5.3.3" $Title = "Ensure 'Access reviews' for privileged roles are configured" $Level = "L1" $BenchmarkType = "Automated" $Uri = "https://graph.microsoft.com/v1.0/identityGovernance/accessReviews/definitions" function Get-PropValue { param( [AllowNull()]$Object, [string]$Name ) if ($null -eq $Object) { return $null } if ($Object -is [hashtable]) { foreach ($Key in $Object.Keys) { if ([string]$Key -ieq $Name) { return $Object[$Key] } } } if ($Object.PSObject -and $Object.PSObject.Properties) { foreach ($Property in $Object.PSObject.Properties) { if ([string]$Property.Name -ieq $Name) { return $Property.Value } } } return $null } function Get-PrivilegedRoleScopeQueries { param([AllowNull()]$Review) $Queries = [System.Collections.Generic.List[string]]::new() $Scope = Get-PropValue -Object $Review -Name "scope" if ($Scope) { $DirectScopeQuery = Get-PropValue -Object $Scope -Name "query" if ($DirectScopeQuery) { $Queries.Add([string]$DirectScopeQuery) } $PrincipalScopes = @(Get-PropValue -Object $Scope -Name "principalScopes") foreach ($PrincipalScope in $PrincipalScopes) { $PrincipalQuery = Get-PropValue -Object $PrincipalScope -Name "query" if ($PrincipalQuery) { $Queries.Add([string]$PrincipalQuery) } } } return @($Queries) } try { $Response = Invoke-MgGraphRequest -Uri $Uri -Method GET -ErrorAction Stop $Definitions = @(Get-PropValue -Object $Response -Name "value") if ($Definitions.Count -eq 0) { [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = "FAIL" Pass = $false Evidence = [pscustomobject]@{ ReviewCount = 0 PrivilegedRoleReviewCount = 0 CompliantPrivilegedRoleReviewCount = 0 AccessReviewReport = @() SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = "No access review definitions were returned." Timestamp = Get-Date } return } $PrivilegedRoleReviews = @() foreach ($Review in $Definitions) { $Queries = Get-PrivilegedRoleScopeQueries -Review $Review if (($Queries -join " || ") -match "(?i)roleAssignmentScheduleInstances|roleDefinitions|directoryRoles") { $PrivilegedRoleReviews += $Review } } $AccessReviewReport = foreach ($Review in $PrivilegedRoleReviews) { $Settings = Get-PropValue -Object $Review -Name "settings" $Recurrence = Get-PropValue -Object $Settings -Name "recurrence" $Pattern = Get-PropValue -Object $Recurrence -Name "pattern" $RecurrenceType = [string](Get-PropValue -Object $Pattern -Name "type") $Status = [string](Get-PropValue -Object $Review -Name "status") $Reviewers = @(Get-PropValue -Object $Review -Name "reviewers") $Mail = [bool](Get-PropValue -Object $Settings -Name "mailNotificationsEnabled") $Reminder = [bool](Get-PropValue -Object $Settings -Name "reminderNotificationsEnabled") $Justification = [bool](Get-PropValue -Object $Settings -Name "justificationRequiredOnApproval") $AutoApply = [bool](Get-PropValue -Object $Settings -Name "autoApplyDecisionsEnabled") $DefaultDecision = [string](Get-PropValue -Object $Settings -Name "defaultDecision") $DurationInDaysRaw = Get-PropValue -Object $Settings -Name "instanceDurationInDays" $DurationInDays = 0 [void][int]::TryParse([string]$DurationInDaysRaw, [ref]$DurationInDays) $RecurrencePass = $RecurrenceType -in @("weekly", "absoluteMonthly", "relativeMonthly") $DurationPass = ($DurationInDays -gt 0 -and $DurationInDays -le 14) $NoResponseNoChange = ($DefaultDecision -match '^(?i:none)$') $ReviewersPass = (@($Reviewers).Count -gt 0) $IsCISCompliant = ($Status -eq "InProgress") -and $ReviewersPass -and $Mail -and $Reminder -and $Justification -and $RecurrencePass -and $DurationPass -and $AutoApply -and $NoResponseNoChange [pscustomobject]@{ Name = [string](Get-PropValue -Object $Review -Name "displayName") Status = $Status ReviewersCount = @($Reviewers).Count mailNotificationsEnabled = $Mail reminderNotificationsEnabled = $Reminder justificationRequiredOnApproval = $Justification Frequency = $RecurrenceType instanceDurationInDays = $DurationInDays autoApplyDecisionsEnabled = $AutoApply defaultDecision = $DefaultDecision IsCISCompliant = $IsCISCompliant } } $CompliantCount = @($AccessReviewReport | Where-Object { $_.IsCISCompliant }).Count $Pass = $CompliantCount -gt 0 $Status = if ($Pass) { "PASS" } else { "FAIL" } if ($PrivilegedRoleReviews.Count -eq 0) { $Pass = $false $Status = "FAIL" } [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = $Status Pass = $Pass Evidence = [pscustomobject]@{ ReviewCount = $Definitions.Count PrivilegedRoleReviewCount = $PrivilegedRoleReviews.Count CompliantPrivilegedRoleReviewCount = $CompliantCount AccessReviewReport = @($AccessReviewReport) SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = if ($Pass) { $null } else { "No privileged-role access review matched all CIS-required settings, or no privileged-role access review was found." } Timestamp = Get-Date } } catch { [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = "ERROR" Pass = $null Evidence = [pscustomobject]@{ RequestUri = $Uri SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = $_.Exception.Message Timestamp = Get-Date } } |