From 4272a3cd9b197a84f454da3d40bfe4b30877d3ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Bentsen=20Kj=C3=A6rg=C3=A5rd=20=28KBK=29?= Date: Wed, 13 Nov 2024 11:14:17 +0100 Subject: [PATCH 01/22] Exclude more useless domains from the DA --- .../Domain Analyser/Push-DomainAnalyserTenant.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-DomainAnalyserTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-DomainAnalyserTenant.ps1 index 3b4e14f17ba2..fc530103ede9 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-DomainAnalyserTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-DomainAnalyserTenant.ps1 @@ -27,7 +27,9 @@ function Push-DomainAnalyserTenant { '*.excl.cloud' '*.codetwo.online' '*.call2teams.com' - '*signature365.net' + '*.signature365.net' + '*.myteamsconnect.io' + '*.teams.dstny.com' ) $Domains = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains' -tenantid $Tenant.customerId | Where-Object { $_.isVerified -eq $true } | ForEach-Object { $Domain = $_ From af64f8f00851c7c4e901109228a17c079b287f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 13 Nov 2024 19:15:54 +0100 Subject: [PATCH 02/22] flip null comparisons --- Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 b/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 index 712301b3dc77..9cb9e6914f9c 100644 --- a/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPMFAState.ps1 @@ -92,9 +92,9 @@ function Get-CIPPMFAState { } } - $PerUser = if ($PerUserMFAState -eq $null) { $null } else { ($PerUserMFAState | Where-Object -Property UserPrincipalName -EQ $_.UserPrincipalName).PerUserMFAState } + $PerUser = if ($null -eq $PerUserMFAState) { $null } else { ($PerUserMFAState | Where-Object -Property UserPrincipalName -EQ $_.UserPrincipalName).PerUserMFAState } - $MFARegUser = if (($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.userPrincipalName).isMFARegistered -eq $null) { $false } else { ($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.userPrincipalName) } + $MFARegUser = if ($null -eq ($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.userPrincipalName).isMFARegistered) { $false } else { ($MFARegistration | Where-Object -Property UserPrincipalName -EQ $_.userPrincipalName) } [PSCustomObject]@{ Tenant = $TenantFilter From ed55e69a06ccb7b687beae87f1e8382134ba8d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 13 Nov 2024 19:33:04 +0100 Subject: [PATCH 03/22] Add Invoke-ListPerUserMFA function for retrieving MFA state per user --- .../Users/Invoke-ListPerUserMFA.ps1 | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListPerUserMFA.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListPerUserMFA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListPerUserMFA.ps1 new file mode 100644 index 000000000000..0d3f4c16d914 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListPerUserMFA.ps1 @@ -0,0 +1,50 @@ +using namespace System.Net + +function Invoke-ListPerUserMFA { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Identity.User.Read + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + $User = $request.headers.'x-ms-client-principal' + Write-LogMessage -user $User -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + + # Parse query parameters + $Tenant = $Request.query.TenantFilter + try { + $AllUsers = [System.Convert]::ToBoolean($Request.query.allUsers) + } catch { + $AllUsers = $false + } + $UserId = $Request.query.userId + + # Get the MFA state for the user/all users + try { + if ($AllUsers -eq $true) { + $Results = Get-CIPPPerUserMFA -TenantFilter $Tenant -AllUsers $true + } else { + $Results = Get-CIPPPerUserMFA -TenantFilter $Tenant -userId $UserId + } + $StatusCode = [HttpStatusCode]::OK + } catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + $Results = "Failed to get MFA State for $UserId : $ErrorMessage" + $StatusCode = [HttpStatusCode]::Forbidden + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = @($Results) + }) + + +} From 9784888da622e8dfee4a540e6a66de53a2acaf3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 13 Nov 2024 19:43:45 +0100 Subject: [PATCH 04/22] Whoops forgot camel casing --- .../Identity/Administration/Users/Invoke-ListPerUserMFA.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListPerUserMFA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListPerUserMFA.ps1 index 0d3f4c16d914..39bbcd9bc268 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListPerUserMFA.ps1 @@ -18,7 +18,7 @@ function Invoke-ListPerUserMFA { Write-Host 'PowerShell HTTP trigger function processed a request.' # Parse query parameters - $Tenant = $Request.query.TenantFilter + $Tenant = $Request.query.tenantFilter try { $AllUsers = [System.Convert]::ToBoolean($Request.query.allUsers) } catch { From 0446511424bbf8b9050730f19f60908e8c69e0f9 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 13 Nov 2024 17:27:37 -0500 Subject: [PATCH 05/22] Update Invoke-PublicWebhooks.ps1 --- .../Tenant/Administration/Alerts/Invoke-PublicWebhooks.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-PublicWebhooks.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-PublicWebhooks.ps1 index 3a4e60373847..648a32e76c1c 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-PublicWebhooks.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-PublicWebhooks.ps1 @@ -65,7 +65,8 @@ function Invoke-PublicWebhooks { } Add-CIPPAzDataTableEntity @WebhookIncoming -Entity $Entity } else { - return 'Not replying to this webhook or processing it' + $Body = 'This webhook is not authorized.' + $StatusCode = [HttpStatusCode]::Forbidden } $Body = 'Webhook Recieved' $StatusCode = [HttpStatusCode]::OK @@ -80,4 +81,4 @@ function Invoke-PublicWebhooks { StatusCode = $StatusCode Body = $Body }) -} \ No newline at end of file +} From 90de9022c68aa1488299b0cb1164e64e93505926 Mon Sep 17 00:00:00 2001 From: Esco Date: Thu, 14 Nov 2024 10:11:58 +0100 Subject: [PATCH 06/22] fix: Flip disable alerts --- .../Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 | 4 ++-- .../Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 | 4 ++-- .../Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 | 4 ++-- .../Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 index 8dd565ff762c..a23c3b7b2610 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 @@ -42,9 +42,9 @@ function Invoke-CIPPStandardDisableEmail { if ($Settings.alert -eq $true) { if ($StateIsCorrect -eq $true) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Email authentication method is enabled' -sev Alert - } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Email authentication method is not enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Email authentication method is enabled' -sev Alert } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 index 30af4f6a7bfd..929b56e27df7 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 @@ -42,9 +42,9 @@ function Invoke-CIPPStandardDisableSMS { if ($Settings.alert -eq $true) { if ($StateIsCorrect -eq $true) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'SMS authentication method is enabled' -sev Alert - } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'SMS authentication method is not enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'SMS authentication method is enabled' -sev Alert } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 index e8accc7f686f..d3de8e7f02dc 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 @@ -42,9 +42,9 @@ function Invoke-CIPPStandardDisableVoice { if ($Settings.alert -eq $true) { if ($StateIsCorrect -eq $true) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'Voice authentication method is enabled' -sev Alert - } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Voice authentication method is not enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Voice authentication method is enabled' -sev Alert } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 index 3a10f191b71e..27b1e9c67362 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 @@ -42,9 +42,9 @@ function Invoke-CIPPStandardDisablex509Certificate { if ($Settings.alert -eq $true) { if ($StateIsCorrect -eq $true) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'x509Certificate authentication method is enabled' -sev Alert - } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'x509Certificate authentication method is not enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'x509Certificate authentication method is enabled' -sev Alert } } From 204807373c4b59ceb476d6fcd1385b71aa2bda4e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 14 Nov 2024 13:48:46 -0500 Subject: [PATCH 07/22] Table cleanup --- CIPPTimers.json | 8 ++ .../Timer Functions/Start-TableCleanup.ps1 | 82 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-TableCleanup.ps1 diff --git a/CIPPTimers.json b/CIPPTimers.json index c38acd967d56..0d1d6dca1038 100644 --- a/CIPPTimers.json +++ b/CIPPTimers.json @@ -142,5 +142,13 @@ "Priority": 15, "RunOnProcessor": true, "IsSystem": true + }, + { + "Command": "Start-TableCleanup", + "Description": "Timer to cleanup tables", + "Cron": "0 0 23 * * *", + "Priority": 20, + "RunOnProcessor": true, + "IsSystem": true } ] diff --git a/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-TableCleanup.ps1 b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-TableCleanup.ps1 new file mode 100644 index 000000000000..ff635480a672 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-TableCleanup.ps1 @@ -0,0 +1,82 @@ +function Start-TableCleanup { + <# + .SYNOPSIS + Start the Table Cleanup Timer + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + + $CleanupRules = @( + @{ + DataTableProps = @{ + Context = (Get-CIPPTable -tablename 'webhookTable').Context + Property = @('PartitionKey', 'RowKey', 'ETag', 'Resource') + } + Where = "`$_.Resource -match '^Audit'" + } + @{ + DataTableProps = @{ + Context = (Get-CIPPTable -tablename 'AuditLogSearches').Context + Filter = "Timestamp lt datetime'$((Get-Date).AddDays(-7).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ'))'" + First = 10000 + Property = @('PartitionKey', 'RowKey', 'ETag') + } + } + @{ + DataTableProps = @{ + Context = (Get-CIPPTable -tablename 'CippFunctionStats').Context + Filter = "Timestamp lt datetime'$((Get-Date).AddDays(-7).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ'))'" + First = 10000 + Property = @('PartitionKey', 'RowKey', 'ETag') + } + } + @{ + DataTableProps = @{ + Context = (Get-CIPPTable -tablename 'CippQueue').Context + Filter = "Timestamp lt datetime'$((Get-Date).AddDays(-7).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ'))'" + First = 10000 + Property = @('PartitionKey', 'RowKey', 'ETag') + } + } + @{ + DataTableProps = @{ + Context = (Get-CIPPTable -tablename 'CippQueueTasks').Context + Filter = "Timestamp lt datetime'$((Get-Date).AddDays(-7).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ'))'" + First = 10000 + Property = @('PartitionKey', 'RowKey', 'ETag') + } + } + ) + + if ($PSCmdlet.ShouldProcess('Start-TableCleanup', 'Starting Table Cleanup')) { + Write-Information 'Starting table cleanup' + foreach ($Rule in $CleanupRules) { + if ($Rule.Where) { + $Where = [scriptblock]::Create($Rule.Where) + } else { + $Where = { $true } + } + $DataTableProps = $Rule.DataTableProps + + $CleanupCompleted = $false + do { + $Entities = Get-AzDataTableEntity @DataTableProps | Where-Object $Where + if ($Entities) { + Write-Information "Removing $($Entities.Count) entities from $($Rule.DataTableProps.Context.TableName)" + try { + Remove-AzDataTableEntity -Context $DataTableProps.Context -Entity $Entities -Force + if ($DataTableProps.First -and $Entities.Count -lt $DataTableProps.First) { + $CleanupCompleted = $true + } + } catch { + Write-LogMessage -API 'TableCleanup' -message "Failed to remove entities from $($DataTableProps.Context.TableName)" -sev Error -LogData (Get-CippException -Exception $_) + $CleanupCompleted = $true + } + } else { + $CleanupCompleted = $true + } + } while (!$CleanupCompleted) + } + Write-Information 'Table cleanup complete' + } +} From a8ef1800b0f130ac983eeba76b7fc5d44e6078e5 Mon Sep 17 00:00:00 2001 From: Esco Date: Fri, 15 Nov 2024 11:02:35 +0100 Subject: [PATCH 08/22] fix: fix check for TAP isUsableOnce --- .../CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 index 744102a249dc..eb72868d0ce9 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 @@ -31,19 +31,14 @@ function Invoke-CIPPStandardTAP { ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'TAP' $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -tenantid $Tenant + if ($null -eq $Settings.config) { $Settings.config = $True } $StateIsCorrect = ($CurrentState.state -eq 'enabled') -and - ($CurrentState.isUsableOnce -eq $Settings.config) + ([System.Convert]::ToBoolean($CurrentState.isUsableOnce) -eq [System.Convert]::ToBoolean($Settings.config)) if ($Settings.report -eq $true) { Add-CIPPBPAField -FieldName 'TemporaryAccessPass' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $tenant } - # Input validation - if (([string]::IsNullOrWhiteSpace($Settings.config) -or $Settings.config -eq 'Select a value') -and ($Settings.remediate -eq $true -or $Settings.alert -eq $true)) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'TAP: Invalid state parameter set' -sev Error - Return - } - If ($Settings.remediate -eq $true) { if ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Temporary Access Passwords is already enabled.' -sev Info From 1c94421382cf57de1743042de7141bceaab764cd Mon Sep 17 00:00:00 2001 From: Esco Date: Fri, 15 Nov 2024 13:11:48 +0100 Subject: [PATCH 09/22] fix: fix device wipe action --- .../Endpoint/MEM/Invoke-ExecDeviceAction.ps1 | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecDeviceAction.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecDeviceAction.ps1 index ca788cb979da..d168060b4abf 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecDeviceAction.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecDeviceAction.ps1 @@ -20,7 +20,19 @@ Function Invoke-ExecDeviceAction { if ($Request.Query.Action -eq 'setDeviceName') { $ActionBody = @{ deviceName = $Request.Body.input } | ConvertTo-Json -Compress } - $ActionResult = New-CIPPDeviceAction -Action $Request.Query.Action -ActionBody $ActionBody -DeviceFilter $Request.Query.GUID -TenantFilter $Request.Query.TenantFilter -ExecutingUser $request.headers.'x-ms-client-principal' -APINAME $APINAME + else { + $ActionBody = $Request.Body | ConvertTo-Json -Compress + } + + $cmdparams = @{ + Action = $Request.Query.Action + ActionBody = $ActionBody + DeviceFilter = $Request.Query.GUID + TenantFilter = $Request.Query.TenantFilter + ExecutingUser = $request.headers.'x-ms-client-principal' + APINAME = $APINAME + } + $ActionResult = New-CIPPDeviceAction @cmdparams $body = [pscustomobject]@{'Results' = "$ActionResult" } } catch { From 25ea6733a43c0ce384a014fe558ca04406291bd3 Mon Sep 17 00:00:00 2001 From: Brandon Martinez Date: Fri, 15 Nov 2024 17:09:57 -0800 Subject: [PATCH 10/22] Fix duplicate graph request in group add --- .../Identity/Administration/Groups/Invoke-AddGroup.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroup.ps1 index f4dda9d8f50a..7dce9d72d194 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroup.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroup.ps1 @@ -65,7 +65,6 @@ Function Invoke-AddGroup { } $GraphRequest = New-ExoRequest -tenantid $tenant -cmdlet 'New-DistributionGroup' -cmdParams $params } - $GraphRequest = New-ExoRequest -tenantid $tenant -cmdlet 'New-DistributionGroup' -cmdParams $params # At some point add logic to use AddOwner/AddMember for New-DistributionGroup, but idk how we're going to brr that - rvdwegen } "Successfully created group $($groupobj.displayname) for $($tenant)" From e593668d142cec32780c2cf69ff8e80efc281eda Mon Sep 17 00:00:00 2001 From: Kiyo Akaiwa Date: Mon, 18 Nov 2024 18:45:33 -0500 Subject: [PATCH 11/22] Added check for MDO licenses for AntiPhish/SafeLinks/SafeAttachment Standards --- .../Invoke-CIPPStandardAntiPhishPolicy.ps1 | 112 +++++---- ...nvoke-CIPPStandardSafeAttachmentPolicy.ps1 | 195 ++++++++------- .../Invoke-CIPPStandardSafeLinksPolicy.ps1 | 227 ++++++++++-------- 3 files changed, 298 insertions(+), 236 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAntiPhishPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAntiPhishPolicy.ps1 index b57c2a2a3576..d8e819072ba0 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAntiPhishPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAntiPhishPolicy.ps1 @@ -51,6 +51,11 @@ function Invoke-CIPPStandardAntiPhishPolicy { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'AntiPhishPolicy' + $ServicePlans = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus?$select=servicePlans' -tenantid $Tenant + $ServicePlans = $ServicePlans.servicePlans.servicePlanName + $MDOLicensed = $ServicePlans -contains "ATP_ENTERPRISE" + Write-Information "MDOLicensed: $MDOLicensed" + $PolicyList = @('CIPP Default Anti-Phishing Policy','Default Anti-Phishing Policy') $ExistingPolicy = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AntiPhishPolicy' | Where-Object -Property Name -In $PolicyList if ($null -eq $ExistingPolicy.Name) { @@ -69,27 +74,38 @@ function Invoke-CIPPStandardAntiPhishPolicy { $CurrentState = $ExistingPolicy | Select-Object Name, Enabled, PhishThresholdLevel, EnableMailboxIntelligence, EnableMailboxIntelligenceProtection, EnableSpoofIntelligence, EnableFirstContactSafetyTips, EnableSimilarUsersSafetyTips, EnableSimilarDomainsSafetyTips, EnableUnusualCharactersSafetyTips, EnableUnauthenticatedSender, EnableViaTag, AuthenticationFailAction, SpoofQuarantineTag, MailboxIntelligenceProtectionAction, MailboxIntelligenceQuarantineTag, TargetedUserProtectionAction, TargetedUserQuarantineTag, TargetedDomainProtectionAction, TargetedDomainQuarantineTag, EnableOrganizationDomainsProtection - $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and - ($CurrentState.Enabled -eq $true) -and - ($CurrentState.PhishThresholdLevel -eq $Settings.PhishThresholdLevel) -and - ($CurrentState.EnableMailboxIntelligence -eq $true) -and - ($CurrentState.EnableMailboxIntelligenceProtection -eq $true) -and - ($CurrentState.EnableSpoofIntelligence -eq $true) -and - ($CurrentState.EnableFirstContactSafetyTips -eq $Settings.EnableFirstContactSafetyTips) -and - ($CurrentState.EnableSimilarUsersSafetyTips -eq $Settings.EnableSimilarUsersSafetyTips) -and - ($CurrentState.EnableSimilarDomainsSafetyTips -eq $Settings.EnableSimilarDomainsSafetyTips) -and - ($CurrentState.EnableUnusualCharactersSafetyTips -eq $Settings.EnableUnusualCharactersSafetyTips) -and - ($CurrentState.EnableUnauthenticatedSender -eq $true) -and - ($CurrentState.EnableViaTag -eq $true) -and - ($CurrentState.AuthenticationFailAction -eq $Settings.AuthenticationFailAction) -and - ($CurrentState.SpoofQuarantineTag -eq $Settings.SpoofQuarantineTag) -and - ($CurrentState.MailboxIntelligenceProtectionAction -eq $Settings.MailboxIntelligenceProtectionAction) -and - ($CurrentState.MailboxIntelligenceQuarantineTag -eq $Settings.MailboxIntelligenceQuarantineTag) -and - ($CurrentState.TargetedUserProtectionAction -eq $Settings.TargetedUserProtectionAction) -and - ($CurrentState.TargetedUserQuarantineTag -eq $Settings.TargetedUserQuarantineTag) -and - ($CurrentState.TargetedDomainProtectionAction -eq $Settings.TargetedDomainProtectionAction) -and - ($CurrentState.TargetedDomainQuarantineTag -eq $Settings.TargetedDomainQuarantineTag) -and - ($CurrentState.EnableOrganizationDomainsProtection -eq $true) + if ($MDOLicensed) { + $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and + ($CurrentState.Enabled -eq $true) -and + ($CurrentState.PhishThresholdLevel -eq $Settings.PhishThresholdLevel) -and + ($CurrentState.EnableMailboxIntelligence -eq $true) -and + ($CurrentState.EnableMailboxIntelligenceProtection -eq $true) -and + ($CurrentState.EnableSpoofIntelligence -eq $true) -and + ($CurrentState.EnableFirstContactSafetyTips -eq $Settings.EnableFirstContactSafetyTips) -and + ($CurrentState.EnableSimilarUsersSafetyTips -eq $Settings.EnableSimilarUsersSafetyTips) -and + ($CurrentState.EnableSimilarDomainsSafetyTips -eq $Settings.EnableSimilarDomainsSafetyTips) -and + ($CurrentState.EnableUnusualCharactersSafetyTips -eq $Settings.EnableUnusualCharactersSafetyTips) -and + ($CurrentState.EnableUnauthenticatedSender -eq $true) -and + ($CurrentState.EnableViaTag -eq $true) -and + ($CurrentState.AuthenticationFailAction -eq $Settings.AuthenticationFailAction) -and + ($CurrentState.SpoofQuarantineTag -eq $Settings.SpoofQuarantineTag) -and + ($CurrentState.MailboxIntelligenceProtectionAction -eq $Settings.MailboxIntelligenceProtectionAction) -and + ($CurrentState.MailboxIntelligenceQuarantineTag -eq $Settings.MailboxIntelligenceQuarantineTag) -and + ($CurrentState.TargetedUserProtectionAction -eq $Settings.TargetedUserProtectionAction) -and + ($CurrentState.TargetedUserQuarantineTag -eq $Settings.TargetedUserQuarantineTag) -and + ($CurrentState.TargetedDomainProtectionAction -eq $Settings.TargetedDomainProtectionAction) -and + ($CurrentState.TargetedDomainQuarantineTag -eq $Settings.TargetedDomainQuarantineTag) -and + ($CurrentState.EnableOrganizationDomainsProtection -eq $true) + } else { + $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and + ($CurrentState.Enabled -eq $true) -and + ($CurrentState.EnableSpoofIntelligence -eq $true) -and + ($CurrentState.EnableFirstContactSafetyTips -eq $Settings.EnableFirstContactSafetyTips) -and + ($CurrentState.EnableUnauthenticatedSender -eq $true) -and + ($CurrentState.EnableViaTag -eq $true) -and + ($CurrentState.AuthenticationFailAction -eq $Settings.AuthenticationFailAction) -and + ($CurrentState.SpoofQuarantineTag -eq $Settings.SpoofQuarantineTag) + } $AcceptedDomains = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AcceptedDomain' @@ -106,27 +122,39 @@ function Invoke-CIPPStandardAntiPhishPolicy { if ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Anti-phishing policy already correctly configured' -sev Info } else { - $cmdparams = @{ - Enabled = $true - PhishThresholdLevel = $Settings.PhishThresholdLevel - EnableMailboxIntelligence = $true - EnableMailboxIntelligenceProtection = $true - EnableSpoofIntelligence = $true - EnableFirstContactSafetyTips = $Settings.EnableFirstContactSafetyTips - EnableSimilarUsersSafetyTips = $Settings.EnableSimilarUsersSafetyTips - EnableSimilarDomainsSafetyTips = $Settings.EnableSimilarDomainsSafetyTips - EnableUnusualCharactersSafetyTips = $Settings.EnableUnusualCharactersSafetyTips - EnableUnauthenticatedSender = $true - EnableViaTag = $true - AuthenticationFailAction = $Settings.AuthenticationFailAction - SpoofQuarantineTag = $Settings.SpoofQuarantineTag - MailboxIntelligenceProtectionAction = $Settings.MailboxIntelligenceProtectionAction - MailboxIntelligenceQuarantineTag = $Settings.MailboxIntelligenceQuarantineTag - TargetedUserProtectionAction = $Settings.TargetedUserProtectionAction - TargetedUserQuarantineTag = $Settings.TargetedUserQuarantineTag - TargetedDomainProtectionAction = $Settings.TargetedDomainProtectionAction - TargetedDomainQuarantineTag = $Settings.TargetedDomainQuarantineTag - EnableOrganizationDomainsProtection = $true + if ($MDOLicensed) { + $cmdparams = @{ + Enabled = $true + PhishThresholdLevel = $Settings.PhishThresholdLevel + EnableMailboxIntelligence = $true + EnableMailboxIntelligenceProtection = $true + EnableSpoofIntelligence = $true + EnableFirstContactSafetyTips = $Settings.EnableFirstContactSafetyTips + EnableSimilarUsersSafetyTips = $Settings.EnableSimilarUsersSafetyTips + EnableSimilarDomainsSafetyTips = $Settings.EnableSimilarDomainsSafetyTips + EnableUnusualCharactersSafetyTips = $Settings.EnableUnusualCharactersSafetyTips + EnableUnauthenticatedSender = $true + EnableViaTag = $true + AuthenticationFailAction = $Settings.AuthenticationFailAction + SpoofQuarantineTag = $Settings.SpoofQuarantineTag + MailboxIntelligenceProtectionAction = $Settings.MailboxIntelligenceProtectionAction + MailboxIntelligenceQuarantineTag = $Settings.MailboxIntelligenceQuarantineTag + TargetedUserProtectionAction = $Settings.TargetedUserProtectionAction + TargetedUserQuarantineTag = $Settings.TargetedUserQuarantineTag + TargetedDomainProtectionAction = $Settings.TargetedDomainProtectionAction + TargetedDomainQuarantineTag = $Settings.TargetedDomainQuarantineTag + EnableOrganizationDomainsProtection = $true + } + } else { + $cmdparams = @{ + Enabled = $true + EnableSpoofIntelligence = $true + EnableFirstContactSafetyTips = $Settings.EnableFirstContactSafetyTips + EnableUnauthenticatedSender = $true + EnableViaTag = $true + AuthenticationFailAction = $Settings.AuthenticationFailAction + SpoofQuarantineTag = $Settings.SpoofQuarantineTag + } } if ($CurrentState.Name -eq $PolicyName) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 index 3142d6bee41c..5ac64c7887c6 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 @@ -38,116 +38,133 @@ function Invoke-CIPPStandardSafeAttachmentPolicy { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'SafeAttachmentPolicy' - $PolicyList = @('CIPP Default Safe Attachment Policy','Default Safe Attachment Policy') - $ExistingPolicy = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeAttachmentPolicy' | Where-Object -Property Name -In $PolicyList - if ($null -eq $ExistingPolicy.Name) { - $PolicyName = $PolicyList[0] - } else { - $PolicyName = $ExistingPolicy.Name - } - $RuleList = @( 'CIPP Default Safe Attachment Rule','CIPP Default Safe Attachment Policy') - $ExistingRule = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeAttachmentRule' | Where-Object -Property Name -In $RuleList - if ($null -eq $ExistingRule.Name) { - $RuleName = $RuleList[0] - } else { - $RuleName = $ExistingRule.Name - } + $ServicePlans = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus?$select=servicePlans' -tenantid $Tenant + $ServicePlans = $ServicePlans.servicePlans.servicePlanName + $MDOLicensed = $ServicePlans -contains "ATP_ENTERPRISE" + + if ($MDOLicensed) { + $PolicyList = @('CIPP Default Safe Attachment Policy','Default Safe Attachment Policy') + $ExistingPolicy = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeAttachmentPolicy' | Where-Object -Property Name -In $PolicyList + if ($null -eq $ExistingPolicy.Name) { + $PolicyName = $PolicyList[0] + } else { + $PolicyName = $ExistingPolicy.Name + } + $RuleList = @( 'CIPP Default Safe Attachment Rule','CIPP Default Safe Attachment Policy') + $ExistingRule = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeAttachmentRule' | Where-Object -Property Name -In $RuleList + if ($null -eq $ExistingRule.Name) { + $RuleName = $RuleList[0] + } else { + $RuleName = $ExistingRule.Name + } - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeAttachmentPolicy' | - Where-Object -Property Name -EQ $PolicyName | - Select-Object Name, Enable, Action, QuarantineTag, Redirect, RedirectAddress + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeAttachmentPolicy' | + Where-Object -Property Name -EQ $PolicyName | + Select-Object Name, Enable, Action, QuarantineTag, Redirect, RedirectAddress - $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and - ($CurrentState.Enable -eq $true) -and - ($CurrentState.Action -eq $Settings.Action) -and - ($CurrentState.QuarantineTag -eq $Settings.QuarantineTag) -and - ($CurrentState.Redirect -eq $Settings.Redirect) -and - (($null -eq $Settings.RedirectAddress) -or ($CurrentState.RedirectAddress -eq $Settings.RedirectAddress)) + $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and + ($CurrentState.Enable -eq $true) -and + ($CurrentState.Action -eq $Settings.Action) -and + ($CurrentState.QuarantineTag -eq $Settings.QuarantineTag) -and + ($CurrentState.Redirect -eq $Settings.Redirect) -and + (($null -eq $Settings.RedirectAddress) -or ($CurrentState.RedirectAddress -eq $Settings.RedirectAddress)) - $AcceptedDomains = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AcceptedDomain' + $AcceptedDomains = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AcceptedDomain' - $RuleState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeAttachmentRule' | - Where-Object -Property Name -EQ $RuleName | - Select-Object Name, SafeAttachmentPolicy, Priority, RecipientDomainIs + $RuleState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeAttachmentRule' | + Where-Object -Property Name -EQ $RuleName | + Select-Object Name, SafeAttachmentPolicy, Priority, RecipientDomainIs - $RuleStateIsCorrect = ($RuleState.Name -eq $RuleName) -and - ($RuleState.SafeAttachmentPolicy -eq $PolicyName) -and - ($RuleState.Priority -eq 0) -and - (!(Compare-Object -ReferenceObject $RuleState.RecipientDomainIs -DifferenceObject $AcceptedDomains.Name)) + $RuleStateIsCorrect = ($RuleState.Name -eq $RuleName) -and + ($RuleState.SafeAttachmentPolicy -eq $PolicyName) -and + ($RuleState.Priority -eq 0) -and + (!(Compare-Object -ReferenceObject $RuleState.RecipientDomainIs -DifferenceObject $AcceptedDomains.Name)) - if ($Settings.remediate -eq $true) { + if ($Settings.remediate -eq $true) { - if ($StateIsCorrect -eq $true) { - Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Safe Attachment Policy already correctly configured' -sev Info - } else { - $cmdparams = @{ - Enable = $true - Action = $Settings.Action - QuarantineTag = $Settings.QuarantineTag - Redirect = $Settings.Redirect - RedirectAddress = $Settings.RedirectAddress + if ($StateIsCorrect -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Safe Attachment Policy already correctly configured' -sev Info + } else { + $cmdparams = @{ + Enable = $true + Action = $Settings.Action + QuarantineTag = $Settings.QuarantineTag + Redirect = $Settings.Redirect + RedirectAddress = $Settings.RedirectAddress + } + + if ($CurrentState.Name -eq $PolicyName) { + try { + $cmdparams.Add('Identity', $PolicyName) + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-SafeAttachmentPolicy' -cmdparams $cmdparams -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Updated Safe Attachment policy $PolicyName." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to update Safe Attachment policy $PolicyName." -sev Error -LogData $_ + } + } else { + try { + $cmdparams.Add('Name', $PolicyName) + New-ExoRequest -tenantid $Tenant -cmdlet 'New-SafeAttachmentPolicy' -cmdparams $cmdparams -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Created Safe Attachment policy $PolicyName." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create Safe Attachment policy $PolicyName." -sev Error -LogData $_ + } + } } - if ($CurrentState.Name -eq $PolicyName) { - try { - $cmdparams.Add('Identity', $PolicyName) - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-SafeAttachmentPolicy' -cmdparams $cmdparams -UseSystemMailbox $true - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Updated Safe Attachment policy $PolicyName." -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to update Safe Attachment policy $PolicyName." -sev Error -LogData $_ + if ($RuleStateIsCorrect -eq $false) { + $cmdparams = @{ + Priority = 0 + RecipientDomainIs = $AcceptedDomains.Name } - } else { - try { - $cmdparams.Add('Name', $PolicyName) - New-ExoRequest -tenantid $Tenant -cmdlet 'New-SafeAttachmentPolicy' -cmdparams $cmdparams -UseSystemMailbox $true - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Created Safe Attachment policy $PolicyName." -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create Safe Attachment policy $PolicyName." -sev Error -LogData $_ + + if ($RuleState.SafeAttachmentPolicy -ne $PolicyName) { + $cmdparams.Add('SafeAttachmentPolicy', $PolicyName) } - } - } - if ($RuleStateIsCorrect -eq $false) { - $cmdparams = @{ - Priority = 0 - RecipientDomainIs = $AcceptedDomains.Name + if ($RuleState.Name -eq $RuleName) { + try { + $cmdparams.Add('Identity', $RuleName) + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-SafeAttachmentRule' -cmdparams $cmdparams -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Updated Safe Attachment rule $RuleName." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to update Safe Attachment rule $RuleName." -sev Error -LogData $_ + } + } else { + try { + $cmdparams.Add('Name', $RuleName) + New-ExoRequest -tenantid $Tenant -cmdlet 'New-SafeAttachmentRule' -cmdparams $cmdparams -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Created Safe Attachment rule $RuleName." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create Safe Attachment rule $RuleName." -sev Error -LogData $_ + } + } } + } - if ($RuleState.SafeAttachmentPolicy -ne $PolicyName) { - $cmdparams.Add('SafeAttachmentPolicy', $PolicyName) - } + if ($Settings.alert -eq $true) { - if ($RuleState.Name -eq $RuleName) { - try { - $cmdparams.Add('Identity', $RuleName) - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-SafeAttachmentRule' -cmdparams $cmdparams -UseSystemMailbox $true - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Updated Safe Attachment rule $RuleName." -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to update Safe Attachment rule $RuleName." -sev Error -LogData $_ - } + if ($StateIsCorrect -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Safe Attachment Policy is enabled' -sev Info } else { - try { - $cmdparams.Add('Name', $RuleName) - New-ExoRequest -tenantid $Tenant -cmdlet 'New-SafeAttachmentRule' -cmdparams $cmdparams -UseSystemMailbox $true - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Created Safe Attachment rule $RuleName." -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create Safe Attachment rule $RuleName." -sev Error -LogData $_ - } + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Safe Attachment Policy is not enabled' -sev Alert } } - } - if ($Settings.alert -eq $true) { + if ($Settings.report -eq $true) { + Add-CIPPBPAField -FieldName 'SafeAttachmentPolicy' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $tenant + } + } else { + if ($Settings.remediate -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create Safe Attachment policy: Tenant does not have Microsoft Defender for Office 365 license" -sev Error + } - if ($StateIsCorrect -eq $true) { - Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Safe Attachment Policy is enabled' -sev Info - } else { - Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Safe Attachment Policy is not enabled' -sev Alert + if ($Settings.alert -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'Safe Attachment Policy is not enabled: Tenant does not have Microsoft Defender for Office 365 license' -sev Alert } - } - if ($Settings.report -eq $true) { - Add-CIPPBPAField -FieldName 'SafeAttachmentPolicy' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $tenant + if ($Settings.report -eq $true) { + Add-CIPPBPAField -FieldName 'SafeAttachmentPolicy' -FieldValue $false -StoreAs bool -Tenant $tenant + } } - } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 index e8639f10ce49..d692297f26b6 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 @@ -36,126 +36,143 @@ function Invoke-CIPPStandardSafeLinksPolicy { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'SafeLinksPolicy' - $PolicyList = @('CIPP Default SafeLinks Policy','Default SafeLinks Policy') - $ExistingPolicy = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeLinksPolicy' | Where-Object -Property Name -In $PolicyList - if ($null -eq $ExistingPolicy.Name) { - $PolicyName = $PolicyList[0] - } else { - $PolicyName = $ExistingPolicy.Name - } - $RuleList = @( 'CIPP Default SafeLinks Rule','CIPP Default SafeLinks Policy') - $ExistingRule = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeLinksRule' | Where-Object -Property Name -In $RuleList - if ($null -eq $ExistingRule.Name) { - $RuleName = $RuleList[0] - } else { - $RuleName = $ExistingRule.Name - } - - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeLinksPolicy' | - Where-Object -Property Name -EQ $PolicyName | - Select-Object Name, EnableSafeLinksForEmail, EnableSafeLinksForTeams, EnableSafeLinksForOffice, TrackClicks, AllowClickThrough, ScanUrls, EnableForInternalSenders, DeliverMessageAfterScan, DisableUrlRewrite, EnableOrganizationBranding - - $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and - ($CurrentState.EnableSafeLinksForEmail -eq $true) -and - ($CurrentState.EnableSafeLinksForTeams -eq $true) -and - ($CurrentState.EnableSafeLinksForOffice -eq $true) -and - ($CurrentState.TrackClicks -eq $true) -and - ($CurrentState.ScanUrls -eq $true) -and - ($CurrentState.EnableForInternalSenders -eq $true) -and - ($CurrentState.DeliverMessageAfterScan -eq $true) -and - ($CurrentState.AllowClickThrough -eq $Settings.AllowClickThrough) -and - ($CurrentState.DisableUrlRewrite -eq $Settings.DisableUrlRewrite) -and - ($CurrentState.EnableOrganizationBranding -eq $Settings.EnableOrganizationBranding) - - $AcceptedDomains = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AcceptedDomain' - - $RuleState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeLinksRule' | - Where-Object -Property Name -EQ $RuleName | - Select-Object Name, SafeLinksPolicy, Priority, RecipientDomainIs - - $RuleStateIsCorrect = ($RuleState.Name -eq $RuleName) -and - ($RuleState.SafeLinksPolicy -eq $PolicyName) -and - ($RuleState.Priority -eq 0) -and - (!(Compare-Object -ReferenceObject $RuleState.RecipientDomainIs -DifferenceObject $AcceptedDomains.Name)) - - if ($Settings.remediate -eq $true) { - - if ($StateIsCorrect -eq $true) { - Write-LogMessage -API 'Standards' -tenant $Tenant -message 'SafeLink Policy already correctly configured' -sev Info + $ServicePlans = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus?$select=servicePlans' -tenantid $Tenant + $ServicePlans = $ServicePlans.servicePlans.servicePlanName + $MDOLicensed = $ServicePlans -contains "ATP_ENTERPRISE" + + if ($MDOLicensed) { + $PolicyList = @('CIPP Default SafeLinks Policy','Default SafeLinks Policy') + $ExistingPolicy = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeLinksPolicy' | Where-Object -Property Name -In $PolicyList + if ($null -eq $ExistingPolicy.Name) { + $PolicyName = $PolicyList[0] } else { - $cmdparams = @{ - EnableSafeLinksForEmail = $true - EnableSafeLinksForTeams = $true - EnableSafeLinksForOffice = $true - TrackClicks = $true - ScanUrls = $true - EnableForInternalSenders = $true - DeliverMessageAfterScan = $true - AllowClickThrough = $Settings.AllowClickThrough - DisableUrlRewrite = $Settings.DisableUrlRewrite - EnableOrganizationBranding = $Settings.EnableOrganizationBranding - } + $PolicyName = $ExistingPolicy.Name + } + $RuleList = @( 'CIPP Default SafeLinks Rule','CIPP Default SafeLinks Policy') + $ExistingRule = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeLinksRule' | Where-Object -Property Name -In $RuleList + if ($null -eq $ExistingRule.Name) { + $RuleName = $RuleList[0] + } else { + $RuleName = $ExistingRule.Name + } - if ($CurrentState.Name -eq $Policyname) { - try { - $cmdparams.Add('Identity', $PolicyName) - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-SafeLinksPolicy' -cmdparams $cmdparams -UseSystemMailbox $true - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Updated SafeLink policy $PolicyName." -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to update SafeLink policy $PolicyName." -sev Error -LogData $_ - } + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeLinksPolicy' | + Where-Object -Property Name -EQ $PolicyName | + Select-Object Name, EnableSafeLinksForEmail, EnableSafeLinksForTeams, EnableSafeLinksForOffice, TrackClicks, AllowClickThrough, ScanUrls, EnableForInternalSenders, DeliverMessageAfterScan, DisableUrlRewrite, EnableOrganizationBranding + + $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and + ($CurrentState.EnableSafeLinksForEmail -eq $true) -and + ($CurrentState.EnableSafeLinksForTeams -eq $true) -and + ($CurrentState.EnableSafeLinksForOffice -eq $true) -and + ($CurrentState.TrackClicks -eq $true) -and + ($CurrentState.ScanUrls -eq $true) -and + ($CurrentState.EnableForInternalSenders -eq $true) -and + ($CurrentState.DeliverMessageAfterScan -eq $true) -and + ($CurrentState.AllowClickThrough -eq $Settings.AllowClickThrough) -and + ($CurrentState.DisableUrlRewrite -eq $Settings.DisableUrlRewrite) -and + ($CurrentState.EnableOrganizationBranding -eq $Settings.EnableOrganizationBranding) + + $AcceptedDomains = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AcceptedDomain' + + $RuleState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeLinksRule' | + Where-Object -Property Name -EQ $RuleName | + Select-Object Name, SafeLinksPolicy, Priority, RecipientDomainIs + + $RuleStateIsCorrect = ($RuleState.Name -eq $RuleName) -and + ($RuleState.SafeLinksPolicy -eq $PolicyName) -and + ($RuleState.Priority -eq 0) -and + (!(Compare-Object -ReferenceObject $RuleState.RecipientDomainIs -DifferenceObject $AcceptedDomains.Name)) + + if ($Settings.remediate -eq $true) { + + if ($StateIsCorrect -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'SafeLink Policy already correctly configured' -sev Info } else { - try { - $cmdparams.Add('Name', $PolicyName) - New-ExoRequest -tenantid $Tenant -cmdlet 'New-SafeLinksPolicy' -cmdparams $cmdparams -UseSystemMailbox $true - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Created SafeLink policy $PolicyName." -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create SafeLink policy $PolicyName." -sev Error -LogData $_ + $cmdparams = @{ + EnableSafeLinksForEmail = $true + EnableSafeLinksForTeams = $true + EnableSafeLinksForOffice = $true + TrackClicks = $true + ScanUrls = $true + EnableForInternalSenders = $true + DeliverMessageAfterScan = $true + AllowClickThrough = $Settings.AllowClickThrough + DisableUrlRewrite = $Settings.DisableUrlRewrite + EnableOrganizationBranding = $Settings.EnableOrganizationBranding } - } - } - if ($RuleStateIsCorrect -eq $false) { - $cmdparams = @{ - Priority = 0 - RecipientDomainIs = $AcceptedDomains.Name + if ($CurrentState.Name -eq $Policyname) { + try { + $cmdparams.Add('Identity', $PolicyName) + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-SafeLinksPolicy' -cmdparams $cmdparams -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Updated SafeLink policy $PolicyName." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to update SafeLink policy $PolicyName." -sev Error -LogData $_ + } + } else { + try { + $cmdparams.Add('Name', $PolicyName) + New-ExoRequest -tenantid $Tenant -cmdlet 'New-SafeLinksPolicy' -cmdparams $cmdparams -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Created SafeLink policy $PolicyName." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create SafeLink policy $PolicyName." -sev Error -LogData $_ + } + } } - if ($RuleState.SafeLinksPolicy -ne $PolicyName) { - $cmdparams.Add('SafeLinksPolicy', $PolicyName) - } + if ($RuleStateIsCorrect -eq $false) { + $cmdparams = @{ + Priority = 0 + RecipientDomainIs = $AcceptedDomains.Name + } - if ($RuleState.Name -eq $RuleName) { - try { - $cmdparams.Add('Identity', $RuleName) - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-SafeLinksRule' -cmdparams $cmdparams -UseSystemMailbox $true - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Updated SafeLink rule $RuleName." -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to update SafeLink rule $RuleName." -sev Error -LogData $_ + if ($RuleState.SafeLinksPolicy -ne $PolicyName) { + $cmdparams.Add('SafeLinksPolicy', $PolicyName) } - } else { - try { - $cmdparams.Add('Name', $RuleName) - New-ExoRequest -tenantid $Tenant -cmdlet 'New-SafeLinksRule' -cmdparams $cmdparams -UseSystemMailbox $true - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Created SafeLink rule $RuleName." -sev Info - } catch { - Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create SafeLink rule $RuleName." -sev Error -LogData $_ + + if ($RuleState.Name -eq $RuleName) { + try { + $cmdparams.Add('Identity', $RuleName) + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-SafeLinksRule' -cmdparams $cmdparams -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Updated SafeLink rule $RuleName." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to update SafeLink rule $RuleName." -sev Error -LogData $_ + } + } else { + try { + $cmdparams.Add('Name', $RuleName) + New-ExoRequest -tenantid $Tenant -cmdlet 'New-SafeLinksRule' -cmdparams $cmdparams -UseSystemMailbox $true + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Created SafeLink rule $RuleName." -sev Info + } catch { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create SafeLink rule $RuleName." -sev Error -LogData $_ + } } } } - } - if ($Settings.alert -eq $true) { + if ($Settings.alert -eq $true) { - if ($StateIsCorrect -eq $true) { - Write-LogMessage -API 'Standards' -tenant $Tenant -message 'SafeLink Policy is enabled' -sev Info - } else { - Write-LogMessage -API 'Standards' -tenant $Tenant -message 'SafeLink Policy is not enabled' -sev Alert + if ($StateIsCorrect -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'SafeLink Policy is enabled' -sev Info + } else { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'SafeLink Policy is not enabled' -sev Alert + } } - } - if ($Settings.report -eq $true) { - Add-CIPPBPAField -FieldName 'SafeLinksPolicy' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $tenant - } + if ($Settings.report -eq $true) { + Add-CIPPBPAField -FieldName 'SafeLinksPolicy' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $tenant + } + } else { + if ($Settings.remediate -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message "Failed to create SafeLink policy: Tenant does not have Microsoft Defender for Office 365 license" -sev Error + } + if ($Settings.alert -eq $true) { + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'SafeLink Policy is not enabled: Tenant does not have Microsoft Defender for Office 365 license' -sev Alert + } + + if ($Settings.report -eq $true) { + Add-CIPPBPAField -FieldName 'SafeLinksPolicy' -FieldValue $false -StoreAs bool -Tenant $tenant + } + } } From 622fc04f38a3d5c5435b9ce772b32d592e57bf2d Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 19 Nov 2024 12:58:51 -0500 Subject: [PATCH 12/22] memory usage tracking --- .../Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 index b5cb9286edc5..eee642d90665 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 @@ -32,7 +32,8 @@ function Push-AuditLogTenant { # Get webhook rules $ConfigEntries = Get-CIPPAzDataTableEntity @ConfigTable $LogSearchesTable = Get-CippTable -TableName 'AuditLogSearches' - + Write-Information ("Audit: Memory usage before processing $([System.GC]::GetTotalMemory($false))") + $SearchCount = 0 $Configuration = $ConfigEntries | Where-Object { ($_.Tenants -match $TenantFilter -or $_.Tenants -match 'AllTenants') } if ($Configuration) { try { @@ -88,6 +89,8 @@ function Push-AuditLogTenant { } } } + $SearchCount++ + Write-Information "Audit: Memory usage after processing $SearchCount searches: $([System.GC]::GetTotalMemory($false))" } } catch { Write-Information ( 'Audit Log search: Error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) @@ -95,5 +98,8 @@ function Push-AuditLogTenant { } } catch { Write-Information ( 'Push-AuditLogTenant: Error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) + } finally { + Write-Information "Audit Logs: Completed processing $($TenantFilter)" + Write-Information "Audit Logs: Memory usage after processing $([System.GC]::GetTotalMemory($false))" } } From 49df7d07d6d140ad19e111606cdda98afdefebb1 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 19 Nov 2024 17:49:50 -0500 Subject: [PATCH 13/22] update 'true' to $true --- .../Public/Invoke-CIPPOffboardingJob.ps1 | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 index 2de463c0c811..a1732cbf6fd9 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 @@ -15,23 +15,23 @@ function Invoke-CIPPOffboardingJob { $userid = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($username)?`$select=id" -tenantid $Tenantfilter).id Write-Host "Running offboarding job for $username with options: $($Options | ConvertTo-Json -Depth 10)" $Return = switch ($Options) { - { $_.'ConvertToShared' -eq 'true' } { + { $_.'ConvertToShared' -eq $true } { Set-CIPPMailboxType -ExecutingUser $ExecutingUser -tenantFilter $tenantFilter -userid $username -username $username -MailboxType 'Shared' -APIName $APIName } - { $_.RevokeSessions -eq 'true' } { + { $_.RevokeSessions -eq $true } { Revoke-CIPPSessions -tenantFilter $tenantFilter -username $username -userid $userid -ExecutingUser $ExecutingUser -APIName $APIName } - { $_.ResetPass -eq 'true' } { + { $_.ResetPass -eq $true } { Set-CIPPResetPassword -tenantFilter $tenantFilter -userid $username -ExecutingUser $ExecutingUser -APIName $APIName } - { $_.RemoveGroups -eq 'true' } { + { $_.RemoveGroups -eq $true } { Remove-CIPPGroups -userid $userid -tenantFilter $Tenantfilter -ExecutingUser $ExecutingUser -APIName $APIName -Username "$Username" } - { $_.'HideFromGAL' -eq 'true' } { + { $_.'HideFromGAL' -eq $true } { Set-CIPPHideFromGAL -tenantFilter $tenantFilter -userid $username -HideFromGAL $true -ExecutingUser $ExecutingUser -APIName $APIName } - { $_.'DisableSignIn' -eq 'true' } { + { $_.'DisableSignIn' -eq $true } { Set-CIPPSignInState -TenantFilter $tenantFilter -userid $username -AccountEnabled $false -ExecutingUser $ExecutingUser -APIName $APIName } @@ -57,23 +57,23 @@ function Invoke-CIPPOffboardingJob { Set-CIPPForwarding -userid $userid -username $username -tenantFilter $Tenantfilter -Forward $Options.forward -KeepCopy $KeepCopy -ExecutingUser $ExecutingUser -APIName $APIName } } - { $_.'RemoveLicenses' -eq 'true' } { + { $_.'RemoveLicenses' -eq $true } { Remove-CIPPLicense -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $ExecutingUser -APIName $APIName -Schedule } - { $_.'Deleteuser' -eq 'true' } { + { $_.'deleteuser' -eq $true } { Remove-CIPPUser -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $ExecutingUser -APIName $APIName } - { $_.'removeRules' -eq 'true' } { + { $_.'removeRules' -eq $true } { Write-Host "Removing rules for $username" Remove-CIPPMailboxRule -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $ExecutingUser -APIName $APIName -RemoveAllRules } - { $_.'removeMobile' -eq 'true' } { + { $_.'removeMobile' -eq $true } { Remove-CIPPMobileDevice -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $ExecutingUser -APIName $APIName } - { $_.'removeCalendarInvites' -eq 'true' } { + { $_.'removeCalendarInvites' -eq $true } { Remove-CIPPCalendarInvites -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $ExecutingUser -APIName $APIName } { $_.'removePermissions' } { From 7e8eac1c0b71683b0eb5910a2ab727c870a4388a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 20 Nov 2024 13:50:39 -0500 Subject: [PATCH 14/22] check for tenant filter parameter also look for nested Parameters and convert to hashtable --- .../Start-UserTasksOrchestrator.ps1 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1 index 10aebb5c0767..f97f7478bdf1 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1 @@ -23,6 +23,9 @@ function Start-UserTasksOrchestrator { TaskState = 'Running' } $task.Parameters = $task.Parameters | ConvertFrom-Json -AsHashtable + if ($task.Parameters.Parameters) { + $task.Parameters.Parameters = $task.Parameters.Parameters | ConvertFrom-Json -AsHashtable + } $task.AdditionalProperties = $task.AdditionalProperties | ConvertFrom-Json if (!$task.Parameters) { $task.Parameters = @{} } @@ -36,7 +39,9 @@ function Start-UserTasksOrchestrator { if ($task.Tenant -eq 'AllTenants') { $AllTenantCommands = foreach ($Tenant in $TenantList) { $NewParams = $task.Parameters.Clone() - $NewParams.TenantFilter = $Tenant.defaultDomainName + if ((Get-Command $task.Command).Parameters.TenantFilter) { + $NewParams.TenantFilter = $Tenant.defaultDomainName + } [pscustomobject]@{ Command = $task.Command Parameters = $NewParams @@ -46,7 +51,9 @@ function Start-UserTasksOrchestrator { } $Batch.AddRange($AllTenantCommands) } else { - $ScheduledCommand.Parameters['TenantFilter'] = $task.Tenant + if ((Get-Command $task.Command).Parameters.TenantFilter) { + $ScheduledCommand.Parameters['TenantFilter'] = $task.Tenant + } $Batch.Add($ScheduledCommand) } } catch { From 700436fd665eec0d9309a1188686affb961fb03a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 21 Nov 2024 13:23:26 -0500 Subject: [PATCH 15/22] Audit log tweaks --- .../AuditLogs/Get-CippAuditLogSearchResults.ps1 | 15 +++++++++++++-- .../Public/Webhooks/Test-CIPPAuditLogRules.ps1 | 10 ++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 b/Modules/CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 index d2e9ab074bb9..ec0f8fd5b5bd 100644 --- a/Modules/CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 +++ b/Modules/CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 @@ -14,10 +14,21 @@ function Get-CippAuditLogSearchResults { [string]$TenantFilter, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('id')] - [string]$QueryId + [string]$QueryId, + [switch]$CountOnly ) process { - New-GraphGetRequest -uri ('https://graph.microsoft.com/beta/security/auditLog/queries/{0}/records?$top=999' -f $QueryId) -AsApp $true -tenantid $TenantFilter -ErrorAction Stop + $GraphRequest = @{ + Uri = ('https://graph.microsoft.com/beta/security/auditLog/queries/{0}/records?$top=999&$count=true' -f $QueryId) + Method = 'GET' + AsApp = $true + tenantid = $TenantFilter + } + if ($CountOnly.IsPresent) { + $GraphRequest.CountOnly = $true + } + + New-GraphGetRequest @GraphRequest -ErrorAction Stop } } diff --git a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 index 94d2a83278bd..963b25b66e1c 100644 --- a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 +++ b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 @@ -37,16 +37,18 @@ function Test-CIPPAuditLogRules { } #write-warning 'Getting audit records from Graph API' try { + $LogCount = Get-CippAuditLogSearchResults -TenantFilter $TenantFilter -QueryId $SearchId -CountOnly + $RunGuid = (New-Guid).Guid + Write-Warning "Logs to process: $LogCount - SearchId: $SearchId - RunGuid: $($RunGuid) - $($TenantFilter)" + $Results.TotalLogs = $LogCount + Write-Information "RunGuid: $RunGud - Collecting logs" $SearchResults = Get-CippAuditLogSearchResults -TenantFilter $TenantFilter -QueryId $SearchId } catch { Write-Warning "Error getting audit logs: $($_.Exception.Message)" Write-LogMessage -API 'Webhooks' -message "Error getting audit logs for search $($SearchId)" -LogData (Get-CippException -Exception $_) -sev Error -tenant $TenantFilter throw $_ } - $LogCount = ($SearchResults | Measure-Object).Count - $RunGuid = New-Guid - Write-Warning "Logs to process: $LogCount - RunGuid: $($RunGuid) - $($TenantFilter)" - $Results.TotalLogs = $LogCount + if ($LogCount -gt 0) { $LocationTable = Get-CIPPTable -TableName 'knownlocationdb' $ProcessedData = foreach ($AuditRecord in $SearchResults) { From 711911baea20f67807f4fb2080b54b4dc7118db4 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 21 Nov 2024 16:22:36 -0500 Subject: [PATCH 16/22] better handling of no searches/rules --- .../Start-AuditLogOrchestrator.ps1 | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 index 0f56ae4a7e99..25deaf238213 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 @@ -7,20 +7,28 @@ function Start-AuditLogOrchestrator { param() try { $AuditLogSearchesTable = Get-CIPPTable -TableName 'AuditLogSearches' - $AuditLogSearches = Get-CIPPAzDataTableEntity @AuditLogSearchesTable -Filter "CippStatus eq 'Pending'" + $15MinutesAgo = (Get-Date).AddMinutes(-15).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + $1DayAgo = (Get-Date).AddDays(-1).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + $AuditLogSearches = Get-CIPPAzDataTableEntity @AuditLogSearchesTable -Filter "(CippStatus eq 'Pending' or (CippStatus eq 'Processing' and Timestamp le datetime'$15MinutesAgo')) and Timestamp ge datetime'$1DayAgo'" -Property PartitionKey, RowKey, Tenant, CippStatus, Timestamp + + $WebhookRulesTable = Get-CIPPTable -TableName 'WebhookRules' + $WebhookRules = Get-CIPPAzDataTableEntity @WebhookRulesTable if (($AuditLogSearches | Measure-Object).Count -eq 0) { Write-Information 'No audit log searches available' + } elseif (($WebhookRules | Measure-Object).Count -eq 0) { + Write-Information 'No webhook rules defined' } else { - $Queue = New-CippQueueEntry -Name 'Audit Log Collection' -Reference 'AuditLogCollection' -TotalTasks ($AuditLogSearches).Count - $Batch = $AuditLogSearches | Sort-Object -Property Tenant -Unique | Select-Object @{Name = 'TenantFilter'; Expression = { $_.Tenant } }, @{Name = 'QueueId'; Expression = { $Queue.RowKey } }, @{Name = 'FunctionName'; Expression = { 'AuditLogTenant' } } - - $InputObject = [PSCustomObject]@{ - OrchestratorName = 'AuditLogs' - Batch = @($Batch) - SkipLog = $true - } + Write-Information "Audit Logs: Processing $($AuditLogSearches.Count) searches" if ($PSCmdlet.ShouldProcess('Start-AuditLogOrchestrator', 'Starting Audit Log Polling')) { + $Queue = New-CippQueueEntry -Name 'Audit Log Collection' -Reference 'AuditLogCollection' -TotalTasks ($AuditLogSearches).Count + $Batch = $AuditLogSearches | Sort-Object -Property Tenant -Unique | Select-Object @{Name = 'TenantFilter'; Expression = { $_.Tenant } }, @{Name = 'QueueId'; Expression = { $Queue.RowKey } }, @{Name = 'FunctionName'; Expression = { 'AuditLogTenant' } } + + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'AuditLogs' + Batch = @($Batch) + SkipLog = $true + } Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) } } From f55b0323a871c25b74378394aa45e8df4cb6b652 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 21 Nov 2024 21:33:52 -0500 Subject: [PATCH 17/22] fix graph request Add conditions for nextLink being used Omit empty query string params --- Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 | 2 +- .../Public/GraphRequests/Get-GraphRequestList.ps1 | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 b/Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 index 30aa79cffaa2..41eba0084cfd 100644 --- a/Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 +++ b/Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 @@ -21,7 +21,7 @@ function Add-CIPPScheduledTask { $Parameters = [System.Collections.Hashtable]@{} foreach ($Key in $task.Parameters.PSObject.Properties.Name) { $Param = $task.Parameters.$Key - if ($Param -is [System.Collections.IDictionary]) { + if ($Param -is [System.Collections.IDictionary] -or $Param.Key) { $ht = @{} foreach ($p in $Param.GetEnumerator()) { $ht[$p.Key] = $p.Value diff --git a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 index d32c1ff87f34..b577d6d6eb27 100644 --- a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 +++ b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 @@ -85,7 +85,12 @@ function Get-GraphRequestList { $GraphQuery = [System.UriBuilder]('https://graph.microsoft.com/{0}/{1}' -f $Version, $Endpoint) $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) foreach ($Item in ($Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) { - $ParamCollection.Add($Item.Key, $Item.Value) + if ($Item.Value -is [System.Boolean]) { + $Item.Value = $Item.Value.ToString().ToLower() + } + if ($Item.Value) { + $ParamCollection.Add($Item.Key, $Item.Value) + } } $GraphQuery.Query = $ParamCollection.ToString() $PartitionKey = Get-StringHash -String (@($Endpoint, $ParamCollection.ToString()) -join '-') @@ -246,6 +251,7 @@ function Get-GraphRequestList { default { try { $QueueThresholdExceeded = $false + if ($Parameters.'$count' -and !$SkipCache -and !$NoPagination) { if ($Count -gt $singleTenantThreshold) { $QueueThresholdExceeded = $true @@ -290,7 +296,7 @@ function Get-GraphRequestList { if (!$QueueThresholdExceeded) { #nextLink should ONLY be used in direct calls with manual pagination. It should not be used in queueing - if ($nextLink) { $GraphRequest.uri = $nextLink } + if ($NoPagination.IsPresent -and $nextLink -match '^https://.+') { $GraphRequest.uri = $nextLink } $GraphRequestResults = New-GraphGetRequest @GraphRequest -Caller 'Get-GraphRequestList' -ErrorAction Stop $GraphRequestResults = $GraphRequestResults | Select-Object *, @{n = 'Tenant'; e = { $TenantFilter } }, @{n = 'CippStatus'; e = { 'Good' } } @@ -313,7 +319,8 @@ function Get-GraphRequestList { } } catch { - throw $_.Exception + $Message = ('Exception at {0}:{1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) + throw $Message } } } From 51fee4f975921b9b3ca4c1cbed976569149ce9ee Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 21 Nov 2024 21:56:40 -0500 Subject: [PATCH 18/22] add force to update-azdatatableentity --- .../Orchestrator Functions/Start-UserTasksOrchestrator.ps1 | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1 index f97f7478bdf1..0578b3739d43 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1 @@ -16,16 +16,13 @@ function Start-UserTasksOrchestrator { $currentUnixTime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds if ($currentUnixTime -ge $task.ScheduledTime) { try { - $null = Update-AzDataTableEntity @Table -Entity @{ + $null = Update-AzDataTableEntity -Force @Table -Entity @{ PartitionKey = $task.PartitionKey RowKey = $task.RowKey ExecutedTime = "$currentUnixTime" TaskState = 'Running' } $task.Parameters = $task.Parameters | ConvertFrom-Json -AsHashtable - if ($task.Parameters.Parameters) { - $task.Parameters.Parameters = $task.Parameters.Parameters | ConvertFrom-Json -AsHashtable - } $task.AdditionalProperties = $task.AdditionalProperties | ConvertFrom-Json if (!$task.Parameters) { $task.Parameters = @{} } @@ -59,7 +56,7 @@ function Start-UserTasksOrchestrator { } catch { $errorMessage = $_.Exception.Message - $null = Update-AzDataTableEntity @Table -Entity @{ + $null = Update-AzDataTableEntity -Force @Table -Entity @{ PartitionKey = $task.PartitionKey RowKey = $task.RowKey Results = "$errorMessage" From ff1a071c159142fd145470716860a97dbac82c1c Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 3 Dec 2024 10:06:23 -0500 Subject: [PATCH 19/22] Update profile.ps1 --- profile.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/profile.ps1 b/profile.ps1 index 173bc71ffb8c..8ca5ffa7c0c5 100644 --- a/profile.ps1 +++ b/profile.ps1 @@ -63,7 +63,7 @@ if (!$LastStartup -or $CurrentVersion -ne $LastStartup.Version) { Version = $CurrentVersion } } - Update-AzDataTableEntity @Table -Entity $LastStartup + Update-AzDataTableEntity @Table -Entity $LastStartup -Force } # Uncomment the next line to enable legacy AzureRm alias in Azure PowerShell. # Enable-AzureRmAlias From 2bde73acaa9b491a54645b646767d65dd88f808e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 3 Dec 2024 10:22:18 -0500 Subject: [PATCH 20/22] Exclude timestamp, etag from backups --- .../HTTP Functions/CIPP/Settings/Invoke-ExecRestoreBackup.ps1 | 2 +- Modules/CIPPCore/Public/New-CIPPBackup.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRestoreBackup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRestoreBackup.ps1 index 00cb9bd38a36..d37d9bbdfe4c 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRestoreBackup.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRestoreBackup.ps1 @@ -13,7 +13,7 @@ Function Invoke-ExecRestoreBackup { $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' try { - foreach ($line in ($Request.body | ConvertFrom-Json | Select-Object * -ExcludeProperty ETag)) { + foreach ($line in ($Request.body | ConvertFrom-Json | Select-Object * -ExcludeProperty ETag, Timestamp)) { Write-Host ($line) $Table = Get-CippTable -tablename $line.table $ht2 = @{} diff --git a/Modules/CIPPCore/Public/New-CIPPBackup.ps1 b/Modules/CIPPCore/Public/New-CIPPBackup.ps1 index d161967509be..7e068a1fde90 100644 --- a/Modules/CIPPCore/Public/New-CIPPBackup.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPBackup.ps1 @@ -24,7 +24,7 @@ function New-CIPPBackup { ) $CSVfile = foreach ($CSVTable in $BackupTables) { $Table = Get-CippTable -tablename $CSVTable - Get-AzDataTableEntity @Table | Select-Object * -ExcludeProperty DomainAnalyser, table | Select-Object *, @{l = 'table'; e = { $CSVTable } } + Get-AzDataTableEntity @Table | Select-Object * -ExcludeProperty DomainAnalyser, table, Timestamp, ETag | Select-Object *, @{l = 'table'; e = { $CSVTable } } } $RowKey = 'CIPPBackup' + '_' + (Get-Date).ToString('yyyy-MM-dd-HHmm') $CSVfile From 248c570a51b960ab8b4cbad1edc6ae194f4308ee Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 3 Dec 2024 10:35:20 -0500 Subject: [PATCH 21/22] standard template fix --- Modules/CIPPCore/Public/Invoke-RemoveStandardTemplate.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Invoke-RemoveStandardTemplate.ps1 b/Modules/CIPPCore/Public/Invoke-RemoveStandardTemplate.ps1 index 029c1c4e4284..b1ceac4e3b49 100644 --- a/Modules/CIPPCore/Public/Invoke-RemoveStandardTemplate.ps1 +++ b/Modules/CIPPCore/Public/Invoke-RemoveStandardTemplate.ps1 @@ -14,7 +14,7 @@ Function Invoke-RemoveStandardTemplate { $User = $request.headers.'x-ms-client-principal' Write-LogMessage -user $User -API $APINAME -message 'Accessed this API' -Sev 'Debug' - $ID = $request.body.ID + $ID = $Request.Body.ID ?? $Request.Query.ID try { $Table = Get-CippTable -tablename 'templates' From 440d435efd3a40a6f6714ab45341ef5f2652986d Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 3 Dec 2024 10:37:15 -0500 Subject: [PATCH 22/22] Update version_latest.txt --- version_latest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_latest.txt b/version_latest.txt index db0785f27378..54358db763fa 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -6.5.3 +6.5.4