Src/Private/Get-AbrExoAuditLogging.ps1
|
function Get-AbrExoAuditLogging { <# .SYNOPSIS Documents Exchange Online audit logging configuration with ACSC E8 and CIS compliance assessments. .NOTES Version: 0.1.0 Author: Pai Wei Sing #> [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory)] [string]$TenantId ) begin { Write-PScriboMessage -Message "Collecting Exchange Online Audit Logging configuration for $TenantId." Show-AbrDebugExecutionTime -Start -TitleMessage 'AuditLogging' } process { Section -Style Heading2 'Audit Configuration' { Paragraph "The following section documents audit logging configuration for tenant $TenantId." BlankLine $AdminAuditLogEnabled = $false $AdminAuditLogAgeLimit = 0 $MailboxAuditBypassCount = 0 #region Admin Audit Log Config try { Write-Host " - Retrieving admin audit log configuration..." $AuditConfig = Get-AdminAuditLogConfig -ErrorAction Stop $AdminAuditLogEnabled = ($AuditConfig.AdminAuditLogEnabled -eq $true) $UnifiedAuditLogEnabled = ($AuditConfig.UnifiedAuditLogIngestionEnabled -eq $true) # AdminAuditLogAgeLimit is returned as a string "90.00:00:00" in EXO, not a TimeSpan object. # Parse it safely: take the integer part before the first dot/colon. $AdminAuditLogAgeLimit = 0 $ageLimitRaw = $AuditConfig.AdminAuditLogAgeLimit if ($ageLimitRaw) { try { if ($ageLimitRaw -is [TimeSpan]) { $AdminAuditLogAgeLimit = [int]$ageLimitRaw.TotalDays } elseif ("$ageLimitRaw" -match '^(\d+)') { $AdminAuditLogAgeLimit = [int]$Matches[1] } } catch { $AdminAuditLogAgeLimit = 0 } } $AuditObj = [System.Collections.ArrayList]::new() $auditInObj = [ordered] @{ 'Admin Audit Log Enabled' = $AuditConfig.AdminAuditLogEnabled 'Unified Audit Log Ingestion' = $AuditConfig.UnifiedAuditLogIngestionEnabled 'Admin Audit Log Age Limit (days)'= $AdminAuditLogAgeLimit 'Admin Audit Log Cmdlets' = if ($AuditConfig.AdminAuditLogCmdlets -eq '*') { 'All Cmdlets' } else { ($AuditConfig.AdminAuditLogCmdlets -join ', ') } 'Admin Audit Log Parameters' = if ($AuditConfig.AdminAuditLogParameters -eq '*') { 'All Parameters' } else { ($AuditConfig.AdminAuditLogParameters -join ', ') } 'Log Level' = $AuditConfig.LogLevel } $AuditObj.Add([pscustomobject](ConvertTo-HashToYN $auditInObj)) | Out-Null $null = (& { if ($HealthCheck.ExchangeOnline.AuditLogging) { $null = ($AuditObj | Where-Object { $_.'Admin Audit Log Enabled' -eq 'No' } | Set-Style -Style Critical | Out-Null) $null = ($AuditObj | Where-Object { $_.'Admin Audit Log Age Limit (days)' -ne '--' -and [int]$_.'Admin Audit Log Age Limit (days)' -lt 90 } | Set-Style -Style Warning | Out-Null) } }) $AuditTableParams = @{ Name = "Admin Audit Log Configuration - $TenantId"; List = $true; ColumnWidths = 45, 55 } if ($Report.ShowTableCaptions) { $AuditTableParams['Caption'] = "- $($AuditTableParams.Name)" } $AuditObj | Table @AuditTableParams $script:ExcelSheets['Audit Config'] = $AuditObj } catch { Write-ExoError 'AuditLogging' "Unable to retrieve audit log config: $($_.Exception.Message)" Paragraph "Unable to retrieve admin audit log configuration: $($_.Exception.Message)" } #endregion #region Mailbox Audit Bypass try { Write-Host " - Checking mailbox audit bypass assignments..." $AuditBypass = Get-MailboxAuditBypassAssociation -ResultSize Unlimited -ErrorAction Stop | Where-Object { $_.AuditBypassEnabled -eq $true } $MailboxAuditBypassCount = @($AuditBypass).Count if ($AuditBypass -and $MailboxAuditBypassCount -gt 0) { Section -Style Heading3 'Mailboxes with Audit Logging Bypassed' { Paragraph "WARNING: The following $MailboxAuditBypassCount account(s) have mailbox audit bypass enabled. These accounts will not generate mailbox audit log entries, creating blind spots in forensic investigations." BlankLine $BypassObj = [System.Collections.ArrayList]::new() foreach ($Bypass in $AuditBypass) { $bypassInObj = [ordered] @{ 'Name' = $Bypass.Name 'Bypass Enabled' = $Bypass.AuditBypassEnabled } $BypassObj.Add([pscustomobject](ConvertTo-HashToYN $bypassInObj)) | Out-Null } $null = (& { if ($HealthCheck.ExchangeOnline.AuditLogging) { $null = ($BypassObj | Set-Style -Style Warning | Out-Null) } }) $BypassTableParams = @{ Name = "Audit Bypass Accounts - $TenantId"; List = $false; ColumnWidths = 70, 30 } if ($Report.ShowTableCaptions) { $BypassTableParams['Caption'] = "- $($BypassTableParams.Name)" } $BypassObj | Table @BypassTableParams $script:ExcelSheets['Audit Bypass Accounts'] = $BypassObj } } else { Paragraph "No mailbox audit bypass assignments found. All mailboxes have audit logging enabled." } } catch { Write-ExoError 'AuditLogging' "Unable to retrieve audit bypass assignments: $($_.Exception.Message)" } #endregion BlankLine Paragraph "ACSC Essential Eight Assessment" BlankLine $e8Vars = @{ AdminAuditLogEnabled = $AdminAuditLogEnabled AdminAuditLogAgeLimit = $AdminAuditLogAgeLimit MailboxAuditBypassCount = $MailboxAuditBypassCount } $e8Checks = Build-AbrExoComplianceChecks -Definitions (Get-AbrExoE8Checks 'AuditLogging') -Framework 'E8' -CallerVariables $e8Vars New-AbrExoE8AssessmentTable -Checks $e8Checks -Name 'Audit Logging' -TenantId $TenantId if ($e8Checks) { foreach ($row in $e8Checks) { $null = $script:E8AllChecks.Add([pscustomobject]@{ Section = 'Audit Logging' ML = $row.ML Control = $row.Control Status = $row.Status Detail = $row.Detail }) } } BlankLine Paragraph "CIS Microsoft 365 Foundations Benchmark Assessment" BlankLine $cisVars = @{ AdminAuditLogEnabled = $UnifiedAuditLogEnabled MailboxAuditBypassCount = $MailboxAuditBypassCount } $cisChecks = Build-AbrExoComplianceChecks -Definitions (Get-AbrExoCISChecks 'AuditLogging') -Framework 'CIS' -CallerVariables $cisVars New-AbrExoCISAssessmentTable -Checks $cisChecks -Name 'Audit Logging' -TenantId $TenantId if ($cisChecks) { foreach ($row in $cisChecks) { $null = $script:CISAllChecks.Add([pscustomobject]@{ Section = 'Audit Logging' CISControl = $row.CISControl Level = $row.Level Status = $row.Status Detail = $row.Detail }) } } } } end { Show-AbrDebugExecutionTime -End -TitleMessage 'AuditLogging' } } |