|
1 |
| -function Start-AuditLogOrchestrator { |
2 |
| - <# |
3 |
| - .SYNOPSIS |
4 |
| - Start the Audit Log Polling Orchestrator |
5 |
| - #> |
6 |
| - [CmdletBinding(SupportsShouldProcess = $true)] |
7 |
| - param() |
8 |
| - try { |
9 |
| - $AuditLogSearchesTable = Get-CIPPTable -TableName 'AuditLogSearches' |
10 |
| - $AuditLogSearches = Get-CIPPAzDataTableEntity @AuditLogSearchesTable -Filter "CippStatus eq 'Pending'" |
| 1 | +function Test-CIPPAuditLogRules { |
| 2 | + [CmdletBinding()] |
| 3 | + Param( |
| 4 | + [Parameter(Mandatory = $true)] |
| 5 | + $TenantFilter, |
| 6 | + [Parameter(Mandatory = $true)] |
| 7 | + $SearchId |
| 8 | + ) |
11 | 9 |
|
12 |
| - $ConfigTable = Get-CippTable -TableName 'WebhookRules' |
13 |
| - $ConfigEntries = Get-CIPPAzDataTableEntity @ConfigTable |
| 10 | + $Results = [PSCustomObject]@{ |
| 11 | + TotalLogs = 0 |
| 12 | + MatchedLogs = 0 |
| 13 | + MatchedRules = @() |
| 14 | + DataToProcess = @() |
| 15 | + } |
14 | 16 |
|
15 |
| - $TenantList = Get-Tenants -IncludeErrors |
16 |
| - # Round time down to nearest minute |
17 |
| - $Now = Get-Date |
18 |
| - $StartTime = ($Now.AddSeconds(-$Now.Seconds)).AddHours(-1) |
19 |
| - $EndTime = $Now.AddSeconds(-$Now.Seconds) |
| 17 | + $ExtendedPropertiesIgnoreList = @( |
| 18 | + 'OAuth2:Authorize' |
| 19 | + 'OAuth2:Token' |
| 20 | + 'SAS:EndAuth' |
| 21 | + 'SAS:ProcessAuth' |
| 22 | + 'deviceAuth:ReprocessTls' |
| 23 | + 'Consent:Set' |
| 24 | + ) |
20 | 25 |
|
21 |
| - if (($AuditLogSearches | Measure-Object).Count -eq 0) { |
22 |
| - Write-Information 'No audit log searches available' |
23 |
| - } else { |
24 |
| - $Queue = New-CippQueueEntry -Name 'Audit Log Collection' -Reference 'AuditLogCollection' -TotalTasks ($AuditLogSearches).Count |
25 |
| - $Batch = $AuditLogSearches | Sort-Object -Property Tenant -Unique | Select-Object @{Name = 'TenantFilter'; Expression = { $_.Tenant } }, @{Name = 'QueueId'; Expression = { $Queue.RowKey } }, @{Name = 'FunctionName'; Expression = { 'AuditLogTenant' } } |
| 26 | + $TrustedIPTable = Get-CIPPTable -TableName 'trustedIps' |
| 27 | + $ConfigTable = Get-CIPPTable -TableName 'WebhookRules' |
| 28 | + $ConfigEntries = Get-CIPPAzDataTableEntity @ConfigTable |
| 29 | + $Configuration = $ConfigEntries | Where-Object { ($_.Tenants -match $TenantFilter -or $_.Tenants -match 'AllTenants') } | ForEach-Object { |
| 30 | + [pscustomobject]@{ |
| 31 | + Tenants = ($_.Tenants | ConvertFrom-Json).fullValue |
| 32 | + Conditions = $_.Conditions |
| 33 | + Actions = $_.Actions |
| 34 | + LogType = $_.Type |
| 35 | + } |
| 36 | + } |
| 37 | + #write-warning 'Getting audit records from Graph API' |
| 38 | + $SearchResults = Get-CippAuditLogSearchResults -TenantFilter $TenantFilter -QueryId $SearchId |
| 39 | + $LogCount = ($SearchResults | Measure-Object).Count |
| 40 | + $RunGuid = New-Guid |
| 41 | + Write-Warning "Logs to process: $LogCount - RunGuid: $($RunGuid) - $($TenantFilter)" |
| 42 | + $Results.TotalLogs = $LogCount |
| 43 | + if ($LogCount -gt 0) { |
| 44 | + $LocationTable = Get-CIPPTable -TableName 'knownlocationdb' |
| 45 | + $ProcessedData = foreach ($AuditRecord in $SearchResults) { |
| 46 | + $RootProperties = $AuditRecord | Select-Object * -ExcludeProperty auditData |
| 47 | + $Data = $AuditRecord.auditData | Select-Object *, CIPPAction, CIPPClause, CIPPGeoLocation, CIPPBadRepIP, CIPPHostedIP, CIPPIPDetected, CIPPLocationInfo, CIPPExtendedProperties, CIPPDeviceProperties, CIPPParameters, CIPPModifiedProperties, AuditRecord -ErrorAction SilentlyContinue |
| 48 | + try { |
| 49 | + if ($Data.ExtendedProperties) { |
| 50 | + $Data.CIPPExtendedProperties = ($Data.ExtendedProperties | ConvertTo-Json) |
| 51 | + $Data.ExtendedProperties | ForEach-Object { |
| 52 | + if ($_.Value -in $ExtendedPropertiesIgnoreList) { |
| 53 | + #write-warning "No need to process this operation as its in our ignore list. Some extended information: $($data.operation):$($_.Value) - $($TenantFilter)" |
| 54 | + continue |
| 55 | + } |
| 56 | + $Data | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -Force -ErrorAction SilentlyContinue |
| 57 | + } |
| 58 | + } |
| 59 | + if ($Data.DeviceProperties) { |
| 60 | + $Data.CIPPDeviceProperties = ($Data.DeviceProperties | ConvertTo-Json) |
| 61 | + $Data.DeviceProperties | ForEach-Object { $Data | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -Force -ErrorAction SilentlyContinue } |
| 62 | + } |
| 63 | + if ($Data.parameters) { |
| 64 | + $Data.CIPPParameters = ($Data.parameters | ConvertTo-Json) |
| 65 | + $Data.parameters | ForEach-Object { $Data | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -Force -ErrorAction SilentlyContinue } |
| 66 | + } |
| 67 | + if ($Data.ModifiedProperties) { |
| 68 | + $Data.CIPPModifiedProperties = ($Data.ModifiedProperties | ConvertTo-Json) |
| 69 | + try { |
| 70 | + $Data.ModifiedProperties | ForEach-Object { $Data | Add-Member -NotePropertyName "$($_.Name)" -NotePropertyValue "$($_.NewValue)" -Force -ErrorAction SilentlyContinue } |
| 71 | + } catch { |
| 72 | + ##write-warning ($Data.ModifiedProperties | ConvertTo-Json -Depth 10) |
| 73 | + } |
| 74 | + try { |
| 75 | + $Data.ModifiedProperties | ForEach-Object { $Data | Add-Member -NotePropertyName $("Previous_Value_$($_.Name)") -NotePropertyValue "$($_.OldValue)" -Force -ErrorAction SilentlyContinue } |
| 76 | + } catch { |
| 77 | + ##write-warning ($Data.ModifiedProperties | ConvertTo-Json -Depth 10) |
| 78 | + } |
| 79 | + } |
26 | 80 |
|
27 |
| - $InputObject = [PSCustomObject]@{ |
28 |
| - OrchestratorName = 'AuditLogs' |
29 |
| - Batch = @($Batch) |
30 |
| - SkipLog = $true |
31 |
| - } |
32 |
| - if ($PSCmdlet.ShouldProcess('Start-AuditLogOrchestrator', 'Starting Audit Log Polling')) { |
33 |
| - Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) |
| 81 | + if ($Data.clientip) { |
| 82 | + if ($Data.clientip -match '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$') { |
| 83 | + $Data.clientip = $Data.clientip -replace ':\d+$', '' # Remove the port number if present |
| 84 | + } |
| 85 | + # Check if IP is on trusted IP list |
| 86 | + $TrustedIP = Get-CIPPAzDataTableEntity @TrustedIPTable -Filter "PartitionKey eq '$TenantFilter' and RowKey eq '$($Data.clientip)' and state eq 'Trusted'" |
| 87 | + if ($TrustedIP) { |
| 88 | + #write-warning "IP $($Data.clientip) is trusted" |
| 89 | + $Trusted = $true |
| 90 | + } |
| 91 | + if (!$Trusted) { |
| 92 | + $Location = Get-CIPPAzDataTableEntity @LocationTable -Filter "RowKey eq '$($Data.clientIp)'" | Select-Object -Last 1 |
| 93 | + if ($Location) { |
| 94 | + $Country = $Location.CountryOrRegion |
| 95 | + $City = $Location.City |
| 96 | + $Proxy = $Location.Proxy |
| 97 | + $hosting = $Location.Hosting |
| 98 | + $ASName = $Location.ASName |
| 99 | + } else { |
| 100 | + try { |
| 101 | + $Location = Get-CIPPGeoIPLocation -IP $Data.clientip |
| 102 | + } catch { |
| 103 | + #write-warning "Unable to get IP location for $($Data.clientip): $($_.Exception.Message)" |
| 104 | + } |
| 105 | + $Country = if ($Location.CountryCode) { $Location.CountryCode } else { 'Unknown' } |
| 106 | + $City = if ($Location.City) { $Location.City } else { 'Unknown' } |
| 107 | + $Proxy = if ($Location.Proxy -ne $null) { $Location.Proxy } else { 'Unknown' } |
| 108 | + $hosting = if ($Location.Hosting -ne $null) { $Location.Hosting } else { 'Unknown' } |
| 109 | + $ASName = if ($Location.ASName) { $Location.ASName } else { 'Unknown' } |
| 110 | + $IP = $Data.ClientIP |
| 111 | + $LocationInfo = @{ |
| 112 | + RowKey = [string]$Data.clientip |
| 113 | + PartitionKey = [string]$Data.id |
| 114 | + Tenant = [string]$TenantFilter |
| 115 | + CountryOrRegion = "$Country" |
| 116 | + City = "$City" |
| 117 | + Proxy = "$Proxy" |
| 118 | + Hosting = "$hosting" |
| 119 | + ASName = "$ASName" |
| 120 | + } |
| 121 | + try { |
| 122 | + $null = Add-CIPPAzDataTableEntity @LocationTable -Entity $LocationInfo -Force |
| 123 | + } catch { |
| 124 | + #write-warning "Failed to add location info for $($Data.clientip) to cache: $($_.Exception.Message)" |
| 125 | + |
| 126 | + } |
| 127 | + } |
| 128 | + $Data.CIPPGeoLocation = $Country |
| 129 | + $Data.CIPPBadRepIP = $Proxy |
| 130 | + $Data.CIPPHostedIP = $hosting |
| 131 | + $Data.CIPPIPDetected = $IP |
| 132 | + $Data.CIPPLocationInfo = ($Location | ConvertTo-Json) |
| 133 | + $Data.AuditRecord = ($RootProperties | ConvertTo-Json) |
| 134 | + } |
| 135 | + } |
| 136 | + $Data | Select-Object * -ExcludeProperty ExtendedProperties, DeviceProperties, parameters |
| 137 | + } catch { |
| 138 | + #write-warning "Audit log: Error processing data: $($_.Exception.Message)`r`n$($_.InvocationInfo.PositionMessage)" |
| 139 | + Write-LogMessage -API 'Webhooks' -message 'Error Processing Audit Log Data' -LogData (Get-CippException -Exception $_) -sev Error -tenant $TenantFilter |
34 | 140 | }
|
35 | 141 | }
|
| 142 | + #write-warning "Processed Data: $(($ProcessedData | Measure-Object).Count) - This should be higher than 0 in many cases, because the where object has not run yet." |
| 143 | + #write-warning "Creating filters - $(($ProcessedData.operation | Sort-Object -Unique) -join ',') - $($TenantFilter)" |
36 | 144 |
|
37 |
| - Write-Information 'Audit Logs: Creating new searches' |
38 |
| - foreach ($Tenant in $TenantList) { |
39 |
| - $Configuration = $ConfigEntries | Where-Object { ($_.Tenants -match $TenantFilter -or $_.Tenants -match 'AllTenants') } |
40 |
| - if ($Configuration) { |
41 |
| - $ServiceFilters = $Configuration | Select-Object -Property type | Sort-Object -Property type -Unique | ForEach-Object { $_.type.split('.')[1] } |
42 |
| - try { |
43 |
| - $LogSearch = @{ |
44 |
| - StartTime = $StartTime |
45 |
| - EndTime = $EndTime |
46 |
| - ServiceFilters = $ServiceFilters |
47 |
| - TenantFilter = $Tenant.defaultDomainName |
48 |
| - ProcessLogs = $true |
49 |
| - RecordTypeFilters = @( |
50 |
| - 'exchangeAdmin', 'azureActiveDirectory', 'azureActiveDirectoryAccountLogon', 'dataCenterSecurityCmdlet', |
51 |
| - 'complianceDLPSharePoint', 'complianceDLPExchange', 'azureActiveDirectoryStsLogon', 'skypeForBusinessPSTNUsage', |
52 |
| - 'skypeForBusinessUsersBlocked', 'securityComplianceCenterEOPCmdlet', 'microsoftFlow', 'aeD', 'microsoftStream', |
53 |
| - 'threatFinder', 'project', 'dataGovernance', 'securityComplianceAlerts', 'threatIntelligenceUrl', |
54 |
| - 'securityComplianceInsights', 'mipLabel', 'workplaceAnalytics', 'powerAppsApp', 'powerAppsPlan', |
55 |
| - 'threatIntelligenceAtpContent', 'labelContentExplorer', 'hygieneEvent', |
56 |
| - 'dataInsightsRestApiAudit', 'informationBarrierPolicyApplication', 'microsoftTeamsAdmin', 'hrSignal', |
57 |
| - 'informationWorkerProtection', 'campaign', 'dlpEndpoint', 'airInvestigation', 'quarantine', 'microsoftForms', |
58 |
| - 'applicationAudit', 'complianceSupervisionExchange', 'customerKeyServiceEncryption', 'officeNative', |
59 |
| - 'mipAutoLabelSharePointItem', 'mipAutoLabelSharePointPolicyLocation', 'secureScore', |
60 |
| - 'mipAutoLabelExchangeItem', 'cortanaBriefing', 'search', 'wdatpAlerts', 'powerPlatformAdminDlp', |
61 |
| - 'powerPlatformAdminEnvironment', 'mdatpAudit', 'sensitivityLabelPolicyMatch', 'sensitivityLabelAction', |
62 |
| - 'sensitivityLabeledFileAction', 'attackSim', 'airManualInvestigation', 'securityComplianceRBAC', 'userTraining', |
63 |
| - 'airAdminActionInvestigation', 'mstic', 'physicalBadgingSignal', 'aipDiscover', 'aipSensitivityLabelAction', |
64 |
| - 'aipProtectionAction', 'aipFileDeleted', 'aipHeartBeat', 'mcasAlerts', 'onPremisesFileShareScannerDlp', |
65 |
| - 'onPremisesSharePointScannerDlp', 'exchangeSearch', 'privacyDataMinimization', 'labelAnalyticsAggregate', |
66 |
| - 'myAnalyticsSettings', 'securityComplianceUserChange', 'complianceDLPExchangeClassification', |
67 |
| - 'complianceDLPEndpoint', 'mipExactDataMatch', 'msdeResponseActions', 'msdeGeneralSettings', 'msdeIndicatorsSettings', |
68 |
| - 'ms365DCustomDetection', 'msdeRolesSettings', 'mapgAlerts', 'mapgPolicy', 'mapgRemediation', |
69 |
| - 'privacyRemediationAction', 'privacyDigestEmail', 'mipAutoLabelSimulationProgress', 'mipAutoLabelSimulationCompletion', |
70 |
| - 'mipAutoLabelProgressFeedback', 'dlpSensitiveInformationType', 'mipAutoLabelSimulationStatistics', |
71 |
| - 'largeContentMetadata', 'microsoft365Group', 'cdpMlInferencingResult', 'filteringMailMetadata', |
72 |
| - 'cdpClassificationMailItem', 'cdpClassificationDocument', 'officeScriptsRunAction', 'filteringPostMailDeliveryAction', |
73 |
| - 'cdpUnifiedFeedback', 'tenantAllowBlockList', 'consumptionResource', 'healthcareSignal', 'dlpImportResult', |
74 |
| - 'cdpCompliancePolicyExecution', 'multiStageDisposition', 'privacyDataMatch', 'filteringDocMetadata', |
75 |
| - 'filteringEmailFeatures', 'powerBIDlp', 'filteringUrlInfo', 'filteringAttachmentInfo', 'coreReportingSettings', |
76 |
| - 'complianceConnector', 'powerPlatformLockboxResourceAccessRequest', 'powerPlatformLockboxResourceCommand', |
77 |
| - 'cdpPredictiveCodingLabel', 'cdpCompliancePolicyUserFeedback', 'webpageActivityEndpoint', 'omePortal', |
78 |
| - 'cmImprovementActionChange', 'filteringUrlClick', 'mipLabelAnalyticsAuditRecord', 'filteringEntityEvent', |
79 |
| - 'filteringRuleHits', 'filteringMailSubmission', 'labelExplorer', 'microsoftManagedServicePlatform', |
80 |
| - 'powerPlatformServiceActivity', 'scorePlatformGenericAuditRecord', 'filteringTimeTravelDocMetadata', 'alert', |
81 |
| - 'alertStatus', 'alertIncident', 'incidentStatus', 'case', 'caseInvestigation', 'recordsManagement', |
82 |
| - 'privacyRemediation', 'dataShareOperation', 'cdpDlpSensitive', 'ehrConnector', 'filteringMailGradingResult', |
83 |
| - 'microsoftTodoAudit', 'timeTravelFilteringDocMetadata', 'microsoftDefenderForIdentityAudit', |
84 |
| - 'supervisoryReviewDayXInsight', 'defenderExpertsforXDRAdmin', 'cdpEdgeBlockedMessage', 'hostedRpa', |
85 |
| - 'cdpContentExplorerAggregateRecord', 'cdpHygieneAttachmentInfo', 'cdpHygieneSummary', 'cdpPostMailDeliveryAction', |
86 |
| - 'cdpEmailFeatures', 'cdpHygieneUrlInfo', 'cdpUrlClick', 'cdpPackageManagerHygieneEvent', 'filteringDocScan', |
87 |
| - 'timeTravelFilteringDocScan', 'mapgOnboard' |
88 |
| - ) |
| 145 | + $Where = $Configuration | ForEach-Object { |
| 146 | + $conditions = $_.Conditions | ConvertFrom-Json | Where-Object { $_.Input.value -ne '' } |
| 147 | + $actions = $_.Actions |
| 148 | + $conditionStrings = [System.Collections.Generic.List[string]]::new() |
| 149 | + $CIPPClause = [System.Collections.Generic.List[string]]::new() |
| 150 | + foreach ($condition in $conditions) { |
| 151 | + $value = if ($condition.Input.value -is [array]) { |
| 152 | + $arrayAsString = $condition.Input.value | ForEach-Object { |
| 153 | + "'$_'" |
89 | 154 | }
|
90 |
| - $NewSearch = New-CippAuditLogSearch @LogSearch |
91 |
| - Write-Information "Created audit log search $($Tenant.defaultDomainName) - $($NewSearch.displayName)" |
92 |
| - } catch { |
93 |
| - Write-Information "Error creating audit log search $($Tenant.defaultDomainName) - $($_.Exception.Message)" |
| 155 | + "@($($arrayAsString -join ', '))" |
| 156 | + } else { "'$($condition.Input.value)'" } |
| 157 | + |
| 158 | + $conditionStrings.Add("`$(`$_.$($condition.Property.label)) -$($condition.Operator.value) $value") |
| 159 | + $CIPPClause.Add("$($condition.Property.label) is $($condition.Operator.label) $value") |
| 160 | + } |
| 161 | + $finalCondition = $conditionStrings -join ' -AND ' |
| 162 | + |
| 163 | + [PSCustomObject]@{ |
| 164 | + clause = $finalCondition |
| 165 | + expectedAction = $actions |
| 166 | + CIPPClause = $CIPPClause |
| 167 | + } |
| 168 | + |
| 169 | + } |
| 170 | + |
| 171 | + $MatchedRules = [System.Collections.Generic.List[string]]::new() |
| 172 | + $DataToProcess = foreach ($clause in $Where) { |
| 173 | + #write-warning "Webhook: Processing clause: $($clause.clause)" |
| 174 | + $ReturnedData = $ProcessedData | Where-Object { Invoke-Expression $clause.clause } |
| 175 | + if ($ReturnedData) { |
| 176 | + #write-warning "Webhook: There is matching data: $(($ReturnedData.operation | Select-Object -Unique) -join ', ')" |
| 177 | + $ReturnedData = foreach ($item in $ReturnedData) { |
| 178 | + $item.CIPPAction = $clause.expectedAction |
| 179 | + $item.CIPPClause = $clause.CIPPClause -join ' and ' |
| 180 | + $MatchedRules.Add($clause.CIPPClause -join ' and ') |
| 181 | + $item |
94 | 182 | }
|
95 | 183 | }
|
| 184 | + $ReturnedData |
96 | 185 | }
|
97 |
| - } catch { |
98 |
| - Write-LogMessage -API 'Audit Logs' -message 'Error processing audit logs' -sev Error -LogData (Get-CippException -Exception $_) |
99 |
| - Write-Information ( 'Audit logs error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) |
| 186 | + $Results.MatchedRules = @($MatchedRules | Select-Object -Unique) |
| 187 | + $Results.MatchedLogs = ($DataToProcess | Measure-Object).Count |
| 188 | + $Results.DataToProcess = $DataToProcess |
100 | 189 | }
|
| 190 | + Write-Warning "Finished - RunGuid: $($RunGuid) - $($TenantFilter)" |
| 191 | + $Results |
101 | 192 | }
|
0 commit comments