Public/Get-NLBaselineCACISCompliance.ps1
|
function Get-NLBaselineCACISCompliance { <# .SYNOPSIS Validate Conditional Access policies against CIS Microsoft 365 Foundations Benchmark .DESCRIPTION Analyzes all Conditional Access policies and validates compliance with CIS Microsoft 365 Foundations Benchmark controls. Provides detailed reporting on which CIS controls are met and which are missing. .EXAMPLE Get-NLBaselineCACISCompliance #> [CmdletBinding()] param() try { # Check connection $context = Get-MgContext -ErrorAction SilentlyContinue if (-not $context -or -not $context.TenantId) { Write-Host "Not connected to Microsoft 365. Connecting..." -ForegroundColor Yellow Write-Host "" $connection = Connect-NLBaselineCA if (-not $connection) { Write-Error "Cannot connect to Microsoft 365" return } $context = Get-MgContext } Write-Host "========================================" -ForegroundColor Cyan Write-Host " CIS COMPLIANCE VALIDATION" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" Write-Host "CIS Microsoft 365 Foundations Benchmark v6.0.0" -ForegroundColor Yellow Write-Host "" # Get all policies using helper function with REST API fallback Write-Host "Retrieving Conditional Access policies..." -ForegroundColor Gray $policies = Get-AllConditionalAccessPolicies if (-not $policies -or $policies.Count -eq 0) { Write-Error "Failed to retrieve Conditional Access policies or no policies found" return } Write-Host "Found $($policies.Count) policies to analyze" -ForegroundColor Green Write-Host "" # CIS Benchmark Controls for Conditional Access $cisControls = @{ "1.1.1" = @{ Description = "Ensure MFA is enabled for all users" Required = $true Policies = @() Met = $false } "1.1.2" = @{ Description = "Ensure users can register security methods securely" Required = $true Policies = @() Met = $false } "1.1.3" = @{ Description = "Ensure MFA is required for administrative roles" Required = $true Policies = @() Met = $false } "1.1.4" = @{ Description = "Ensure legacy authentication is blocked" Required = $true Policies = @() Met = $false } "1.1.5" = @{ Description = "Enable Identity Protection policies" Required = $false Policies = @() Met = $false } "1.2.1" = @{ Description = "Restrict access by location/IP" Required = $false Policies = @() Met = $false } "1.2.2" = @{ Description = "Admin session controls" Required = $true Policies = @() Met = $false } "2.1.1" = @{ Description = "Modern authentication required" Required = $true Policies = @() Met = $false } "2.1.2" = @{ Description = "Device management required" Required = $true Policies = @() Met = $false } "2.1.3" = @{ Description = "Device compliance policies" Required = $true Policies = @() Met = $false } "3.1.1" = @{ Description = "Guest MFA required" Required = $true Policies = @() Met = $false } "3.1.2" = @{ Description = "Guest access restrictions" Required = $true Policies = @() Met = $false } "3.1.3" = @{ Description = "Guest session controls" Required = $true Policies = @() Met = $false } "6.1.1" = @{ Description = "Geographic restrictions" Required = $false Policies = @() Met = $false } } # Analyze each policy Write-Host "Analyzing policies against CIS controls..." -ForegroundColor Gray Write-Host "" foreach ($policy in $policies) { $policyName = $policy.DisplayName $state = $policy.State # Skip disabled policies if ($state -eq "disabled") { continue } # Check 1.1.1 - MFA for all users if ($policy.GrantControls.BuiltInControls -contains "mfa" -or $policy.GrantControls.AuthenticationStrength -ne $null) { if ($policy.Conditions.Users.IncludeUsers -contains "All" -or $policy.Conditions.Users.IncludeUsers.Count -gt 100) { $cisControls["1.1.1"].Policies += $policyName $cisControls["1.1.1"].Met = $true } } # Check 1.1.2 - Secure registration if ($policy.Conditions.Applications.IncludeUserActions -contains "urn:user:registersecurityinfo" -or $policy.Conditions.Applications.IncludeUserActions -contains "microsoft.graph.security.securityInfoRegistration") { if ($policy.GrantControls.BuiltInControls -contains "mfa" -or $policy.Conditions.Locations.IncludeLocations -contains "AllTrustedLocations") { $cisControls["1.1.2"].Policies += $policyName $cisControls["1.1.2"].Met = $true } } # Check 1.1.3 - MFA for admin roles if ($policy.GrantControls.BuiltInControls -contains "mfa" -or $policy.GrantControls.AuthenticationStrength -ne $null) { if ($policy.Conditions.Users.IncludeRoles.Count -gt 0) { $cisControls["1.1.3"].Policies += $policyName $cisControls["1.1.3"].Met = $true } } # Check 1.1.4 - Block legacy auth if ($policy.Conditions.ClientAppTypes -contains "exchangeActiveSync" -or $policy.Conditions.ClientAppTypes -contains "other") { if ($policy.GrantControls.BuiltInControls -contains "block") { $cisControls["1.1.4"].Policies += $policyName $cisControls["1.1.4"].Met = $true } } # Check 1.1.5 - Identity Protection if ($policy.Conditions.SignInRiskLevels.Count -gt 0 -or $policy.Conditions.UserRiskLevels.Count -gt 0) { $cisControls["1.1.5"].Policies += $policyName $cisControls["1.1.5"].Met = $true } # Check 1.2.1 - Location restrictions if ($policy.Conditions.Locations.ExcludeLocations.Count -gt 0 -or $policy.Conditions.Locations.IncludeLocations.Count -gt 0) { $cisControls["1.2.1"].Policies += $policyName $cisControls["1.2.1"].Met = $true } # Check 1.2.2 - Admin session controls if ($policy.Conditions.Users.IncludeRoles.Count -gt 0) { if ($policy.SessionControls.SignInFrequency -ne $null -or $policy.SessionControls.PersistentBrowser -ne $null -or $policy.SessionControls.CloudAppSecurity -ne $null) { $cisControls["1.2.2"].Policies += $policyName $cisControls["1.2.2"].Met = $true } } # Check 2.1.1 - Modern auth required if ($policy.Conditions.ClientAppTypes -notcontains "exchangeActiveSync" -and $policy.Conditions.ClientAppTypes -notcontains "other") { if ($policy.GrantControls.BuiltInControls -contains "mfa" -or $policy.GrantControls.BuiltInControls -contains "compliantDevice") { $cisControls["2.1.1"].Policies += $policyName $cisControls["2.1.1"].Met = $true } } # Check 2.1.2 - Device management if ($policy.GrantControls.BuiltInControls -contains "compliantDevice" -or $policy.GrantControls.BuiltInControls -contains "domainJoinedDevice" -or $policy.GrantControls.BuiltInControls -contains "approvedApplication") { $cisControls["2.1.2"].Policies += $policyName $cisControls["2.1.2"].Met = $true } # Check 2.1.3 - Device compliance if ($policy.GrantControls.BuiltInControls -contains "compliantDevice" -or $policy.SessionControls.CloudAppSecurity -ne $null) { $cisControls["2.1.3"].Policies += $policyName $cisControls["2.1.3"].Met = $true } # Check 3.1.1 - Guest MFA if ($policy.Conditions.Users.IncludeGuestsOrExternalUsers -ne $null) { if ($policy.GrantControls.BuiltInControls -contains "mfa" -or $policy.GrantControls.AuthenticationStrength -ne $null) { $cisControls["3.1.1"].Policies += $policyName $cisControls["3.1.1"].Met = $true } } # Check 3.1.2 - Guest restrictions if ($policy.Conditions.Users.IncludeGuestsOrExternalUsers -ne $null) { if ($policy.Conditions.Applications.IncludeApplications.Count -lt 10 -or $policy.GrantControls.BuiltInControls -contains "block") { $cisControls["3.1.2"].Policies += $policyName $cisControls["3.1.2"].Met = $true } } # Check 3.1.3 - Guest session controls if ($policy.Conditions.Users.IncludeGuestsOrExternalUsers -ne $null) { if ($policy.SessionControls.SignInFrequency -ne $null -or $policy.SessionControls.PersistentBrowser -ne $null) { $cisControls["3.1.3"].Policies += $policyName $cisControls["3.1.3"].Met = $true } } # Check 6.1.1 - Geographic restrictions if ($policy.Conditions.Locations.ExcludeLocations.Count -gt 0) { $cisControls["6.1.1"].Policies += $policyName $cisControls["6.1.1"].Met = $true } } # Calculate compliance score $requiredControls = $cisControls.Values | Where-Object { $_.Required -eq $true } $metRequired = ($requiredControls | Where-Object { $_.Met -eq $true }).Count $totalRequired = $requiredControls.Count $complianceScore = [math]::Round(($metRequired / $totalRequired) * 100, 2) # Display results Write-Host "========================================" -ForegroundColor Green Write-Host " COMPLIANCE RESULTS" -ForegroundColor Green Write-Host "========================================" -ForegroundColor Green Write-Host "" Write-Host "Overall Compliance Score: $complianceScore%" -ForegroundColor $(if ($complianceScore -ge 90) { "Green" } elseif ($complianceScore -ge 70) { "Yellow" } else { "Red" }) Write-Host "Required Controls Met: $metRequired / $totalRequired" -ForegroundColor White Write-Host "" Write-Host "CIS Control Status:" -ForegroundColor Cyan Write-Host "" foreach ($controlId in ($cisControls.Keys | Sort-Object)) { $control = $cisControls[$controlId] $status = if ($control.Met) { "✅ MET" } else { "❌ NOT MET" } $color = if ($control.Met) { "Green" } else { $(if ($control.Required) { "Red" } else { "Yellow" }) } Write-Host " $controlId - $($control.Description)" -ForegroundColor White Write-Host " Status: $status" -ForegroundColor $color if ($control.Policies.Count -gt 0) { Write-Host " Policies: $($control.Policies.Count)" -ForegroundColor Gray foreach ($policyName in $control.Policies) { Write-Host " - $policyName" -ForegroundColor DarkGray } } Write-Host "" } # Save report $moduleConfigPath = Get-ConfigPath if (Test-Path $moduleConfigPath) { $moduleConfig = Get-Content $moduleConfigPath | ConvertFrom-Json $storagePath = $moduleConfig.StoragePath if (Test-Path $storagePath) { $reportPath = Join-Path $storagePath "CISCompliance" if (-not (Test-Path $reportPath)) { New-Item -Path $reportPath -ItemType Directory -Force | Out-Null } $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" $reportFile = Join-Path $reportPath "cis-compliance-$timestamp.json" $reportFileCsv = Join-Path $reportPath "cis-compliance-$timestamp.csv" # Prepare report data $reportData = @{ AnalysisDate = (Get-Date).ToString("o") CISBenchmark = "Microsoft 365 Foundations Benchmark v6.0.0" ComplianceScore = $complianceScore RequiredControlsMet = $metRequired TotalRequiredControls = $totalRequired TotalPoliciesAnalyzed = $policies.Count Controls = @() } foreach ($controlId in $cisControls.Keys) { $control = $cisControls[$controlId] $reportData.Controls += [PSCustomObject]@{ ControlId = $controlId Description = $control.Description Required = $control.Required Met = $control.Met PolicyCount = $control.Policies.Count Policies = $control.Policies } } $reportData | ConvertTo-Json -Depth 10 | Out-File -FilePath $reportFile -Encoding UTF8 # CSV export $reportData.Controls | Export-Csv -Path $reportFileCsv -NoTypeInformation -Encoding UTF8 Write-Host "========================================" -ForegroundColor Green Write-Host " REPORT SAVED" -ForegroundColor Green Write-Host "========================================" -ForegroundColor Green Write-Host "JSON report: $reportFile" -ForegroundColor White Write-Host "CSV report: $reportFileCsv" -ForegroundColor White Write-Host "" } } return $reportData } catch { Write-Error "Error validating CIS compliance: $_" } } |