From 368d85797406f638264eb1b9626bca4a26c0ed88 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Sat, 19 Jul 2025 20:05:22 +0200 Subject: [PATCH 01/85] add pendinginput to var --- Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 b/Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 index 124624857bf1..64754d6a980e 100644 --- a/Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 @@ -21,10 +21,10 @@ function Get-CIPPTenantCapabilities { } Add-CIPPAzDataTableEntity @ConfigTable -Entity $Entity -Force } - $Plans = $Org.servicePlans | Where-Object { $_.provisioningStatus -eq 'Success' } | Sort-Object -Property serviceplanName -Unique | Select-Object servicePlanName, provisioningStatus + $Plans = $Org.servicePlans | Where-Object { $_.provisioningStatus -eq 'Success' -or $_.provisioningStatus -eq 'PendingInput' } | Sort-Object -Property serviceplanName -Unique | Select-Object servicePlanName, provisioningStatus $Results = @{} foreach ($Plan in $Plans) { - $Results."$($Plan.servicePlanName)" = $Plan.provisioningStatus -eq 'Success' + $Results."$($Plan.servicePlanName)" = $Plan.provisioningStatus -eq 'Success' -or $Plan.provisioningStatus -eq 'PendingInput' } [PSCustomObject]$Results } From 35f7af586f00c03189dd13df8de25c16be4d2a7e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Mon, 21 Jul 2025 17:19:30 +0200 Subject: [PATCH 02/85] Fixed --- .../Public/Standards/Invoke-CIPPStandardExConnector.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExConnector.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExConnector.ps1 index 359f32f4206e..b57860ecf55b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExConnector.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExConnector.ps1 @@ -1,4 +1,4 @@ -function Invoke-CIPPStandardExConnector { +function Invoke-CIPPStandardExchangeConnectorTemplate { <# .FUNCTIONALITY Internal @@ -12,7 +12,7 @@ function Invoke-CIPPStandardExConnector { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'ExConnector' - If ($Settings.remediate -eq $true) { + if ($Settings.remediate -eq $true) { foreach ($Template in $Settings.TemplateList) { try { @@ -20,7 +20,7 @@ function Invoke-CIPPStandardExConnector { $Filter = "PartitionKey eq 'ExConnectorTemplate' and RowKey eq '$($Template.value)'" $connectorType = (Get-AzDataTableEntity @Table -Filter $Filter).direction $RequestParams = (Get-AzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json - if($RequestParams.comment) { $RequestParams.comment = Get-CIPPTextReplacement -Text $RequestParams.comment -TenantFilter $Tenant } else { $RequestParams | Add-Member -NotePropertyValue "no comment" -NotePropertyName comment -Force } + if ($RequestParams.comment) { $RequestParams.comment = Get-CIPPTextReplacement -Text $RequestParams.comment -TenantFilter $Tenant } else { $RequestParams | Add-Member -NotePropertyValue 'no comment' -NotePropertyName comment -Force } $Existing = New-ExoRequest -ErrorAction SilentlyContinue -tenantid $Tenant -cmdlet "Get-$($ConnectorType)connector" | Where-Object -Property Identity -EQ $RequestParams.name if ($Existing) { $RequestParams | Add-Member -NotePropertyValue $Existing.Identity -NotePropertyName Identity -Force From 7107bb6157332414aad8e98241bf204be09f1e91 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Tue, 22 Jul 2025 14:53:03 +0200 Subject: [PATCH 03/85] fix issue with CA template editing --- .../HTTP Functions/CIPP/Core/Invoke-ExecEditTemplate.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecEditTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecEditTemplate.ps1 index 96bb985840b9..cb88cf706419 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecEditTemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecEditTemplate.ps1 @@ -16,7 +16,7 @@ function Invoke-ExecEditTemplate { try { $Table = Get-CippTable -tablename 'templates' - $guid = $request.body.id + $guid = $request.body.id ? $request.body.id : $request.body.GUID $JSON = ConvertTo-Json -Compress -Depth 100 -InputObject ($request.body | Select-Object * -ExcludeProperty GUID) $Type = $request.query.Type From b2e27d348258565221651ad29700b62c8cbd90f7 Mon Sep 17 00:00:00 2001 From: STG-Tanner <102822322+STG-Tanner@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:52:44 -0500 Subject: [PATCH 04/85] Fix URL in Invoke-NinjaOneTenantSync.ps1 --- .../Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index aeb7cabfd00d..155a400aea6a 100644 --- a/Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -1575,7 +1575,7 @@ function Invoke-NinjaOneTenantSync { @{ Name = 'CIPP Tenant Dashboard' - Link = "https://$CIPPUrl?tenantFilter=$($Customer.defaultDomainName)" + Link = "https://$CIPPUrl/?tenantFilter=$($Customer.defaultDomainName)" Icon = 'fas fa-shield-halved' }, @{ From abd014aca76fba32f485c619cd15afebb5ad98be Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 22 Jul 2025 17:30:49 -0400 Subject: [PATCH 05/85] catch exceptions for test-cippaccess and return unauthorized --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 546ca1cebb7a..524a3f6fd0ad 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -53,7 +53,16 @@ function Receive-CippHttpTrigger { if ($FunctionName -eq 'Invoke-Me') { return } - + } catch { + Write-Information "Access denied for $FunctionName : $($_.Exception.Message)" + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::Forbidden + Body = $_.Exception.Message + }) + return + } + + try { Write-Information "Access: $Access" if ($Access) { & $FunctionName @HttpTrigger From 639c6909c149be87a3b5ecea38204e94816b3d1e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Wed, 23 Jul 2025 13:01:17 +0200 Subject: [PATCH 06/85] fix breach check exec ution --- .../CIPPCore/Public/Entrypoints/Invoke-ExecBreachSearch.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBreachSearch.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBreachSearch.ps1 index f012ad7ec12e..2fb1e75e4337 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBreachSearch.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBreachSearch.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-ExecBreachSearch { +function Invoke-ExecBreachSearch { <# .FUNCTIONALITY Entrypoint @@ -15,13 +15,13 @@ Function Invoke-ExecBreachSearch { Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' # Interact with query parameters or the body of the request. - $TenantFilter = $Request.query.tenantFilter + $TenantFilter = $Request.body.tenantFilter #Move to background job New-BreachTenantSearch -TenantFilter $TenantFilter Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK - Body = @{ Results = "Executing Search for $TenantFilter" } + Body = @{ Results = "Executing Search for $TenantFilter. This may take up to 24 hours to complete." } }) } From 2dfa809c132a1c94a9a2ac6c4442d27ff7135367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 23 Jul 2025 15:00:35 +0200 Subject: [PATCH 07/85] fix: update state retrieval and logging message for CloudMessageRecall --- .../Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 index fed75ae9b8a3..ccd6411243c1 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 @@ -38,7 +38,7 @@ function Invoke-CIPPStandardCloudMessageRecall { ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'CloudMessageRecall' # Get state value using null-coalescing operator - $state = $Settings.state.value + $state = $Settings.state.value ?? $Settings.state $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').MessageRecallEnabled $WantedState = if ($state -eq 'true') { $true } else { $false } @@ -53,7 +53,7 @@ function Invoke-CIPPStandardCloudMessageRecall { # Input validation if (([string]::IsNullOrWhiteSpace($state) -or $state -eq 'Select a value') -and ($Settings.remediate -eq $true -or $Settings.alert -eq $true)) { - Write-LogMessage -API 'Standards' -tenant $Tenant -message 'MessageRecallEnabled: Invalid state parameter set' -sev Error + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'CloudMessageRecall: Invalid state parameter set' -sev Error return } From b7f016f280762d20297a000a25f48e6541d420c2 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:45:15 +0200 Subject: [PATCH 08/85] CAAccessTemplates --- ...-CIPPStandardConditionalAccessTemplate.ps1 | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 index cdf52e7608a0..e5910a4caada 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 @@ -31,17 +31,17 @@ function Invoke-CIPPStandardConditionalAccessTemplate { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'ConditionalAccess' $TestResult = Test-CIPPStandardLicense -StandardName 'ConditionalAccessTemplate' -TenantFilter $Tenant -RequiredCapabilities @('AAD_PREMIUM', 'AAD_PREMIUM_P2') + $Table = Get-CippTable -tablename 'templates' if ($TestResult -eq $false) { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. + $AllCAPolicies = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies?$top=999' -tenantid $Tenant if ($Settings.remediate -eq $true) { - $AllCAPolicies = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies?$top=999' -tenantid $Tenant foreach ($Setting in $Settings) { try { - $Table = Get-CippTable -tablename 'templates' $Filter = "PartitionKey eq 'CATemplate' and RowKey eq '$($Setting.TemplateList.value)'" $JSONObj = (Get-CippAzDataTableEntity @Table -Filter $Filter).JSON $null = New-CIPPCAPolicy -replacePattern 'displayName' -TenantFilter $tenant -state $Setting.state -RawJSON $JSONObj -Overwrite $true -APIName $APIName -Headers $Request.Headers -DisableSD $Setting.DisableSD @@ -51,20 +51,28 @@ function Invoke-CIPPStandardConditionalAccessTemplate { } } } - if ($Settings.report -eq $true) { - $Policies = $Settings.TemplateList.JSON | ConvertFrom-Json -Depth 10 + if ($Settings.report -eq $true -or $Settings.remediate -eq $true) { + Write-Host 'REPORT: Checking if all policies are present in the tenant.' + Write-Host "REPORT: Templates in policy: $($Settings.TemplateList.label)" + $Filter = "PartitionKey eq 'CATemplate'" + $Policies = (Get-CippAzDataTableEntity @Table -Filter $Filter | Where-Object RowKey -In $Settings.TemplateList.value).JSON | ConvertFrom-Json -Depth 10 + Write-Host "REPORT: Found $($Policies.Count) policies in the template." #check if all groups.displayName are in the existingGroups, if not $fieldvalue should contain all missing groups, else it should be true. - $MissingPolicies = foreach ($policy in $Policies) { - $CheckExististing = $AllCAPolicies | Where-Object -Property displayName -EQ $policy.displayname + $MissingPolicies = foreach ($Setting in $Settings.TemplateList) { + Write-Host "REPORT: Checking if policy $($Setting.displayname) exists in the tenant." + $policy = $Policies | Where-Object { $_.displayName -eq $Setting.label } + Write-Host "REPORT: Policy data is: $($policy | ConvertTo-Json -Depth 10)" + $CheckExististing = $AllCAPolicies | Where-Object -Property displayName -EQ $Setting.displayname if (!$CheckExististing) { - $policy.displayname + Write-Host "REPORT: Policy $($Setting.value) with does not exist in the tenant." + Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue "Policy $($Setting.label) is missing from this tenant." -Tenant $Tenant + } else { + $Compare = Compare-CIPPIntuneObject -ReferenceObject $policy -compareObject $CheckExististing } } - if ($MissingPolicies.Count -eq 0) { - $fieldValue = $true - } else { - $fieldValue = $MissingPolicies -join ', ' - } - Set-CIPPStandardsCompareField -FieldName 'standards.ConditionalAccessTemplate' -FieldValue $fieldValue -Tenant $Tenant + Write-Host "REPORT: Found $($MissingPolicies.Count) policies that are missing in the tenant." + + Write-Host "REPORT: The following policies are missing: $fieldValue" + Write-Host "REPORT: Setting field value to $fieldValue" } } From 4dee69b8c925511f4fd681b2738365bbbfd95beb Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:05:38 +0200 Subject: [PATCH 09/85] Fixed CA compare --- ...-CIPPStandardConditionalAccessTemplate.ps1 | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 index e5910a4caada..5ecc913b4c91 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 @@ -52,27 +52,22 @@ function Invoke-CIPPStandardConditionalAccessTemplate { } } if ($Settings.report -eq $true -or $Settings.remediate -eq $true) { - Write-Host 'REPORT: Checking if all policies are present in the tenant.' - Write-Host "REPORT: Templates in policy: $($Settings.TemplateList.label)" $Filter = "PartitionKey eq 'CATemplate'" $Policies = (Get-CippAzDataTableEntity @Table -Filter $Filter | Where-Object RowKey -In $Settings.TemplateList.value).JSON | ConvertFrom-Json -Depth 10 - Write-Host "REPORT: Found $($Policies.Count) policies in the template." #check if all groups.displayName are in the existingGroups, if not $fieldvalue should contain all missing groups, else it should be true. $MissingPolicies = foreach ($Setting in $Settings.TemplateList) { - Write-Host "REPORT: Checking if policy $($Setting.displayname) exists in the tenant." $policy = $Policies | Where-Object { $_.displayName -eq $Setting.label } - Write-Host "REPORT: Policy data is: $($policy | ConvertTo-Json -Depth 10)" - $CheckExististing = $AllCAPolicies | Where-Object -Property displayName -EQ $Setting.displayname + $CheckExististing = $AllCAPolicies | Where-Object -Property displayName -EQ $Setting.label if (!$CheckExististing) { - Write-Host "REPORT: Policy $($Setting.value) with does not exist in the tenant." Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue "Policy $($Setting.label) is missing from this tenant." -Tenant $Tenant } else { - $Compare = Compare-CIPPIntuneObject -ReferenceObject $policy -compareObject $CheckExististing + $Compare = Compare-CIPPIntuneObject -ReferenceObject $policy -DifferenceObject $CheckExististing + if (!$Compare) { + Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue $Compare -Tenant $Tenant + } else { + Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue $true -Tenant $Tenant + } } } - Write-Host "REPORT: Found $($MissingPolicies.Count) policies that are missing in the tenant." - - Write-Host "REPORT: The following policies are missing: $fieldValue" - Write-Host "REPORT: Setting field value to $fieldValue" } } From f5f8ed7b8b2e80fbb5d3a0a153620c1d4c1833cc Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:17:32 +0200 Subject: [PATCH 10/85] compare ca policy --- .../Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 index 5ecc913b4c91..c85dc7bb6464 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 @@ -61,7 +61,8 @@ function Invoke-CIPPStandardConditionalAccessTemplate { if (!$CheckExististing) { Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue "Policy $($Setting.label) is missing from this tenant." -Tenant $Tenant } else { - $Compare = Compare-CIPPIntuneObject -ReferenceObject $policy -DifferenceObject $CheckExististing + $CompareObj = ConvertFrom-Json -ErrorAction SilentlyContinue -InputObject (New-CIPPCATemplate -TenantFilter $tenant -JSON $CheckExististing) + $Compare = Compare-CIPPIntuneObject -ReferenceObject $policy -DifferenceObject $CompareObj if (!$Compare) { Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue $Compare -Tenant $Tenant } else { From 2952c31b9d98e70509c274bc01cf1baa32758fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 23 Jul 2025 16:15:56 +0200 Subject: [PATCH 11/85] fix blocked for spam displaying being inverted, and also invalid parameter --- .../Users/Invoke-ListUserMailboxDetails.ps1 | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 index 1f5ed5a1a1bf..34f7a1d7c5b1 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 @@ -18,6 +18,10 @@ function Invoke-ListUserMailboxDetails { # Interact with query parameters or the body of the request. $TenantFilter = $Request.Query.tenantFilter $UserID = $Request.Query.UserID + $UserMail = $Request.Query.userMail + Write-Host "TenantFilter: $TenantFilter" + Write-Host "UserID: $UserID" + Write-Host "UserMail: $UserMail" try { $Requests = @( @@ -53,7 +57,7 @@ function Invoke-ListUserMailboxDetails { @{ CmdletInput = @{ CmdletName = 'Get-BlockedSenderAddress' - Parameters = @{ Identity = $UserID } + Parameters = @{ SenderAddress = $UserMail } } }, @{ @@ -63,7 +67,6 @@ function Invoke-ListUserMailboxDetails { } } ) - Write-Host $UserID $usernames = New-GraphGetRequest -tenantid $TenantFilter -uri 'https://graph.microsoft.com/beta/users?$select=id,userPrincipalName,displayName,mailNickname&$top=999' $Results = New-ExoBulkRequest -TenantId $TenantFilter -CmdletArray $Requests -returnWithCommand $true -Anchor $username Write-Host "First line of usernames is $($usernames[0] | ConvertTo-Json)" @@ -105,9 +108,9 @@ function Invoke-ListUserMailboxDetails { # Determine if the user is blocked for spam if ($BlockedSender -and $BlockedSender.Count -gt 0) { - $BlockedForSpam = $false - } else { $BlockedForSpam = $true + } else { + $BlockedForSpam = $false } } catch { Write-Error "Failed Fetching Data $($_.Exception.message): $($_.InvocationInfo.ScriptLineNumber)" From 9f76be4550188cc04edeb8110c639b920cd20911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 23 Jul 2025 16:41:34 +0200 Subject: [PATCH 12/85] WORDS --- cspell.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cspell.json b/cspell.json index b931fc283665..56a59eca16f4 100644 --- a/cspell.json +++ b/cspell.json @@ -4,6 +4,7 @@ "dictionaryDefinitions": [], "dictionaries": [], "words": [ + "adminapi", "ADMS", "AITM", "Autotask", @@ -23,6 +24,7 @@ "gdap", "GDAP", "IMAP", + "innererror", "Intune", "locationcipp", "MAPI", From 2a820fd82a34bcde53165d60510f9d62179c8c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 23 Jul 2025 16:41:51 +0200 Subject: [PATCH 13/85] Minor formatting and flip the null check --- Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 index 3f42b22ae2e1..2abf72d3f2af 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 @@ -4,7 +4,7 @@ function New-ExoRequest { Internal #> [CmdletBinding(DefaultParameterSetName = 'ExoRequest')] - Param( + param( [Parameter(Mandatory = $true, ParameterSetName = 'ExoRequest')] [string]$cmdlet, @@ -43,7 +43,7 @@ function New-ExoRequest { $token = Get-GraphToken -Tenantid $tenantid -scope "$Resource/.default" -AsApp:$AsApp.IsPresent if ($cmdParams) { - #if cmdparams is a pscustomobject, convert to hashtable, otherwise leave as is + #if cmdParams is a pscustomobject, convert to hashtable, otherwise leave as is $Params = $cmdParams } else { $Params = @{} @@ -145,7 +145,7 @@ function New-ExoRequest { } until ($null -eq $URL) Write-Verbose ($ResponseHeaders | ConvertTo-Json) - if ($ReturnedData.'@adminapi.warnings' -and $ReturnedData.value -eq $null) { + if ($ReturnedData.'@adminapi.warnings' -and $null -eq $ReturnedData.value) { $ReturnedData.value = $ReturnedData.'@adminapi.warnings' } } catch { From 0a8b548e71157d8b0815073da3427d031d9f3d4a Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:46:20 +0200 Subject: [PATCH 14/85] update ca policy deployment --- .../Invoke-CIPPStandardConditionalAccessTemplate.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 index c85dc7bb6464..f12c32b6e2e9 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 @@ -64,9 +64,9 @@ function Invoke-CIPPStandardConditionalAccessTemplate { $CompareObj = ConvertFrom-Json -ErrorAction SilentlyContinue -InputObject (New-CIPPCATemplate -TenantFilter $tenant -JSON $CheckExististing) $Compare = Compare-CIPPIntuneObject -ReferenceObject $policy -DifferenceObject $CompareObj if (!$Compare) { - Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue $Compare -Tenant $Tenant - } else { Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue $true -Tenant $Tenant + } else { + Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue $Compare -Tenant $Tenant } } } From 8d85f2463a10ded55b7f53f162f7dcf4f1eca761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 23 Jul 2025 20:52:39 +0200 Subject: [PATCH 15/85] fix variable casing and improve error handling in Add-CIPPAlias function --- Modules/CIPPCore/Public/Add-CIPPAlias.ps1 | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Modules/CIPPCore/Public/Add-CIPPAlias.ps1 b/Modules/CIPPCore/Public/Add-CIPPAlias.ps1 index 075f114f767e..6a0fc8615eb1 100644 --- a/Modules/CIPPCore/Public/Add-CIPPAlias.ps1 +++ b/Modules/CIPPCore/Public/Add-CIPPAlias.ps1 @@ -1,9 +1,9 @@ function Add-CIPPAlias { [CmdletBinding()] param ( - $user, + $User, $Aliases, - $UserprincipalName, + $UserPrincipalName, $TenantFilter, $APIName = 'Add Alias', $Headers @@ -11,16 +11,17 @@ function Add-CIPPAlias { try { foreach ($Alias in $Aliases) { - Write-Host "Adding alias $Alias to $user" - New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$user" -tenantid $TenantFilter -type 'patch' -body "{`"mail`": `"$Alias`"}" -verbose + Write-Host "Adding alias $Alias to $User" + New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$User" -tenantid $TenantFilter -type 'patch' -body "{`"mail`": `"$Alias`"}" -verbose } Write-Host "Resetting primary alias to $User" - New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($user)" -tenantid $TenantFilter -type 'patch' -body "{`"mail`": `"$User`"}" -verbose - Write-LogMessage -headers $Headers -API $APINAME -tenant $($TenantFilter) -message "Added alias $($Alias) to $($UserprincipalName)" -Sev 'Info' + New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$User" -tenantid $TenantFilter -type 'patch' -body "{`"mail`": `"$User`"}" -verbose + Write-LogMessage -headers $Headers -API $APIName -tenant $($TenantFilter) -message "Added alias $($Alias) to $($UserPrincipalName)" -Sev 'Info' return ("Added Aliases: $($Aliases -join ',')") } catch { - Write-LogMessage -headers $Headers -API $APINAME -tenant $($TenantFilter) -message "Failed to set alias. Error:$($_.Exception.Message)" -Sev 'Error' - throw "Failed to set alias: $($_.Exception.Message)" + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -headers $Headers -API $APIName -tenant $($TenantFilter) -message "Failed to set alias. Error:$($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + throw "Failed to set alias: $($ErrorMessage.NormalizedError)" } } From e835c594aa21184a6d9ebed8a99e7aa7478a998b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 23 Jul 2025 21:14:18 +0200 Subject: [PATCH 16/85] Add missing state and otherMails property, fix casing, add set sponsor function and change edituser code to use CIPP functions --- .../Administration/Users/Invoke-EditUser.ps1 | 14 ++++-------- Modules/CIPPCore/Public/New-CIPPUserTask.ps1 | 6 ++--- Modules/CIPPCore/Public/New-CippUser.ps1 | 2 ++ Modules/CIPPCore/Public/Set-CIPPManager.ps1 | 13 ++++++----- Modules/CIPPCore/Public/Set-CIPPSponsor.ps1 | 22 +++++++++++++++++++ 5 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 Modules/CIPPCore/Public/Set-CIPPSponsor.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 index f5fd50105a6d..a02af8d24403 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 @@ -224,19 +224,13 @@ function Invoke-EditUser { } if ($Request.body.setManager.value) { - $ManagerBody = [PSCustomObject]@{'@odata.id' = "https://graph.microsoft.com/beta/users/$($Request.body.setManager.value)" } - $ManagerBodyJSON = ConvertTo-Json -Compress -Depth 10 -InputObject $ManagerBody - $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($UserObj.id)/manager/`$ref" -tenantid $UserObj.tenantFilter -type PUT -body $ManagerBodyJSON -Verbose - Write-LogMessage -headers $Headers -API $APIName -tenant $UserObj.tenantFilter -message "Set $($UserObj.DisplayName)'s manager to $($Request.body.setManager.label)" -Sev Info - $Results.Add("Success. Set $($UserObj.DisplayName)'s manager to $($Request.body.setManager.label)") + $ManagerResult = Set-CIPPManager -User $UserPrincipalName -Manager $Request.body.setManager.value -TenantFilter $UserObj.tenantFilter -Headers $Headers + $Results.Add($ManagerResult) } if ($Request.body.setSponsor.value) { - $SponsorBody = [PSCustomObject]@{'@odata.id' = "https://graph.microsoft.com/beta/users/$($Request.body.setSponsor.value)" } - $SponsorBodyJSON = ConvertTo-Json -Compress -Depth 10 -InputObject $SponsorBody - $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($UserObj.id)/sponsors/`$ref" -tenantid $UserObj.tenantFilter -type POST -body $SponsorBodyJSON -Verbose - Write-LogMessage -headers $Headers -API $APIName -tenant $UserObj.tenantFilter -message "Set $($UserObj.DisplayName)'s sponsor to $($Request.body.setSponsor.label)" -Sev Info - $Results.Add("Success. Set $($UserObj.DisplayName)'s sponsor to $($Request.body.setSponsor.label)") + $SponsorResult = Set-CIPPSponsor -User $UserPrincipalName -Sponsor $Request.body.setSponsor.value -TenantFilter $UserObj.tenantFilter -Headers $Headers + $Results.Add($SponsorResult) } # Associate values to output bindings by calling 'Push-OutputBinding'. diff --git a/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 b/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 index 97c14c02d6e6..e18f016429a6 100644 --- a/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 @@ -54,7 +54,7 @@ function New-CIPPUserTask { try { if ($UserObj.AddedAliases) { - $AliasResults = Add-CIPPAlias -user $CreationResults.Username -Aliases ($UserObj.AddedAliases -split '\s') -UserprincipalName $CreationResults.Username -TenantFilter $UserObj.tenantFilter -APIName $APIName -Headers $Headers + $AliasResults = Add-CIPPAlias -User $CreationResults.Username -Aliases ($UserObj.AddedAliases -split '\s') -UserPrincipalName $CreationResults.Username -TenantFilter $UserObj.tenantFilter -APIName $APIName -Headers $Headers $Results.Add($AliasResults) } } catch { @@ -69,12 +69,12 @@ function New-CIPPUserTask { } if ($UserObj.setManager) { - $ManagerResult = Set-CIPPManager -user $CreationResults.Username -Manager $UserObj.setManager.value -TenantFilter $UserObj.tenantFilter -APIName 'Set Manager' -Headers $Headers + $ManagerResult = Set-CIPPManager -User $CreationResults.Username -Manager $UserObj.setManager.value -TenantFilter $UserObj.tenantFilter -Headers $Headers $Results.Add($ManagerResult) } if ($UserObj.setSponsor) { - $SponsorResult = Set-CIPPManager -user $CreationResults.Username -Manager $UserObj.setSponsor.value -TenantFilter $UserObj.tenantFilter -APIName 'Set Sponsor' -Headers $Headers + $SponsorResult = Set-CIPPSponsor -User $CreationResults.Username -Sponsor $UserObj.setSponsor.value -TenantFilter $UserObj.tenantFilter -Headers $Headers $Results.Add($SponsorResult) } diff --git a/Modules/CIPPCore/Public/New-CippUser.ps1 b/Modules/CIPPCore/Public/New-CippUser.ps1 index e5dde2e8bc73..e63c53f024c6 100644 --- a/Modules/CIPPCore/Public/New-CippUser.ps1 +++ b/Modules/CIPPCore/Public/New-CippUser.ps1 @@ -26,12 +26,14 @@ function New-CIPPUser { 'userPrincipalName' = $UserPrincipalName 'usageLocation' = $UserObj.usageLocation.value ? $UserObj.usageLocation.value : $UserObj.usageLocation 'city' = $UserObj.City + 'state' = $UserObj.state 'country' = $UserObj.Country 'jobtitle' = $UserObj.Jobtitle 'mobilePhone' = $UserObj.MobilePhone 'streetAddress' = $UserObj.streetAddress 'postalCode' = $UserObj.PostalCode 'companyName' = $UserObj.CompanyName + 'otherMails' = $UserObj.otherMails ? @($UserObj.otherMails) : @() 'passwordProfile' = @{ 'forceChangePasswordNextSignIn' = [bool]$UserObj.MustChangePass 'password' = $password diff --git a/Modules/CIPPCore/Public/Set-CIPPManager.ps1 b/Modules/CIPPCore/Public/Set-CIPPManager.ps1 index e9d172853173..c1394b04936d 100644 --- a/Modules/CIPPCore/Public/Set-CIPPManager.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPManager.ps1 @@ -1,7 +1,7 @@ function Set-CIPPManager { [CmdletBinding()] param ( - $user, + $User, $Manager, $TenantFilter, $APIName = 'Set Manager', @@ -11,12 +11,13 @@ function Set-CIPPManager { try { $ManagerBody = [PSCustomObject]@{'@odata.id' = "https://graph.microsoft.com/beta/users/$($Manager)" } $ManagerBodyJSON = ConvertTo-Json -Compress -Depth 10 -InputObject $ManagerBody - New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($User)/manager/`$ref" -tenantid $TenantFilter -type PUT -body $ManagerBodyJSON -Verbose - Write-LogMessage -headers $Headers -API $APINAME -tenant $UserObj.tenantID -message "Set $user's manager to $Manager" -Sev 'Info' + $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($User)/manager/`$ref" -tenantid $TenantFilter -type PUT -body $ManagerBodyJSON + Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message "Set $User's manager to $Manager" -Sev 'Info' } catch { - Write-LogMessage -headers $Headers -API $APINAME -tenant $($UserObj.tenantID) -message "Failed to Set Manager. Error:$($_.Exception.Message)" -Sev 'Error' - throw "Failed to set manager: $($_.Exception.Message)" + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message "Failed to Set Manager. Error:$($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $_ + throw "Failed to set manager: $($ErrorMessage.NormalizedError)" } - return "Set $user's manager to $Manager" + return "Set $User's manager to $Manager" } diff --git a/Modules/CIPPCore/Public/Set-CIPPSponsor.ps1 b/Modules/CIPPCore/Public/Set-CIPPSponsor.ps1 new file mode 100644 index 000000000000..ad2444c01b9f --- /dev/null +++ b/Modules/CIPPCore/Public/Set-CIPPSponsor.ps1 @@ -0,0 +1,22 @@ +function Set-CIPPSponsor { + [CmdletBinding()] + param ( + $User, + $Sponsor, + $TenantFilter, + $APIName = 'Set Sponsor', + $Headers + ) + + try { + $SponsorBody = [PSCustomObject]@{'@odata.id' = "https://graph.microsoft.com/beta/users/$($Sponsor)" } + $SponsorBodyJSON = ConvertTo-Json -Compress -Depth 10 -InputObject $SponsorBody + $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($User)/sponsors/`$ref" -tenantid $TenantFilter -type PUT -body $SponsorBodyJSON + Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message "Set $User's sponsor to $Sponsor" -Sev 'Info' + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message "Failed to Set Sponsor. Error:$($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $_ + throw "Failed to set sponsor: $($_.Exception.Message)" + } + return "Set $user's sponsor to $Sponsor" +} From ed97a0ea09006f6db1f0546da549ccfc057faeed Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 23 Jul 2025 17:45:37 -0400 Subject: [PATCH 17/85] allow partner tenant lookups --- .../HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 index 42d4da8b8085..489bf3371654 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 @@ -1,7 +1,7 @@ function Invoke-ListDirectoryObjects { <# .FUNCTIONALITY - Entrypoint + Entrypoint,AnyTenant .ROLE CIPP.Core.Read #> @@ -28,7 +28,7 @@ function Invoke-ListDirectoryObjects { } | ConvertTo-Json -Depth 10 try { - $Results = New-GraphPOSTRequest -tenantid $TenantFilter -uri $Uri -body $Body -AsApp $AsApp + $Results = New-GraphPOSTRequest -tenantid $TenantFilter -uri $Uri -body $Body -AsApp $AsApp -NoAuthCheck $true $StatusCode = [System.Net.HttpStatusCode]::OK } catch { $StatusCode = [System.Net.HttpStatusCode]::BadRequest From b2cde732e2c87b7338c898d3418f4c1b8e114ea4 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 23 Jul 2025 18:02:10 -0400 Subject: [PATCH 18/85] fix payload for add/edit user --- .../Administration/Users/Invoke-EditUser.ps1 | 15 ++++++++------- Modules/CIPPCore/Public/New-CippUser.ps1 | 17 +++++++++-------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 index f5fd50105a6d..144a28f17316 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 @@ -38,17 +38,18 @@ function Invoke-EditUser { 'givenName' = $UserObj.givenName 'surname' = $UserObj.surname 'displayName' = $UserObj.displayName - 'department' = $UserObj.Department - 'mailNickname' = $UserObj.Username ? $UserObj.username :$UserObj.mailNickname + 'department' = $UserObj.department + 'mailNickname' = $UserObj.username ? $UserObj.username : $UserObj.mailNickname 'userPrincipalName' = $UserPrincipalName 'usageLocation' = $UserObj.usageLocation.value ? $UserObj.usageLocation.value : $UserObj.usageLocation - 'city' = $UserObj.City - 'country' = $UserObj.Country 'jobTitle' = $UserObj.jobTitle - 'mobilePhone' = $UserObj.MobilePhone + 'mobilePhone' = $UserObj.mobilePhone 'streetAddress' = $UserObj.streetAddress - 'postalCode' = $UserObj.PostalCode - 'companyName' = $UserObj.CompanyName + 'city' = $UserObj.city + 'state' = $UserObj.state + 'postalCode' = $UserObj.postalCode + 'country' = $UserObj.country + 'companyName' = $UserObj.companyName 'businessPhones' = $UserObj.businessPhones ? @($UserObj.businessPhones) : @() 'otherMails' = $UserObj.otherMails ? @($UserObj.otherMails) : @() 'passwordProfile' = @{ diff --git a/Modules/CIPPCore/Public/New-CippUser.ps1 b/Modules/CIPPCore/Public/New-CippUser.ps1 index e5dde2e8bc73..c0fcec02a611 100644 --- a/Modules/CIPPCore/Public/New-CippUser.ps1 +++ b/Modules/CIPPCore/Public/New-CippUser.ps1 @@ -21,17 +21,18 @@ function New-CIPPUser { 'surname' = $UserObj.surname 'accountEnabled' = $true 'displayName' = $UserObj.displayName - 'department' = $UserObj.Department - 'mailNickname' = $UserObj.Username ? $UserObj.username : $UserObj.mailNickname + 'department' = $UserObj.department + 'mailNickname' = $UserObj.username ? $UserObj.username : $UserObj.mailNickname 'userPrincipalName' = $UserPrincipalName 'usageLocation' = $UserObj.usageLocation.value ? $UserObj.usageLocation.value : $UserObj.usageLocation - 'city' = $UserObj.City - 'country' = $UserObj.Country - 'jobtitle' = $UserObj.Jobtitle - 'mobilePhone' = $UserObj.MobilePhone + 'jobTitle' = $UserObj.jobTitle + 'mobilePhone' = $UserObj.mobilePhone 'streetAddress' = $UserObj.streetAddress - 'postalCode' = $UserObj.PostalCode - 'companyName' = $UserObj.CompanyName + 'city' = $UserObj.city + 'state' = $UserObj.state + 'country' = $UserObj.country + 'postalCode' = $UserObj.postalCode + 'companyName' = $UserObj.companyName 'passwordProfile' = @{ 'forceChangePasswordNextSignIn' = [bool]$UserObj.MustChangePass 'password' = $password From 9b533535ca09b8f43170d91ba60f730cb82e2291 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 23 Jul 2025 22:45:16 -0400 Subject: [PATCH 19/85] fix issue loading scheduler with corrupt data --- .../CIPP/Scheduler/Invoke-ListScheduledItems.ps1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-ListScheduledItems.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-ListScheduledItems.ps1 index b60fe1e64a15..68d4e7f692c2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-ListScheduledItems.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-ListScheduledItems.ps1 @@ -61,12 +61,18 @@ function Invoke-ListScheduledItems { $Tasks = $Tasks | Where-Object -Property Tenant -In $AllowedTenantDomains } $ScheduledTasks = foreach ($Task in $tasks) { + if (!$Task.Tenant -or !$Task.Command) { + continue + } + if ($Task.Parameters) { $Task.Parameters = $Task.Parameters | ConvertFrom-Json -ErrorAction SilentlyContinue } else { $Task | Add-Member -NotePropertyName Parameters -NotePropertyValue @{} } - if ($Task.Recurrence -eq 0 -or [string]::IsNullOrEmpty($Task.Recurrence)) { + if (!$Task.Recurrence) { + $Task | Add-Member -NotePropertyName Recurrence -NotePropertyValue 'Once' -Force + } elseif ($Task.Recurrence -eq 0 -or [string]::IsNullOrEmpty($Task.Recurrence)) { $Task.Recurrence = 'Once' } try { From a1591da3b1f3c0609c1c1765fe0305f23bcb738d Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 23 Jul 2025 22:53:41 -0400 Subject: [PATCH 20/85] suppress extra output --- Modules/CIPPCore/Public/Set-CIPPCAExclusion.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPCAExclusion.ps1 b/Modules/CIPPCore/Public/Set-CIPPCAExclusion.ps1 index 41e7b560e982..9c6443c15103 100644 --- a/Modules/CIPPCore/Public/Set-CIPPCAExclusion.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPCAExclusion.ps1 @@ -43,7 +43,7 @@ function Set-CIPPCAExclusion { $RawJson = ConvertTo-Json -Depth 10 -InputObject $NewExclusions if ($PSCmdlet.ShouldProcess($PolicyId, "Add exclusion for $UserID")) { - New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExisting.id)" -tenantid $tenantfilter -type PATCH -body $RawJSON -AsApp $true + $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExisting.id)" -tenantid $tenantfilter -type PATCH -body $RawJSON -AsApp $true } } @@ -65,7 +65,7 @@ function Set-CIPPCAExclusion { } $RawJson = ConvertTo-Json -Depth 10 -InputObject $NewExclusions if ($PSCmdlet.ShouldProcess($PolicyId, "Remove exclusion for $UserID")) { - New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExisting.id)" -tenantid $tenantfilter -type PATCH -body $RawJSON -AsApp $true + $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExisting.id)" -tenantid $tenantfilter -type PATCH -body $RawJSON -AsApp $true } } From e906274dd7f76f19723665dcc71cd43b0bb38101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Thu, 24 Jul 2025 11:11:05 +0200 Subject: [PATCH 21/85] update to newest license files --- ConversionTable.csv | 95 +++++++++++++++++++ Modules/CIPPCore/Public/ConversionTable.csv | 95 +++++++++++++++++++ Modules/CippExtensions/ConversionTable.csv | 95 +++++++++++++++++++ .../CippExtensions/Public/ConversionTable.csv | 95 +++++++++++++++++++ 4 files changed, 380 insertions(+) diff --git a/ConversionTable.csv b/ConversionTable.csv index 2658524b8cf2..9407f70d6cb7 100644 --- a/ConversionTable.csv +++ b/ConversionTable.csv @@ -240,6 +240,9 @@ Dynamics 365 Enterprise Edition - Additional Production Instance for Government, "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ENGINE_ADDON,24435e4b-87d0-4d7d-8beb-63a9b1573022,Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ADDON,2ba394e0-6f18-4b77-b45f-a5663bbab540,RETIRED - Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,CDS_FIELD_SERVICE_CONTRACTOR,f4614a66-d632-443a-bc77-afe92987b322,Common Data Service Field service Part Time Contractors +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,POWERAPPS_DYN_APPS,874fc546-6efe-4d22-90b8-5c4e7aa59f4b,Power Apps for Dynamics 365 Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,CDS_FIELD_SERVICE_CONTRACTOR_GCC,2457fe40-65be-48a1-935f-924ad6e62dba,Common Data Service Field service Part Time Contractors for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,EXCHANGE_S_FOUNDATION_GOV,922ba911-5694-4e99-a794-73aed9bfeec8,Exchange Foundation for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,POWERAPPS_DYN_APPS_GOV,3089c02b-e533-4b73-96a5-01fa648c3c3c,PowerApps for Dynamics 365 for Government @@ -2246,6 +2249,93 @@ Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,AAD_PREMIUM_P2,eec0 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_FOR_TEAMS,dcf9d2f4-772e-4434-b757-77a453cfbc02,Avatars for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_ADDITIONAL_FOR_TEAMS,3efbd4ed-8958-4824-8389-1321f8730af8,Avatars for Teams (additional) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CustomerLockboxA_Enterprise,3ec18638-bd4c-4d3b-8905-479ed636b83e,Customer Lockbox (A) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_S_ENTERPRISE,efb87545-963c-4e0d-99df-69c6916d9eb0,Exchange Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,GRAPH_CONNECTORS_SEARCH_INDEX,a6520331-d7d4-4276-95f5-15c0933bc757,Graph Connectors Search with Index +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_IMMERSIVE_FOR_TEAMS,f0ff6ac6-297d-49cd-be34-6dfef97f0c28,Immersive spaces for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFORMATION_BARRIERS,c4801e8a-cb58-4c35-aca6-f2dcc106f287,Information Barriers +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Content_Explorer,d9fa6af4-e046-4c89-9226-729a0786685d,Information Protection and Governance Analytics - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ContentExplorer_Standard,2b815d45-56e4-4e3a-b65c-66cb9175b560,Information Protection and Governance Analytics – Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP2,efb0351d-3b08-4503-993d-383af8de41e3,Information Protection for Office 365 - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP1,5136a095-5cf0-4aff-bec3-e84448b38ea5,Information Protection for Office 365 - Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MYANALYTICS_P2,33c4f319-9bdd-48d6-9c4d-410b750a4a5a,Insights by MyAnalytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_ADVANCED_AUDITING,2f442157-a11c-46b9-ae5b-6e39ff4e5849,Microsoft 365 Advanced Auditing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,OFFICESUBSCRIPTION,43de0ff5-c92c-492b-9116-175376d08c38,Microsoft 365 Apps for enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOMEETADV,3e26ee1f-8a5f-4d52-aee2-b81ce45c8f40,Microsoft 365 Audio Conferencing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_AUDIT_PLATFORM,f6de4823-28fa-440b-b886-4783fa86ddba,Microsoft 365 Audit Platform +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_COMMUNICATION_COMPLIANCE,a413a9ff-720c-4822-98ef-2f37c2a21f4c,Microsoft 365 Communication Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MTP,bf28f719-7844-4079-9c78-c1307898e192,Microsoft 365 Defender +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_LIGHTHOUSE_CUSTOMER_PLAN1,6f23d6a9-adbf-481c-8538-b4c095654487,Microsoft 365 Lighthouse (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOEV,4828c8ec-dc2e-4779-b502-87ac9ce28ab7,Microsoft 365 Phone System +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTBOOKINGS,199a5c09-e0ca-4e37-8f7c-b05d533e1ea2,Microsoft Bookings +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CLIPCHAMP,a1ace008-72f3-4ea0-8dac-33b3a23a2472,Microsoft Clipchamp +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_DLP,6dc145d6-95dd-4191-b9c3-185575ee6f6b,Microsoft Communications DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CUSTOMER_KEY,6db1f1db-2b46-403f-be40-e39395f08dbb,Microsoft Customer Key +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATP_ENTERPRISE,f20fedf3-f3c3-43c3-8267-2bfdd51c0939,Microsoft Defender for Office 365 (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,THREAT_INTELLIGENCE,8e0c0a52-6a6c-4d40-8370-dd62790dcd70,Microsoft Defender for Office 365 (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCEL_PREMIUM,531ee2f8-b1cb-453b-9c21-d2180d014ca5,Microsoft Excel Advanced Analytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FORMS_PLAN_E5,e212cbc7-0961-4c40-9825-01117710dcb1,Microsoft Forms (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFO_GOVERNANCE,e26c2fcc-ab91-4a61-b35c-03cdc8dddf66,Microsoft Information Governance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK,d587c7a3-bda9-4f99-8776-9bcf59c84f75,Microsoft Insider Risk Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK_MANAGEMENT,9d0c4ee5-e4a1-4625-ab39-d82b619b1a34,Microsoft Insider Risk Management - Exchange +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,KAIZALA_STANDALONE,0898bdbb-73b0-471a-81e5-20f1fe4dd66e,Microsoft Kaizala Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_LOOP,c4b8c31a-fb44-4c65-9837-a21f55fcabda,Microsoft Loop +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ML_CLASSIFICATION,d2d51368-76c9-4317-ada2-a12c004c432f,Microsoft ML-Based Classification +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_ANALYTICS,34c0d7a0-a70f-4668-9238-47f9fc208882,Microsoft MyAnalytics (Full) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECTWORKMANAGEMENT,b737dad2-2f6c-4c65-90e3-ca563267e8b9,Microsoft Planner +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RECORDS_MANAGEMENT,65cc641f-cccd-4643-97e0-a17e3045e541,Microsoft Records Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_SEARCH,94065c59-bc8e-4e8b-89e5-5138d471eaff,Microsoft Search +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Deskless,8c7d2df8-86f0-4902-b2ed-a0458298f3b3,Microsoft StaffHub +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,STREAM_O365_E5,6c6042f5-6f01-4d67-b8c1-eb99d36eed3e,Microsoft Stream for Office 365 E5 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_O365,882e1d05-acd1-4ccb-8708-6ee03664b117,Mobile Device Management for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Nucleus,db4d623d-b514-490b-b7ef-8885eee514de,Nucleus +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EQUIVIO_ANALYTICS,4de31727-a228-4ec3-a5bf-8e45b5ca48cc,Office 365 Advanced eDiscovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_O365,8c098270-9dd4-4350-9b30-ba4703f3b36b,Office 365 Cloud App Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PAM_ENTERPRISE,b1188c4c-1b36-4018-b48b-ee07604f6feb,Office 365 Privileged Access Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SAFEDOCS,bf6f5520-59e3-4f82-974b-7dbbc4fd27c7,Office 365 SafeDocs +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTWAC,e95bec33-7c88-4a70-8e19-b10bd9d0c014,Office for the Web +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PEOPLE_SKILLS_FOUNDATION,13b6da2c-0d84-450e-9f69-a33e221387ca,People Skills - Foundation +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWERAPPS_O365_P3,9c0dab89-a30c-4117-86e7-97bda240acd2,Power Apps for Office 365 (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BI_AZURE_P2,70d33638-9c74-4d01-bfd3-562de28bd4ba,Power BI Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PREMIUM_ENCRYPTION,617b097b-4b93-4ede-83de-5f075bb5fb2f,Premium Encryption in Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECT_O365_P3,b21a6b06-1988-436e-a07b-51ec6d9f52ad,Project for Office (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PURVIEW_DISCOVERY,c948ea65-2053-4a5a-8a62-9eaaaf11b522,Purview Discovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Bing_Chat_Enterprise,0d0c0d31-fae7-41f2-b909-eaf4d7f26dba,RETIRED - Commercial data protection for Microsoft Copilot +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_COMPLIANCE,41fcdd7d-4733-4863-9cf4-c65b83ce2df4,RETIRED - Microsoft Communications Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DATA_INVESTIGATIONS,46129a58-a698-46f0-aa5b-17f6586297d9,Retired - Microsoft Data Investigations +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PLACES_CORE,1fe6227d-3e01-46d0-9510-0acad4ff6e94,RETIRED - Places Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTENTERPRISE,5dbe027f-2339-4123-9542-606e4d348a72,SharePoint (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOSTANDARD,0feaeb32-d00e-4d66-bd5a-43b5b83db82c,Skype for Business Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SWAY,a23b959c-7ce8-4e57-9140-b90eb88a9e97,Sway +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BPOS_S_TODO_3,3fb82609-8c27-4f7b-bd51-30634711ee67,To-Do (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVAENGAGE_CORE,a82fbf69-b4d7-49f4-83a6-915b2cf354f4,Viva Engage Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVA_LEARNING_SEEDED,b76fb638-6ba6-402a-b9f9-83d28acb3d86,Viva Learning Seeded +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WHITEBOARD_PLAN3,4a51bca5-1eff-43f5-878c-177680f191af,Whiteboard (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,YAMMER_ENTERPRISE,7547a3fe-08ee-4ccb-b430-5077c5041653,Yammer Enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDEFATP,871d91ec-ec1a-452b-a83f-bd76c7d770ef,Microsoft Defender for Endpoint +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTENDPOINTDLP,64bfac92-2b17-4482-b5e5-a0304429de3e,Microsoft Endpoint DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,UNIVERSAL_PRINT_01,795f6fe0-cc4d-4773-b050-5dde4dc704c9,Universal Print +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WIN10_PRO_ENT_SUB,21b439ba-a0ca-424f-a6cc-52f954a5b111,Windows 10/11 Enterprise (Original) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Windows_Autopatch,9a6eeb79-0b4b-4bf0-9808-39d99a2cd5a3,Windows Autopatch +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDOWSUPDATEFORBUSINESS_DEPLOYMENTSERVICE,7bf960f6-2cd9-443a-8046-5dbff9558365,Windows Update for Business Deployment Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM,6c57d4b6-3b23-47a5-9bc9-69f17b4947b3,Azure Information Protection Premium P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM2,5689bec4-755d-4753-8b61-40975025187c,Azure Information Protection Premium P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_ENTERPRISE,bea4c11e-220a-4e6d-8eb8-8ea15d019f90,Azure Rights Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DYN365_CDS_O365_P3,28b0fa46-c39a-4188-89e2-58e979a6b014,Common Data Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Defender_for_Iot_Enterprise,99cd49a9-0e54-4e07-aea1-d8d9f5f704f5,Defender for IoT - Enterprise IoT Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MFA_PREMIUM,8a256a2b-b617-496d-b51b-e76466e88db0,Microsoft Azure Multi-Factor Authentication +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_STANDALONE,2e2ddb96-6af9-4b1d-a3f0-d6ecfd22edb2,Microsoft Defender for Cloud Apps +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATA,14ab5db5-e6c4-4b20-b4bc-13e36fd2227f,Microsoft Defender for Identity +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM,41781fb2-bc02-4b7c-bd55-b576c07bb09d,Microsoft Entra ID P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM_P2,eec0eb4f-6444-4f95-aba0-50c24d67f998,Microsoft Entra ID P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 @@ -5127,6 +5217,11 @@ Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,DYN36 Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,DO NOT USE - AI Builder capacity Per User add-on Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Power_Pages_Internal_User,60bf28f9-2b70-4522-96f7-335f5e06c941,Power Pages Internal User +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,DYN365_CDS_P2,6ea4c1ef-c259-46df-bce2-943342cd3cb2,Common Data Service +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER_NEW,74d93933-6f22-436e-9441-66d205435abb,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,DYN365_CDS_P2_GOV,37396c73-2203-48e6-8be1-d882dae53275,Common Data Service for Government diff --git a/Modules/CIPPCore/Public/ConversionTable.csv b/Modules/CIPPCore/Public/ConversionTable.csv index 2658524b8cf2..9407f70d6cb7 100644 --- a/Modules/CIPPCore/Public/ConversionTable.csv +++ b/Modules/CIPPCore/Public/ConversionTable.csv @@ -240,6 +240,9 @@ Dynamics 365 Enterprise Edition - Additional Production Instance for Government, "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ENGINE_ADDON,24435e4b-87d0-4d7d-8beb-63a9b1573022,Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ADDON,2ba394e0-6f18-4b77-b45f-a5663bbab540,RETIRED - Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,CDS_FIELD_SERVICE_CONTRACTOR,f4614a66-d632-443a-bc77-afe92987b322,Common Data Service Field service Part Time Contractors +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,POWERAPPS_DYN_APPS,874fc546-6efe-4d22-90b8-5c4e7aa59f4b,Power Apps for Dynamics 365 Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,CDS_FIELD_SERVICE_CONTRACTOR_GCC,2457fe40-65be-48a1-935f-924ad6e62dba,Common Data Service Field service Part Time Contractors for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,EXCHANGE_S_FOUNDATION_GOV,922ba911-5694-4e99-a794-73aed9bfeec8,Exchange Foundation for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,POWERAPPS_DYN_APPS_GOV,3089c02b-e533-4b73-96a5-01fa648c3c3c,PowerApps for Dynamics 365 for Government @@ -2246,6 +2249,93 @@ Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,AAD_PREMIUM_P2,eec0 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_FOR_TEAMS,dcf9d2f4-772e-4434-b757-77a453cfbc02,Avatars for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_ADDITIONAL_FOR_TEAMS,3efbd4ed-8958-4824-8389-1321f8730af8,Avatars for Teams (additional) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CustomerLockboxA_Enterprise,3ec18638-bd4c-4d3b-8905-479ed636b83e,Customer Lockbox (A) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_S_ENTERPRISE,efb87545-963c-4e0d-99df-69c6916d9eb0,Exchange Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,GRAPH_CONNECTORS_SEARCH_INDEX,a6520331-d7d4-4276-95f5-15c0933bc757,Graph Connectors Search with Index +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_IMMERSIVE_FOR_TEAMS,f0ff6ac6-297d-49cd-be34-6dfef97f0c28,Immersive spaces for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFORMATION_BARRIERS,c4801e8a-cb58-4c35-aca6-f2dcc106f287,Information Barriers +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Content_Explorer,d9fa6af4-e046-4c89-9226-729a0786685d,Information Protection and Governance Analytics - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ContentExplorer_Standard,2b815d45-56e4-4e3a-b65c-66cb9175b560,Information Protection and Governance Analytics – Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP2,efb0351d-3b08-4503-993d-383af8de41e3,Information Protection for Office 365 - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP1,5136a095-5cf0-4aff-bec3-e84448b38ea5,Information Protection for Office 365 - Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MYANALYTICS_P2,33c4f319-9bdd-48d6-9c4d-410b750a4a5a,Insights by MyAnalytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_ADVANCED_AUDITING,2f442157-a11c-46b9-ae5b-6e39ff4e5849,Microsoft 365 Advanced Auditing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,OFFICESUBSCRIPTION,43de0ff5-c92c-492b-9116-175376d08c38,Microsoft 365 Apps for enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOMEETADV,3e26ee1f-8a5f-4d52-aee2-b81ce45c8f40,Microsoft 365 Audio Conferencing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_AUDIT_PLATFORM,f6de4823-28fa-440b-b886-4783fa86ddba,Microsoft 365 Audit Platform +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_COMMUNICATION_COMPLIANCE,a413a9ff-720c-4822-98ef-2f37c2a21f4c,Microsoft 365 Communication Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MTP,bf28f719-7844-4079-9c78-c1307898e192,Microsoft 365 Defender +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_LIGHTHOUSE_CUSTOMER_PLAN1,6f23d6a9-adbf-481c-8538-b4c095654487,Microsoft 365 Lighthouse (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOEV,4828c8ec-dc2e-4779-b502-87ac9ce28ab7,Microsoft 365 Phone System +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTBOOKINGS,199a5c09-e0ca-4e37-8f7c-b05d533e1ea2,Microsoft Bookings +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CLIPCHAMP,a1ace008-72f3-4ea0-8dac-33b3a23a2472,Microsoft Clipchamp +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_DLP,6dc145d6-95dd-4191-b9c3-185575ee6f6b,Microsoft Communications DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CUSTOMER_KEY,6db1f1db-2b46-403f-be40-e39395f08dbb,Microsoft Customer Key +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATP_ENTERPRISE,f20fedf3-f3c3-43c3-8267-2bfdd51c0939,Microsoft Defender for Office 365 (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,THREAT_INTELLIGENCE,8e0c0a52-6a6c-4d40-8370-dd62790dcd70,Microsoft Defender for Office 365 (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCEL_PREMIUM,531ee2f8-b1cb-453b-9c21-d2180d014ca5,Microsoft Excel Advanced Analytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FORMS_PLAN_E5,e212cbc7-0961-4c40-9825-01117710dcb1,Microsoft Forms (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFO_GOVERNANCE,e26c2fcc-ab91-4a61-b35c-03cdc8dddf66,Microsoft Information Governance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK,d587c7a3-bda9-4f99-8776-9bcf59c84f75,Microsoft Insider Risk Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK_MANAGEMENT,9d0c4ee5-e4a1-4625-ab39-d82b619b1a34,Microsoft Insider Risk Management - Exchange +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,KAIZALA_STANDALONE,0898bdbb-73b0-471a-81e5-20f1fe4dd66e,Microsoft Kaizala Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_LOOP,c4b8c31a-fb44-4c65-9837-a21f55fcabda,Microsoft Loop +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ML_CLASSIFICATION,d2d51368-76c9-4317-ada2-a12c004c432f,Microsoft ML-Based Classification +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_ANALYTICS,34c0d7a0-a70f-4668-9238-47f9fc208882,Microsoft MyAnalytics (Full) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECTWORKMANAGEMENT,b737dad2-2f6c-4c65-90e3-ca563267e8b9,Microsoft Planner +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RECORDS_MANAGEMENT,65cc641f-cccd-4643-97e0-a17e3045e541,Microsoft Records Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_SEARCH,94065c59-bc8e-4e8b-89e5-5138d471eaff,Microsoft Search +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Deskless,8c7d2df8-86f0-4902-b2ed-a0458298f3b3,Microsoft StaffHub +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,STREAM_O365_E5,6c6042f5-6f01-4d67-b8c1-eb99d36eed3e,Microsoft Stream for Office 365 E5 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_O365,882e1d05-acd1-4ccb-8708-6ee03664b117,Mobile Device Management for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Nucleus,db4d623d-b514-490b-b7ef-8885eee514de,Nucleus +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EQUIVIO_ANALYTICS,4de31727-a228-4ec3-a5bf-8e45b5ca48cc,Office 365 Advanced eDiscovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_O365,8c098270-9dd4-4350-9b30-ba4703f3b36b,Office 365 Cloud App Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PAM_ENTERPRISE,b1188c4c-1b36-4018-b48b-ee07604f6feb,Office 365 Privileged Access Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SAFEDOCS,bf6f5520-59e3-4f82-974b-7dbbc4fd27c7,Office 365 SafeDocs +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTWAC,e95bec33-7c88-4a70-8e19-b10bd9d0c014,Office for the Web +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PEOPLE_SKILLS_FOUNDATION,13b6da2c-0d84-450e-9f69-a33e221387ca,People Skills - Foundation +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWERAPPS_O365_P3,9c0dab89-a30c-4117-86e7-97bda240acd2,Power Apps for Office 365 (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BI_AZURE_P2,70d33638-9c74-4d01-bfd3-562de28bd4ba,Power BI Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PREMIUM_ENCRYPTION,617b097b-4b93-4ede-83de-5f075bb5fb2f,Premium Encryption in Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECT_O365_P3,b21a6b06-1988-436e-a07b-51ec6d9f52ad,Project for Office (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PURVIEW_DISCOVERY,c948ea65-2053-4a5a-8a62-9eaaaf11b522,Purview Discovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Bing_Chat_Enterprise,0d0c0d31-fae7-41f2-b909-eaf4d7f26dba,RETIRED - Commercial data protection for Microsoft Copilot +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_COMPLIANCE,41fcdd7d-4733-4863-9cf4-c65b83ce2df4,RETIRED - Microsoft Communications Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DATA_INVESTIGATIONS,46129a58-a698-46f0-aa5b-17f6586297d9,Retired - Microsoft Data Investigations +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PLACES_CORE,1fe6227d-3e01-46d0-9510-0acad4ff6e94,RETIRED - Places Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTENTERPRISE,5dbe027f-2339-4123-9542-606e4d348a72,SharePoint (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOSTANDARD,0feaeb32-d00e-4d66-bd5a-43b5b83db82c,Skype for Business Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SWAY,a23b959c-7ce8-4e57-9140-b90eb88a9e97,Sway +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BPOS_S_TODO_3,3fb82609-8c27-4f7b-bd51-30634711ee67,To-Do (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVAENGAGE_CORE,a82fbf69-b4d7-49f4-83a6-915b2cf354f4,Viva Engage Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVA_LEARNING_SEEDED,b76fb638-6ba6-402a-b9f9-83d28acb3d86,Viva Learning Seeded +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WHITEBOARD_PLAN3,4a51bca5-1eff-43f5-878c-177680f191af,Whiteboard (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,YAMMER_ENTERPRISE,7547a3fe-08ee-4ccb-b430-5077c5041653,Yammer Enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDEFATP,871d91ec-ec1a-452b-a83f-bd76c7d770ef,Microsoft Defender for Endpoint +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTENDPOINTDLP,64bfac92-2b17-4482-b5e5-a0304429de3e,Microsoft Endpoint DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,UNIVERSAL_PRINT_01,795f6fe0-cc4d-4773-b050-5dde4dc704c9,Universal Print +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WIN10_PRO_ENT_SUB,21b439ba-a0ca-424f-a6cc-52f954a5b111,Windows 10/11 Enterprise (Original) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Windows_Autopatch,9a6eeb79-0b4b-4bf0-9808-39d99a2cd5a3,Windows Autopatch +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDOWSUPDATEFORBUSINESS_DEPLOYMENTSERVICE,7bf960f6-2cd9-443a-8046-5dbff9558365,Windows Update for Business Deployment Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM,6c57d4b6-3b23-47a5-9bc9-69f17b4947b3,Azure Information Protection Premium P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM2,5689bec4-755d-4753-8b61-40975025187c,Azure Information Protection Premium P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_ENTERPRISE,bea4c11e-220a-4e6d-8eb8-8ea15d019f90,Azure Rights Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DYN365_CDS_O365_P3,28b0fa46-c39a-4188-89e2-58e979a6b014,Common Data Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Defender_for_Iot_Enterprise,99cd49a9-0e54-4e07-aea1-d8d9f5f704f5,Defender for IoT - Enterprise IoT Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MFA_PREMIUM,8a256a2b-b617-496d-b51b-e76466e88db0,Microsoft Azure Multi-Factor Authentication +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_STANDALONE,2e2ddb96-6af9-4b1d-a3f0-d6ecfd22edb2,Microsoft Defender for Cloud Apps +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATA,14ab5db5-e6c4-4b20-b4bc-13e36fd2227f,Microsoft Defender for Identity +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM,41781fb2-bc02-4b7c-bd55-b576c07bb09d,Microsoft Entra ID P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM_P2,eec0eb4f-6444-4f95-aba0-50c24d67f998,Microsoft Entra ID P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 @@ -5127,6 +5217,11 @@ Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,DYN36 Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,DO NOT USE - AI Builder capacity Per User add-on Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Power_Pages_Internal_User,60bf28f9-2b70-4522-96f7-335f5e06c941,Power Pages Internal User +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,DYN365_CDS_P2,6ea4c1ef-c259-46df-bce2-943342cd3cb2,Common Data Service +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER_NEW,74d93933-6f22-436e-9441-66d205435abb,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,DYN365_CDS_P2_GOV,37396c73-2203-48e6-8be1-d882dae53275,Common Data Service for Government diff --git a/Modules/CippExtensions/ConversionTable.csv b/Modules/CippExtensions/ConversionTable.csv index 2658524b8cf2..9407f70d6cb7 100644 --- a/Modules/CippExtensions/ConversionTable.csv +++ b/Modules/CippExtensions/ConversionTable.csv @@ -240,6 +240,9 @@ Dynamics 365 Enterprise Edition - Additional Production Instance for Government, "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ENGINE_ADDON,24435e4b-87d0-4d7d-8beb-63a9b1573022,Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ADDON,2ba394e0-6f18-4b77-b45f-a5663bbab540,RETIRED - Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,CDS_FIELD_SERVICE_CONTRACTOR,f4614a66-d632-443a-bc77-afe92987b322,Common Data Service Field service Part Time Contractors +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,POWERAPPS_DYN_APPS,874fc546-6efe-4d22-90b8-5c4e7aa59f4b,Power Apps for Dynamics 365 Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,CDS_FIELD_SERVICE_CONTRACTOR_GCC,2457fe40-65be-48a1-935f-924ad6e62dba,Common Data Service Field service Part Time Contractors for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,EXCHANGE_S_FOUNDATION_GOV,922ba911-5694-4e99-a794-73aed9bfeec8,Exchange Foundation for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,POWERAPPS_DYN_APPS_GOV,3089c02b-e533-4b73-96a5-01fa648c3c3c,PowerApps for Dynamics 365 for Government @@ -2246,6 +2249,93 @@ Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,AAD_PREMIUM_P2,eec0 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_FOR_TEAMS,dcf9d2f4-772e-4434-b757-77a453cfbc02,Avatars for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_ADDITIONAL_FOR_TEAMS,3efbd4ed-8958-4824-8389-1321f8730af8,Avatars for Teams (additional) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CustomerLockboxA_Enterprise,3ec18638-bd4c-4d3b-8905-479ed636b83e,Customer Lockbox (A) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_S_ENTERPRISE,efb87545-963c-4e0d-99df-69c6916d9eb0,Exchange Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,GRAPH_CONNECTORS_SEARCH_INDEX,a6520331-d7d4-4276-95f5-15c0933bc757,Graph Connectors Search with Index +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_IMMERSIVE_FOR_TEAMS,f0ff6ac6-297d-49cd-be34-6dfef97f0c28,Immersive spaces for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFORMATION_BARRIERS,c4801e8a-cb58-4c35-aca6-f2dcc106f287,Information Barriers +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Content_Explorer,d9fa6af4-e046-4c89-9226-729a0786685d,Information Protection and Governance Analytics - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ContentExplorer_Standard,2b815d45-56e4-4e3a-b65c-66cb9175b560,Information Protection and Governance Analytics – Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP2,efb0351d-3b08-4503-993d-383af8de41e3,Information Protection for Office 365 - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP1,5136a095-5cf0-4aff-bec3-e84448b38ea5,Information Protection for Office 365 - Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MYANALYTICS_P2,33c4f319-9bdd-48d6-9c4d-410b750a4a5a,Insights by MyAnalytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_ADVANCED_AUDITING,2f442157-a11c-46b9-ae5b-6e39ff4e5849,Microsoft 365 Advanced Auditing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,OFFICESUBSCRIPTION,43de0ff5-c92c-492b-9116-175376d08c38,Microsoft 365 Apps for enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOMEETADV,3e26ee1f-8a5f-4d52-aee2-b81ce45c8f40,Microsoft 365 Audio Conferencing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_AUDIT_PLATFORM,f6de4823-28fa-440b-b886-4783fa86ddba,Microsoft 365 Audit Platform +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_COMMUNICATION_COMPLIANCE,a413a9ff-720c-4822-98ef-2f37c2a21f4c,Microsoft 365 Communication Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MTP,bf28f719-7844-4079-9c78-c1307898e192,Microsoft 365 Defender +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_LIGHTHOUSE_CUSTOMER_PLAN1,6f23d6a9-adbf-481c-8538-b4c095654487,Microsoft 365 Lighthouse (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOEV,4828c8ec-dc2e-4779-b502-87ac9ce28ab7,Microsoft 365 Phone System +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTBOOKINGS,199a5c09-e0ca-4e37-8f7c-b05d533e1ea2,Microsoft Bookings +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CLIPCHAMP,a1ace008-72f3-4ea0-8dac-33b3a23a2472,Microsoft Clipchamp +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_DLP,6dc145d6-95dd-4191-b9c3-185575ee6f6b,Microsoft Communications DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CUSTOMER_KEY,6db1f1db-2b46-403f-be40-e39395f08dbb,Microsoft Customer Key +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATP_ENTERPRISE,f20fedf3-f3c3-43c3-8267-2bfdd51c0939,Microsoft Defender for Office 365 (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,THREAT_INTELLIGENCE,8e0c0a52-6a6c-4d40-8370-dd62790dcd70,Microsoft Defender for Office 365 (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCEL_PREMIUM,531ee2f8-b1cb-453b-9c21-d2180d014ca5,Microsoft Excel Advanced Analytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FORMS_PLAN_E5,e212cbc7-0961-4c40-9825-01117710dcb1,Microsoft Forms (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFO_GOVERNANCE,e26c2fcc-ab91-4a61-b35c-03cdc8dddf66,Microsoft Information Governance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK,d587c7a3-bda9-4f99-8776-9bcf59c84f75,Microsoft Insider Risk Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK_MANAGEMENT,9d0c4ee5-e4a1-4625-ab39-d82b619b1a34,Microsoft Insider Risk Management - Exchange +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,KAIZALA_STANDALONE,0898bdbb-73b0-471a-81e5-20f1fe4dd66e,Microsoft Kaizala Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_LOOP,c4b8c31a-fb44-4c65-9837-a21f55fcabda,Microsoft Loop +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ML_CLASSIFICATION,d2d51368-76c9-4317-ada2-a12c004c432f,Microsoft ML-Based Classification +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_ANALYTICS,34c0d7a0-a70f-4668-9238-47f9fc208882,Microsoft MyAnalytics (Full) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECTWORKMANAGEMENT,b737dad2-2f6c-4c65-90e3-ca563267e8b9,Microsoft Planner +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RECORDS_MANAGEMENT,65cc641f-cccd-4643-97e0-a17e3045e541,Microsoft Records Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_SEARCH,94065c59-bc8e-4e8b-89e5-5138d471eaff,Microsoft Search +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Deskless,8c7d2df8-86f0-4902-b2ed-a0458298f3b3,Microsoft StaffHub +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,STREAM_O365_E5,6c6042f5-6f01-4d67-b8c1-eb99d36eed3e,Microsoft Stream for Office 365 E5 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_O365,882e1d05-acd1-4ccb-8708-6ee03664b117,Mobile Device Management for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Nucleus,db4d623d-b514-490b-b7ef-8885eee514de,Nucleus +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EQUIVIO_ANALYTICS,4de31727-a228-4ec3-a5bf-8e45b5ca48cc,Office 365 Advanced eDiscovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_O365,8c098270-9dd4-4350-9b30-ba4703f3b36b,Office 365 Cloud App Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PAM_ENTERPRISE,b1188c4c-1b36-4018-b48b-ee07604f6feb,Office 365 Privileged Access Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SAFEDOCS,bf6f5520-59e3-4f82-974b-7dbbc4fd27c7,Office 365 SafeDocs +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTWAC,e95bec33-7c88-4a70-8e19-b10bd9d0c014,Office for the Web +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PEOPLE_SKILLS_FOUNDATION,13b6da2c-0d84-450e-9f69-a33e221387ca,People Skills - Foundation +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWERAPPS_O365_P3,9c0dab89-a30c-4117-86e7-97bda240acd2,Power Apps for Office 365 (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BI_AZURE_P2,70d33638-9c74-4d01-bfd3-562de28bd4ba,Power BI Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PREMIUM_ENCRYPTION,617b097b-4b93-4ede-83de-5f075bb5fb2f,Premium Encryption in Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECT_O365_P3,b21a6b06-1988-436e-a07b-51ec6d9f52ad,Project for Office (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PURVIEW_DISCOVERY,c948ea65-2053-4a5a-8a62-9eaaaf11b522,Purview Discovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Bing_Chat_Enterprise,0d0c0d31-fae7-41f2-b909-eaf4d7f26dba,RETIRED - Commercial data protection for Microsoft Copilot +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_COMPLIANCE,41fcdd7d-4733-4863-9cf4-c65b83ce2df4,RETIRED - Microsoft Communications Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DATA_INVESTIGATIONS,46129a58-a698-46f0-aa5b-17f6586297d9,Retired - Microsoft Data Investigations +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PLACES_CORE,1fe6227d-3e01-46d0-9510-0acad4ff6e94,RETIRED - Places Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTENTERPRISE,5dbe027f-2339-4123-9542-606e4d348a72,SharePoint (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOSTANDARD,0feaeb32-d00e-4d66-bd5a-43b5b83db82c,Skype for Business Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SWAY,a23b959c-7ce8-4e57-9140-b90eb88a9e97,Sway +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BPOS_S_TODO_3,3fb82609-8c27-4f7b-bd51-30634711ee67,To-Do (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVAENGAGE_CORE,a82fbf69-b4d7-49f4-83a6-915b2cf354f4,Viva Engage Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVA_LEARNING_SEEDED,b76fb638-6ba6-402a-b9f9-83d28acb3d86,Viva Learning Seeded +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WHITEBOARD_PLAN3,4a51bca5-1eff-43f5-878c-177680f191af,Whiteboard (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,YAMMER_ENTERPRISE,7547a3fe-08ee-4ccb-b430-5077c5041653,Yammer Enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDEFATP,871d91ec-ec1a-452b-a83f-bd76c7d770ef,Microsoft Defender for Endpoint +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTENDPOINTDLP,64bfac92-2b17-4482-b5e5-a0304429de3e,Microsoft Endpoint DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,UNIVERSAL_PRINT_01,795f6fe0-cc4d-4773-b050-5dde4dc704c9,Universal Print +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WIN10_PRO_ENT_SUB,21b439ba-a0ca-424f-a6cc-52f954a5b111,Windows 10/11 Enterprise (Original) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Windows_Autopatch,9a6eeb79-0b4b-4bf0-9808-39d99a2cd5a3,Windows Autopatch +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDOWSUPDATEFORBUSINESS_DEPLOYMENTSERVICE,7bf960f6-2cd9-443a-8046-5dbff9558365,Windows Update for Business Deployment Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM,6c57d4b6-3b23-47a5-9bc9-69f17b4947b3,Azure Information Protection Premium P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM2,5689bec4-755d-4753-8b61-40975025187c,Azure Information Protection Premium P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_ENTERPRISE,bea4c11e-220a-4e6d-8eb8-8ea15d019f90,Azure Rights Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DYN365_CDS_O365_P3,28b0fa46-c39a-4188-89e2-58e979a6b014,Common Data Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Defender_for_Iot_Enterprise,99cd49a9-0e54-4e07-aea1-d8d9f5f704f5,Defender for IoT - Enterprise IoT Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MFA_PREMIUM,8a256a2b-b617-496d-b51b-e76466e88db0,Microsoft Azure Multi-Factor Authentication +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_STANDALONE,2e2ddb96-6af9-4b1d-a3f0-d6ecfd22edb2,Microsoft Defender for Cloud Apps +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATA,14ab5db5-e6c4-4b20-b4bc-13e36fd2227f,Microsoft Defender for Identity +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM,41781fb2-bc02-4b7c-bd55-b576c07bb09d,Microsoft Entra ID P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM_P2,eec0eb4f-6444-4f95-aba0-50c24d67f998,Microsoft Entra ID P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 @@ -5127,6 +5217,11 @@ Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,DYN36 Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,DO NOT USE - AI Builder capacity Per User add-on Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Power_Pages_Internal_User,60bf28f9-2b70-4522-96f7-335f5e06c941,Power Pages Internal User +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,DYN365_CDS_P2,6ea4c1ef-c259-46df-bce2-943342cd3cb2,Common Data Service +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER_NEW,74d93933-6f22-436e-9441-66d205435abb,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,DYN365_CDS_P2_GOV,37396c73-2203-48e6-8be1-d882dae53275,Common Data Service for Government diff --git a/Modules/CippExtensions/Public/ConversionTable.csv b/Modules/CippExtensions/Public/ConversionTable.csv index 2658524b8cf2..9407f70d6cb7 100644 --- a/Modules/CippExtensions/Public/ConversionTable.csv +++ b/Modules/CippExtensions/Public/ConversionTable.csv @@ -240,6 +240,9 @@ Dynamics 365 Enterprise Edition - Additional Production Instance for Government, "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ENGINE_ADDON,24435e4b-87d0-4d7d-8beb-63a9b1573022,Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ADDON,2ba394e0-6f18-4b77-b45f-a5663bbab540,RETIRED - Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,CDS_FIELD_SERVICE_CONTRACTOR,f4614a66-d632-443a-bc77-afe92987b322,Common Data Service Field service Part Time Contractors +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,POWERAPPS_DYN_APPS,874fc546-6efe-4d22-90b8-5c4e7aa59f4b,Power Apps for Dynamics 365 Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,CDS_FIELD_SERVICE_CONTRACTOR_GCC,2457fe40-65be-48a1-935f-924ad6e62dba,Common Data Service Field service Part Time Contractors for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,EXCHANGE_S_FOUNDATION_GOV,922ba911-5694-4e99-a794-73aed9bfeec8,Exchange Foundation for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,POWERAPPS_DYN_APPS_GOV,3089c02b-e533-4b73-96a5-01fa648c3c3c,PowerApps for Dynamics 365 for Government @@ -2246,6 +2249,93 @@ Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,AAD_PREMIUM_P2,eec0 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_FOR_TEAMS,dcf9d2f4-772e-4434-b757-77a453cfbc02,Avatars for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_ADDITIONAL_FOR_TEAMS,3efbd4ed-8958-4824-8389-1321f8730af8,Avatars for Teams (additional) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CustomerLockboxA_Enterprise,3ec18638-bd4c-4d3b-8905-479ed636b83e,Customer Lockbox (A) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_S_ENTERPRISE,efb87545-963c-4e0d-99df-69c6916d9eb0,Exchange Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,GRAPH_CONNECTORS_SEARCH_INDEX,a6520331-d7d4-4276-95f5-15c0933bc757,Graph Connectors Search with Index +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_IMMERSIVE_FOR_TEAMS,f0ff6ac6-297d-49cd-be34-6dfef97f0c28,Immersive spaces for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFORMATION_BARRIERS,c4801e8a-cb58-4c35-aca6-f2dcc106f287,Information Barriers +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Content_Explorer,d9fa6af4-e046-4c89-9226-729a0786685d,Information Protection and Governance Analytics - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ContentExplorer_Standard,2b815d45-56e4-4e3a-b65c-66cb9175b560,Information Protection and Governance Analytics – Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP2,efb0351d-3b08-4503-993d-383af8de41e3,Information Protection for Office 365 - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP1,5136a095-5cf0-4aff-bec3-e84448b38ea5,Information Protection for Office 365 - Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MYANALYTICS_P2,33c4f319-9bdd-48d6-9c4d-410b750a4a5a,Insights by MyAnalytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_ADVANCED_AUDITING,2f442157-a11c-46b9-ae5b-6e39ff4e5849,Microsoft 365 Advanced Auditing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,OFFICESUBSCRIPTION,43de0ff5-c92c-492b-9116-175376d08c38,Microsoft 365 Apps for enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOMEETADV,3e26ee1f-8a5f-4d52-aee2-b81ce45c8f40,Microsoft 365 Audio Conferencing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_AUDIT_PLATFORM,f6de4823-28fa-440b-b886-4783fa86ddba,Microsoft 365 Audit Platform +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_COMMUNICATION_COMPLIANCE,a413a9ff-720c-4822-98ef-2f37c2a21f4c,Microsoft 365 Communication Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MTP,bf28f719-7844-4079-9c78-c1307898e192,Microsoft 365 Defender +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_LIGHTHOUSE_CUSTOMER_PLAN1,6f23d6a9-adbf-481c-8538-b4c095654487,Microsoft 365 Lighthouse (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOEV,4828c8ec-dc2e-4779-b502-87ac9ce28ab7,Microsoft 365 Phone System +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTBOOKINGS,199a5c09-e0ca-4e37-8f7c-b05d533e1ea2,Microsoft Bookings +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CLIPCHAMP,a1ace008-72f3-4ea0-8dac-33b3a23a2472,Microsoft Clipchamp +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_DLP,6dc145d6-95dd-4191-b9c3-185575ee6f6b,Microsoft Communications DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CUSTOMER_KEY,6db1f1db-2b46-403f-be40-e39395f08dbb,Microsoft Customer Key +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATP_ENTERPRISE,f20fedf3-f3c3-43c3-8267-2bfdd51c0939,Microsoft Defender for Office 365 (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,THREAT_INTELLIGENCE,8e0c0a52-6a6c-4d40-8370-dd62790dcd70,Microsoft Defender for Office 365 (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCEL_PREMIUM,531ee2f8-b1cb-453b-9c21-d2180d014ca5,Microsoft Excel Advanced Analytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FORMS_PLAN_E5,e212cbc7-0961-4c40-9825-01117710dcb1,Microsoft Forms (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFO_GOVERNANCE,e26c2fcc-ab91-4a61-b35c-03cdc8dddf66,Microsoft Information Governance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK,d587c7a3-bda9-4f99-8776-9bcf59c84f75,Microsoft Insider Risk Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK_MANAGEMENT,9d0c4ee5-e4a1-4625-ab39-d82b619b1a34,Microsoft Insider Risk Management - Exchange +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,KAIZALA_STANDALONE,0898bdbb-73b0-471a-81e5-20f1fe4dd66e,Microsoft Kaizala Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_LOOP,c4b8c31a-fb44-4c65-9837-a21f55fcabda,Microsoft Loop +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ML_CLASSIFICATION,d2d51368-76c9-4317-ada2-a12c004c432f,Microsoft ML-Based Classification +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_ANALYTICS,34c0d7a0-a70f-4668-9238-47f9fc208882,Microsoft MyAnalytics (Full) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECTWORKMANAGEMENT,b737dad2-2f6c-4c65-90e3-ca563267e8b9,Microsoft Planner +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RECORDS_MANAGEMENT,65cc641f-cccd-4643-97e0-a17e3045e541,Microsoft Records Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_SEARCH,94065c59-bc8e-4e8b-89e5-5138d471eaff,Microsoft Search +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Deskless,8c7d2df8-86f0-4902-b2ed-a0458298f3b3,Microsoft StaffHub +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,STREAM_O365_E5,6c6042f5-6f01-4d67-b8c1-eb99d36eed3e,Microsoft Stream for Office 365 E5 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_O365,882e1d05-acd1-4ccb-8708-6ee03664b117,Mobile Device Management for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Nucleus,db4d623d-b514-490b-b7ef-8885eee514de,Nucleus +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EQUIVIO_ANALYTICS,4de31727-a228-4ec3-a5bf-8e45b5ca48cc,Office 365 Advanced eDiscovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_O365,8c098270-9dd4-4350-9b30-ba4703f3b36b,Office 365 Cloud App Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PAM_ENTERPRISE,b1188c4c-1b36-4018-b48b-ee07604f6feb,Office 365 Privileged Access Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SAFEDOCS,bf6f5520-59e3-4f82-974b-7dbbc4fd27c7,Office 365 SafeDocs +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTWAC,e95bec33-7c88-4a70-8e19-b10bd9d0c014,Office for the Web +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PEOPLE_SKILLS_FOUNDATION,13b6da2c-0d84-450e-9f69-a33e221387ca,People Skills - Foundation +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWERAPPS_O365_P3,9c0dab89-a30c-4117-86e7-97bda240acd2,Power Apps for Office 365 (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BI_AZURE_P2,70d33638-9c74-4d01-bfd3-562de28bd4ba,Power BI Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PREMIUM_ENCRYPTION,617b097b-4b93-4ede-83de-5f075bb5fb2f,Premium Encryption in Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECT_O365_P3,b21a6b06-1988-436e-a07b-51ec6d9f52ad,Project for Office (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PURVIEW_DISCOVERY,c948ea65-2053-4a5a-8a62-9eaaaf11b522,Purview Discovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Bing_Chat_Enterprise,0d0c0d31-fae7-41f2-b909-eaf4d7f26dba,RETIRED - Commercial data protection for Microsoft Copilot +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_COMPLIANCE,41fcdd7d-4733-4863-9cf4-c65b83ce2df4,RETIRED - Microsoft Communications Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DATA_INVESTIGATIONS,46129a58-a698-46f0-aa5b-17f6586297d9,Retired - Microsoft Data Investigations +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PLACES_CORE,1fe6227d-3e01-46d0-9510-0acad4ff6e94,RETIRED - Places Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTENTERPRISE,5dbe027f-2339-4123-9542-606e4d348a72,SharePoint (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOSTANDARD,0feaeb32-d00e-4d66-bd5a-43b5b83db82c,Skype for Business Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SWAY,a23b959c-7ce8-4e57-9140-b90eb88a9e97,Sway +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BPOS_S_TODO_3,3fb82609-8c27-4f7b-bd51-30634711ee67,To-Do (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVAENGAGE_CORE,a82fbf69-b4d7-49f4-83a6-915b2cf354f4,Viva Engage Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVA_LEARNING_SEEDED,b76fb638-6ba6-402a-b9f9-83d28acb3d86,Viva Learning Seeded +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WHITEBOARD_PLAN3,4a51bca5-1eff-43f5-878c-177680f191af,Whiteboard (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,YAMMER_ENTERPRISE,7547a3fe-08ee-4ccb-b430-5077c5041653,Yammer Enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDEFATP,871d91ec-ec1a-452b-a83f-bd76c7d770ef,Microsoft Defender for Endpoint +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTENDPOINTDLP,64bfac92-2b17-4482-b5e5-a0304429de3e,Microsoft Endpoint DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,UNIVERSAL_PRINT_01,795f6fe0-cc4d-4773-b050-5dde4dc704c9,Universal Print +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WIN10_PRO_ENT_SUB,21b439ba-a0ca-424f-a6cc-52f954a5b111,Windows 10/11 Enterprise (Original) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Windows_Autopatch,9a6eeb79-0b4b-4bf0-9808-39d99a2cd5a3,Windows Autopatch +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDOWSUPDATEFORBUSINESS_DEPLOYMENTSERVICE,7bf960f6-2cd9-443a-8046-5dbff9558365,Windows Update for Business Deployment Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM,6c57d4b6-3b23-47a5-9bc9-69f17b4947b3,Azure Information Protection Premium P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM2,5689bec4-755d-4753-8b61-40975025187c,Azure Information Protection Premium P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_ENTERPRISE,bea4c11e-220a-4e6d-8eb8-8ea15d019f90,Azure Rights Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DYN365_CDS_O365_P3,28b0fa46-c39a-4188-89e2-58e979a6b014,Common Data Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Defender_for_Iot_Enterprise,99cd49a9-0e54-4e07-aea1-d8d9f5f704f5,Defender for IoT - Enterprise IoT Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MFA_PREMIUM,8a256a2b-b617-496d-b51b-e76466e88db0,Microsoft Azure Multi-Factor Authentication +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_STANDALONE,2e2ddb96-6af9-4b1d-a3f0-d6ecfd22edb2,Microsoft Defender for Cloud Apps +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATA,14ab5db5-e6c4-4b20-b4bc-13e36fd2227f,Microsoft Defender for Identity +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM,41781fb2-bc02-4b7c-bd55-b576c07bb09d,Microsoft Entra ID P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM_P2,eec0eb4f-6444-4f95-aba0-50c24d67f998,Microsoft Entra ID P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 @@ -5127,6 +5217,11 @@ Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,DYN36 Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,DO NOT USE - AI Builder capacity Per User add-on Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Power_Pages_Internal_User,60bf28f9-2b70-4522-96f7-335f5e06c941,Power Pages Internal User +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,DYN365_CDS_P2,6ea4c1ef-c259-46df-bce2-943342cd3cb2,Common Data Service +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER_NEW,74d93933-6f22-436e-9441-66d205435abb,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,DYN365_CDS_P2_GOV,37396c73-2203-48e6-8be1-d882dae53275,Common Data Service for Government From 625675ba4e266251d1da732775e4a990eb2ab812 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 24 Jul 2025 10:50:35 -0400 Subject: [PATCH 22/85] catch bobby tables exception on breaches --- .../Public/HIBP/New-BreachTenantSearch.ps1 | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Modules/CippExtensions/Public/HIBP/New-BreachTenantSearch.ps1 b/Modules/CippExtensions/Public/HIBP/New-BreachTenantSearch.ps1 index 96ce636256d0..971111aa4003 100644 --- a/Modules/CippExtensions/Public/HIBP/New-BreachTenantSearch.ps1 +++ b/Modules/CippExtensions/Public/HIBP/New-BreachTenantSearch.ps1 @@ -30,7 +30,12 @@ function New-BreachTenantSearch { #Add user breaches to table if ($usersResults) { - $entity = Add-CIPPAzDataTableEntity @Table -Entity $usersResults -Force - return $LatestBreach.Result + try { + $null = Add-CIPPAzDataTableEntity @Table -Entity $usersResults -Force + return $LatestBreach.Result + } catch { + Write-Error "Failed to add breaches to table: $($_.Exception.Message)" + return $null + } } } From 6498a41dcfa536d0d308797517b1b60ace16b327 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 24 Jul 2025 12:15:23 -0400 Subject: [PATCH 23/85] search tweaks --- .../AuditLogs/New-CippAuditLogSearch.ps1 | 2 +- .../Alerts/Invoke-ExecAuditLogSearch.ps1 | 30 ++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/AuditLogs/New-CippAuditLogSearch.ps1 b/Modules/CIPPCore/Public/AuditLogs/New-CippAuditLogSearch.ps1 index a840dac13486..2cbbd76e9e8d 100644 --- a/Modules/CIPPCore/Public/AuditLogs/New-CippAuditLogSearch.ps1 +++ b/Modules/CIPPCore/Public/AuditLogs/New-CippAuditLogSearch.ps1 @@ -125,7 +125,7 @@ function New-CippAuditLogSearch { ) $SearchParams = @{ - displayName = 'CIPP Audit Search - ' + (Get-Date).ToString('yyyy-MM-dd HH:mm:ss') + displayName = $DisplayName filterStartDateTime = $StartTime.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss') filterEndDateTime = $EndTime.AddHours(1).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss') } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 index dc54f2259f53..f66af08ebffe 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 @@ -28,6 +28,19 @@ function Invoke-ExecAuditLogSearch { return } + # Convert StartTime and EndTime to DateTime from unixtime + if ($Query.StartTime -match '^\d+$') { + $Query.StartTime = [DateTime]::UnixEpoch.AddSeconds([long]$Query.StartTime) + } else { + $Query.StartTime = [DateTime]$Query.StartTime + } + + if ($Query.EndTime -match '^\d+$') { + $Query.EndTime = [DateTime]::UnixEpoch.AddSeconds([long]$Query.EndTime) + } else { + $Query.EndTime = [DateTime]$Query.EndTime + } + $Command = Get-Command New-CippAuditLogSearch $AvailableParameters = $Command.Parameters.Keys $BadProps = foreach ($Prop in $Query.PSObject.Properties.Name) { @@ -44,8 +57,23 @@ function Invoke-ExecAuditLogSearch { } try { + Write-Information "Executing audit log search with parameters: $($Query | ConvertTo-Json -Depth 10)" + $Query = $Query | ConvertTo-Json -Depth 10 | ConvertFrom-Json -AsHashtable - $Results = New-CippAuditLogSearch @Query + $NewSearch = New-CippAuditLogSearch @Query + + if ($NewSearch) { + $Results = @{ + resultText = "Created audit log search: $($NewSearch.displayName)" + state = 'success' + details = $NewSearch + } + } else { + $Results = @{ + resultText = 'Failed to initiate search' + state = 'error' + } + } Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Results From ca57d38564d6a103bb08446a34341d6ab44b9bd9 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 24 Jul 2025 12:39:16 -0400 Subject: [PATCH 24/85] allow other pending statuses in capability check --- Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 b/Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 index 64754d6a980e..196568107230 100644 --- a/Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 @@ -21,10 +21,10 @@ function Get-CIPPTenantCapabilities { } Add-CIPPAzDataTableEntity @ConfigTable -Entity $Entity -Force } - $Plans = $Org.servicePlans | Where-Object { $_.provisioningStatus -eq 'Success' -or $_.provisioningStatus -eq 'PendingInput' } | Sort-Object -Property serviceplanName -Unique | Select-Object servicePlanName, provisioningStatus + $Plans = $Org.servicePlans | Where-Object { $_.provisioningStatus -ne 'disabled' } | Sort-Object -Property serviceplanName -Unique | Select-Object servicePlanName, provisioningStatus $Results = @{} foreach ($Plan in $Plans) { - $Results."$($Plan.servicePlanName)" = $Plan.provisioningStatus -eq 'Success' -or $Plan.provisioningStatus -eq 'PendingInput' + $Results."$($Plan.servicePlanName)" = $Plan.provisioningStatus -ne 'disabled' } [PSCustomObject]$Results } From 63f39b31d07690df1c628df727b60c3a83c56e69 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 24 Jul 2025 16:18:26 -0400 Subject: [PATCH 25/85] sort search results --- .../CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 b/Modules/CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 index 2ced11b10908..ecff809d72b2 100644 --- a/Modules/CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 +++ b/Modules/CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 @@ -28,6 +28,6 @@ function Get-CippAuditLogSearchResults { $GraphRequest.CountOnly = $true } - New-GraphGetRequest @GraphRequest -ErrorAction Stop + New-GraphGetRequest @GraphRequest -ErrorAction Stop | Sort-Object -Property createdDateTime -Descending } } From 7a63fe1ce17ba46012e71460160991a1b82a965b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 24 Jul 2025 21:31:59 -0400 Subject: [PATCH 26/85] delta queries --- .../DeltaQueries/New-GraphDeltaQuery.ps1 | 64 +++++++++++++++++++ .../GraphHelper/New-GraphGetRequest.ps1 | 24 ++++++- 2 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 Modules/CIPPCore/Public/DeltaQueries/New-GraphDeltaQuery.ps1 diff --git a/Modules/CIPPCore/Public/DeltaQueries/New-GraphDeltaQuery.ps1 b/Modules/CIPPCore/Public/DeltaQueries/New-GraphDeltaQuery.ps1 new file mode 100644 index 000000000000..9ec6c2bbdd0c --- /dev/null +++ b/Modules/CIPPCore/Public/DeltaQueries/New-GraphDeltaQuery.ps1 @@ -0,0 +1,64 @@ +function New-GraphDeltaQuery { + <# + .SYNOPSIS + Creates a new Graph Delta Query. + .DESCRIPTION + This function creates a new Graph Delta Query to track changes in a specified resource. + .PARAMETER Resource + The resource to track changes for (e.g., 'users', 'groups'). + .PARAMETER TenantFilter + The tenant to filter the query on. + #> + [CmdletBinding(DefaultParameterSetName = 'NewDeltaQuery')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'NewDeltaQuery')] + [Parameter(Mandatory = $true, ParameterSetName = 'DeltaUrl')] + [string]$TenantFilter, + + [Parameter(ParameterSetName = 'NewDeltaQuery', Mandatory = $true)] + [ValidateSet('users', 'groups', 'contacts', 'devices', 'applications', 'servicePrincipals', 'directoryObjects', 'administrativeUnits')] + [string]$Resource, + + [Parameter(ParameterSetName = 'NewDeltaQuery', Mandatory = $false)] + [hashtable]$Parameters = @{}, + + [Parameter(ParameterSetName = 'DeltaUrl', Mandatory = $true)] + [string]$DeltaUrl + ) + + try { + if ($DeltaUrl) { + $GraphQuery = [System.UriBuilder]$DeltaUrl + } else { + $GraphQuery = [System.UriBuilder]('https://graph.microsoft.com/beta/{0}/delta' -f $Resource) + $QueryParams = @{ + '$deltaToken' = 'latest' + } + $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) + + foreach ($key in $QueryParams.Keys) { + if ($QueryParams[$key]) { + $ParamCollection.Add($key, $QueryParams[$key]) + } + } + + foreach ($Item in ($Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) { + 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() + } + + #Write-Information "Creating Delta Query for $Resource with parameters: $($GraphQuery.Query)" + $response = New-GraphGetRequest -tenantid $TenantFilter -uri $GraphQuery.ToString() -ReturnRawResponse + Write-Information "Delta Query created successfully for $Resource. Response: $($response | ConvertTo-Json -Depth 5)" + return $response.Content + } catch { + Write-Error "Failed to create Delta Query: $(Get-NormalizedError -Message $_.Exception.message)" + Write-Warning $_.InvocationInfo.PositionMessage + } +} diff --git a/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 index 12bab410eeee..09355e7b7d6f 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 @@ -16,7 +16,8 @@ function New-GraphGetRequest { [switch]$ComplexFilter, [switch]$CountOnly, [switch]$IncludeResponseHeaders, - [hashtable]$extraHeaders + [hashtable]$extraHeaders, + [switch]$ReturnRawResponse ) if ($NoAuthCheck -eq $false) { @@ -65,8 +66,25 @@ function New-GraphGetRequest { if ($IncludeResponseHeaders) { $GraphRequest.ResponseHeadersVariable = 'ResponseHeaders' } - $Data = (Invoke-RestMethod @GraphRequest) - if ($CountOnly) { + + if ($ReturnRawResponse) { + $GraphRequest.SkipHttpErrorCheck = $true + $Data = Invoke-WebRequest @GraphRequest + } else { + $Data = (Invoke-RestMethod @GraphRequest) + } + + if ($ReturnRawResponse) { + if (Test-Json -Json $Data.Content) { + $Content = $Data.Content | ConvertFrom-Json + $Content + } else { + $Content = $Data.Content + } + + $Data | Select-Object -Property StatusCode, StatusDescription, @{Name = 'Content'; Expression = { $Content }} + $nextURL = $null + } elseif ($CountOnly) { $Data.'@odata.count' $NextURL = $null } else { From 585e58ffe17a64286ae2edb3944328be7bdfeba2 Mon Sep 17 00:00:00 2001 From: rvdwegen Date: Fri, 25 Jul 2025 17:17:28 +0200 Subject: [PATCH 27/85] patch user endpoint --- .../Administration/Users/Invoke-PatchUser.ps1 | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 new file mode 100644 index 000000000000..4e4abfef0eb2 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 @@ -0,0 +1,38 @@ +function Invoke-PatchUser { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Identity.User.ReadWrite + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $Request.Params.CIPPEndpoint + $Headers = $Request.Headers + $tenantFilter = $Request.Body.tenantFilter + Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + + $HttpResponse = [HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{'Results' = @("Default response, you should never see this.") } + } + + try { + $UserObj = $Request.Body | Select-Object -Property * -ExcludeProperty tenantFilter + if ([string]::IsNullOrWhiteSpace($UserObj.id)) { + $HttpResponse.StatusCode = [HttpStatusCode]::BadRequest + $HttpResponse.Body = @{'Results' = @('Failed to patch user. No user ID provided') } + } else { + $UserObj + $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($UserObj.id)" -tenantid $tenantFilter -type PATCH -body $($UserObj | ConvertTo-Json) -Verbose + $HttpResponse.Body = @{'Results' = @("Properties on user $($UserObj.id) patched successfully") } + } + + } catch { + $HttpResponse.StatusCode = [HttpStatusCode]::InternalServerError + $HttpResponse.Body = @{'Results' = @("Failed to patch user. Error: $($_.Exception.Message)") } + } + + Push-OutputBinding -Name Response -Value $HttpResponse +} \ No newline at end of file From 77cc79b08d5f5d4100c9cbb9c8f509cb5c4fdfd3 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 25 Jul 2025 11:18:02 -0400 Subject: [PATCH 28/85] Update New-GraphGetRequest.ps1 --- Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 index 09355e7b7d6f..e7a3ad3bdf9b 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 @@ -77,7 +77,6 @@ function New-GraphGetRequest { if ($ReturnRawResponse) { if (Test-Json -Json $Data.Content) { $Content = $Data.Content | ConvertFrom-Json - $Content } else { $Content = $Data.Content } From a69844a7e1cd2f9857dc29329fd77c542d54d9e5 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 25 Jul 2025 17:08:34 -0400 Subject: [PATCH 29/85] audit log tweaks guid expansion allow search downloads --- .../Webhooks/Push-AuditLogTenantDownload.ps1 | 65 ++++--- .../Webhooks/Push-AuditLogTenantProcess.ps1 | 7 +- .../Alerts/Invoke-ExecAuditLogSearch.ps1 | 163 +++++++++++------- .../Alerts/Invoke-ListAuditLogSearches.ps1 | 1 - .../Start-AuditLogOrchestrator.ps1 | 2 +- .../Webhooks/Test-CIPPAuditLogRules.ps1 | 152 +++++++++++++++- 6 files changed, 283 insertions(+), 107 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantDownload.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantDownload.ps1 index 88ae358c98a2..f7171d1cdac2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantDownload.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantDownload.ps1 @@ -1,6 +1,5 @@ function Push-AuditLogTenantDownload { - Param($Item) - $ConfigTable = Get-CippTable -TableName 'WebhookRules' + param($Item) $TenantFilter = $Item.TenantFilter try { @@ -37,47 +36,43 @@ function Push-AuditLogTenantDownload { } else { $CIPPURL = 'https://{0}' -f $CippConfig.Value } } - # Get webhook rules - $ConfigEntries = Get-CIPPAzDataTableEntity @ConfigTable $LogSearchesTable = Get-CippTable -TableName 'AuditLogSearches' - $Configuration = $ConfigEntries | Where-Object { ($_.Tenants -match $TenantFilter -or $_.Tenants -match 'AllTenants') } - if ($Configuration) { - try { - $LogSearches = Get-CippAuditLogSearches -TenantFilter $TenantFilter -ReadyToProcess | Select-Object -First 10 - Write-Information ('Audit Logs: Found {0} searches, begin downloading' -f $LogSearches.Count) - foreach ($Search in $LogSearches) { - $SearchEntity = Get-CIPPAzDataTableEntity @LogSearchesTable -Filter "Tenant eq '$($TenantFilter)' and RowKey eq '$($Search.id)'" - $SearchEntity.CippStatus = 'Processing' - Add-CIPPAzDataTableEntity @LogSearchesTable -Entity $SearchEntity -Force - try { - Write-Information "Audit Log search: Processing search ID: $($Search.id) for tenant: $TenantFilter" - $Downloads = New-CIPPAuditLogSearchResultsCache -TenantFilter $TenantFilter -searchId $Search.id - $SearchEntity.CippStatus = 'Downloaded' - } catch { - if ($_.Exception.Message -match 'Request rate is large. More Request Units may be needed, so no changes were made. Please retry this request later.') { - $SearchEntity.CippStatus = 'Pending' - Write-Information "Audit Log search: Rate limit hit for $($SearchEntity.RowKey)." - if ($SearchEntity.PSObject.Properties.Name -contains 'RetryCount') { - $SearchEntity.RetryCount++ - } else { - $SearchEntity | Add-Member -MemberType NoteProperty -Name RetryCount -Value 1 - } + try { + $LogSearches = Get-CippAuditLogSearches -TenantFilter $TenantFilter -ReadyToProcess | Select-Object -First 10 + Write-Information ('Audit Logs: Found {0} searches, begin downloading' -f $LogSearches.Count) + foreach ($Search in $LogSearches) { + $SearchEntity = Get-CIPPAzDataTableEntity @LogSearchesTable -Filter "Tenant eq '$($TenantFilter)' and RowKey eq '$($Search.id)'" + $SearchEntity.CippStatus = 'Processing' + Add-CIPPAzDataTableEntity @LogSearchesTable -Entity $SearchEntity -Force + try { + Write-Information "Audit Log search: Processing search ID: $($Search.id) for tenant: $TenantFilter" + $Downloads = New-CIPPAuditLogSearchResultsCache -TenantFilter $TenantFilter -searchId $Search.id + $SearchEntity.CippStatus = 'Downloaded' + } catch { + if ($_.Exception.Message -match 'Request rate is large. More Request Units may be needed, so no changes were made. Please retry this request later.') { + $SearchEntity.CippStatus = 'Pending' + Write-Information "Audit Log search: Rate limit hit for $($SearchEntity.RowKey)." + if ($SearchEntity.PSObject.Properties.Name -contains 'RetryCount') { + $SearchEntity.RetryCount++ } else { - $Exception = [string](ConvertTo-Json -Compress -InputObject (Get-CippException -Exception $_)) - $SearchEntity | Add-Member -MemberType NoteProperty -Name Error -Value $Exception - $SearchEntity.CippStatus = 'Failed' - Write-Information "Error processing audit log rules: $($_.Exception.Message)" + $SearchEntity | Add-Member -MemberType NoteProperty -Name RetryCount -Value 1 } - + } else { + $Exception = [string](ConvertTo-Json -Compress -InputObject (Get-CippException -Exception $_)) + $SearchEntity | Add-Member -MemberType NoteProperty -Name Error -Value $Exception + $SearchEntity.CippStatus = 'Failed' + Write-Information "Error processing audit log rules: $($_.Exception.Message)" } - Add-CIPPAzDataTableEntity @LogSearchesTable -Entity $SearchEntity -Force + } - } catch { - Write-Information ('Audit Log search: Error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) - exit 0 + Add-CIPPAzDataTableEntity @LogSearchesTable -Entity $SearchEntity -Force } + } catch { + Write-Information ('Audit Log search: Error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) + exit 0 } + } catch { Write-Information ('Push-AuditLogTenant: Error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) exit 0 diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantProcess.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantProcess.ps1 index 086adcaf37dc..f52eef63271f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantProcess.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantProcess.ps1 @@ -1,5 +1,5 @@ function Push-AuditLogTenantProcess { - Param($Item) + param($Item) $TenantFilter = $Item.TenantFilter $RowIds = $Item.RowIds @@ -20,12 +20,13 @@ function Push-AuditLogTenantProcess { if ($Rows.Count -gt 0) { Write-Information "Retrieved $($Rows.Count) rows from cache for processing" Test-CIPPAuditLogRules -TenantFilter $TenantFilter -Rows $Rows - exit 0 + return $true } else { Write-Information 'No rows found in cache for the provided row IDs' - exit 0 + return $false } } catch { Write-Information ('Push-AuditLogTenant: Error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) + return $false } } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 index f66af08ebffe..387ca19a8814 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 @@ -10,78 +10,119 @@ function Invoke-ExecAuditLogSearch { $APIName = $Request.Params.CIPPEndpoint $Headers = $Request.Headers + $Action = $Request.Query.Action ?? $Request.Body.Action + Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' - $Query = $Request.Body - if (!$Query.TenantFilter) { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::BadRequest - Body = 'TenantFilter is required' - }) - return - } - if (!$Query.StartTime -or !$Query.EndTime) { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::BadRequest - Body = 'StartTime and EndTime are required' - }) - return - } + switch ($Action) { + 'ProcessLogs' { + $SearchId = $Request.Query.SearchId ?? $Request.Body.SearchId + $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter + if (!$SearchId) { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = 'SearchId is required' + }) + return + } - # Convert StartTime and EndTime to DateTime from unixtime - if ($Query.StartTime -match '^\d+$') { - $Query.StartTime = [DateTime]::UnixEpoch.AddSeconds([long]$Query.StartTime) - } else { - $Query.StartTime = [DateTime]$Query.StartTime - } + $Search = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/security/auditLog/queries/$SearchId" -AsApp $true -TenantId $TenantFilter + Write-Information ($Search | ConvertTo-Json -Depth 10) - if ($Query.EndTime -match '^\d+$') { - $Query.EndTime = [DateTime]::UnixEpoch.AddSeconds([long]$Query.EndTime) - } else { - $Query.EndTime = [DateTime]$Query.EndTime - } + $Entity = [PSCustomObject]@{ + PartitionKey = [string]'Search' + RowKey = [string]$SearchId + Tenant = [string]$TenantFilter + DisplayName = [string]$Search.displayName + StartTime = [datetime]$Search.filterStartDateTime + EndTime = [datetime]$Search.filterEndDateTime + Query = [string]($Search | ConvertTo-Json -Compress) + CippStatus = [string]'Pending' + } + $Table = Get-CIPPTable -TableName 'AuditLogSearches' + Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force | Out-Null - $Command = Get-Command New-CippAuditLogSearch - $AvailableParameters = $Command.Parameters.Keys - $BadProps = foreach ($Prop in $Query.PSObject.Properties.Name) { - if ($AvailableParameters -notcontains $Prop) { - $Prop + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{ + resultText = "Search '$($Search.displayName)' queued for processing." + state = 'success' + } | ConvertTo-Json -Depth 10 -Compress + }) } - } - if ($BadProps) { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::BadRequest - Body = "Invalid parameters: $($BadProps -join ', ')" - }) - return - } + default { + $Query = $Request.Body + if (!$Query.TenantFilter) { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = 'TenantFilter is required' + }) + return + } + if (!$Query.StartTime -or !$Query.EndTime) { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = 'StartTime and EndTime are required' + }) + return + } - try { - Write-Information "Executing audit log search with parameters: $($Query | ConvertTo-Json -Depth 10)" + # Convert StartTime and EndTime to DateTime from unixtime + if ($Query.StartTime -match '^\d+$') { + $Query.StartTime = [DateTime]::UnixEpoch.AddSeconds([long]$Query.StartTime) + } else { + $Query.StartTime = [DateTime]$Query.StartTime + } - $Query = $Query | ConvertTo-Json -Depth 10 | ConvertFrom-Json -AsHashtable - $NewSearch = New-CippAuditLogSearch @Query + if ($Query.EndTime -match '^\d+$') { + $Query.EndTime = [DateTime]::UnixEpoch.AddSeconds([long]$Query.EndTime) + } else { + $Query.EndTime = [DateTime]$Query.EndTime + } - if ($NewSearch) { - $Results = @{ - resultText = "Created audit log search: $($NewSearch.displayName)" - state = 'success' - details = $NewSearch + $Command = Get-Command New-CippAuditLogSearch + $AvailableParameters = $Command.Parameters.Keys + $BadProps = foreach ($Prop in $Query.PSObject.Properties.Name) { + if ($AvailableParameters -notcontains $Prop) { + $Prop + } } - } else { - $Results = @{ - resultText = 'Failed to initiate search' - state = 'error' + if ($BadProps) { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = "Invalid parameters: $($BadProps -join ', ')" + }) + return + } + + try { + Write-Information "Executing audit log search with parameters: $($Query | ConvertTo-Json -Depth 10)" + + $Query = $Query | ConvertTo-Json -Depth 10 | ConvertFrom-Json -AsHashtable + $NewSearch = New-CippAuditLogSearch @Query + + if ($NewSearch) { + $Results = @{ + resultText = "Created audit log search: $($NewSearch.displayName)" + state = 'success' + details = $NewSearch + } + } else { + $Results = @{ + resultText = 'Failed to initiate search' + state = 'error' + } + } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + } catch { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = $_.Exception.Message + }) } } - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) - } catch { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::BadRequest - Body = $_.Exception.Message - }) } } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ListAuditLogSearches.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ListAuditLogSearches.ps1 index 4468443aece3..988969f55970 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ListAuditLogSearches.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ListAuditLogSearches.ps1 @@ -18,7 +18,6 @@ function Invoke-ListAuditLogSearches { $Days = $Request.Query.Days $Type = $Request.Query.Type - if ($TenantFilter) { switch ($Type) { 'Searches' { diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 index e7010fd8fa0a..944592099a5e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 @@ -16,7 +16,7 @@ function Start-AuditLogOrchestrator { if (($AuditLogSearches | Measure-Object).Count -eq 0) { Write-Information 'No audit log searches available' - } elseif (($WebhookRules | Measure-Object).Count -eq 0) { + } elseif (($AuditLogSearches | Measure-Object).Count -eq 0 -and ($WebhookRules | Measure-Object).Count -eq 0) { Write-Information 'No webhook rules defined' } else { Write-Information "Audit Logs: Downloading $($AuditLogSearches.Count) searches" diff --git a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 index 6824b08058ea..3456ae75bd08 100644 --- a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 +++ b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 @@ -7,7 +7,103 @@ function Test-CIPPAuditLogRules { $Rows ) - $FunctionStartTime = Get-Date + # Helper function to map GUIDs and partner UPNs to user objects + function Add-CIPPGuidMappings { + param( + [Parameter(Mandatory = $true)] + $DataObject, + [Parameter(Mandatory = $true)] + $Users, + [Parameter(Mandatory = $true)] + $Groups, + [Parameter(Mandatory = $true)] + $Devices, + [Parameter(Mandatory = $true)] + $ServicePrincipals, + [Parameter(Mandatory = $true)] + $PartnerUsers, + [Parameter(Mandatory = $false)] + [string]$PropertyPrefix = '' + ) + + $DataObject.PSObject.Properties | ForEach-Object { + # Check for standard GUID format OR partner UPN formats + if ($_.Value -match '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' -or + $_.Value -match 'user_[0-9a-f]{32}@[^@]+\.onmicrosoft\.com' -or + $_.Value -match '[^\\]+\.onmicrosoft\.com\\tenant:\s*[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12},\s*object:\s*[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}') { + + # Use regex from guid-resolver hook to match various partner user formats + # Format 1: user_@.onmicrosoft.com + if ($_.Value -match 'user_([0-9a-f]{32})@([^@]+\.onmicrosoft\.com)') { + $hexId = $matches[1] + $tenantDomain = $matches[2] + if ($hexId.Length -eq 32) { + # Convert the 32-character hex string to GUID format + $guid = "$($hexId.Substring(0,8))-$($hexId.Substring(8,4))-$($hexId.Substring(12,4))-$($hexId.Substring(16,4))-$($hexId.Substring(20,12))" + Write-Information "Found partner UPN format: $($_.Value) with GUID: $guid and tenant: $tenantDomain" + + # Check partner users for this GUID + foreach ($PartnerUser in $PartnerUsers) { + if ($PartnerUser.id -eq $guid) { + $DataObject | Add-Member -NotePropertyName "$PropertyPrefix$($_.Name)" -NotePropertyValue $PartnerUser.userPrincipalName -Force -ErrorAction SilentlyContinue + Write-Information "Mapped Partner User UPN: $($PartnerUser.userPrincipalName) to $PropertyPrefix$($_.Name)" + return + } + } + } + } + + # Format 2: TenantName.onmicrosoft.com\tenant: , object: + if ($_.Value -match '([^\\]+\.onmicrosoft\.com)\\tenant:\s*([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}),\s*object:\s*([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})') { + $customerTenantDomain = $matches[1] + $partnerTenantGuid = $matches[2] + $objectGuid = $matches[3] + Write-Information "Found partner exchange format: customer tenant $customerTenantDomain, partner tenant $partnerTenantGuid, object $objectGuid" + + # Check partner users for this object GUID + foreach ($PartnerUser in $PartnerUsers) { + if ($PartnerUser.id -eq $objectGuid) { + $DataObject | Add-Member -NotePropertyName "$PropertyPrefix$($_.Name)" -NotePropertyValue $PartnerUser.userPrincipalName -Force -ErrorAction SilentlyContinue + Write-Information "Mapped Partner User UPN: $($PartnerUser.userPrincipalName) to $PropertyPrefix$($_.Name)" + return + } + } + } + + # Check standard directory objects (users, groups, devices, service principals) + foreach ($User in $Users) { + if ($User.id -eq $_.Value) { + $DataObject | Add-Member -NotePropertyName "$PropertyPrefix$($_.Name)" -NotePropertyValue $User.userPrincipalName -Force -ErrorAction SilentlyContinue + Write-Information "Mapped User: $($User.userPrincipalName) to $PropertyPrefix$($_.Name)" + return + } + } + foreach ($Group in $Groups) { + if ($Group.id -eq $_.Value) { + $DataObject | Add-Member -NotePropertyName "$PropertyPrefix$($_.Name)" -NotePropertyValue $Group -Force -ErrorAction SilentlyContinue + Write-Information "Mapped Group: $($Group.displayName) to $PropertyPrefix$($_.Name)" + return + } + } + foreach ($Device in $Devices) { + if ($Device.id -eq $_.Value) { + $DataObject | Add-Member -NotePropertyName "$PropertyPrefix$($_.Name)" -NotePropertyValue $Device -Force -ErrorAction SilentlyContinue + Write-Information "Mapped Device: $($Device.displayName) to $PropertyPrefix$($_.Name)" + return + } + } + foreach ($ServicePrincipal in $ServicePrincipals) { + if ($ServicePrincipal.id -eq $_.Value -or $ServicePrincipal.appId -eq $_.Value) { + $DataObject | Add-Member -NotePropertyName "$PropertyPrefix$($_.Name)" -NotePropertyValue $ServicePrincipal -Force -ErrorAction SilentlyContinue + Write-Information "Mapped Service Principal: $($ServicePrincipal.displayName) to $PropertyPrefix$($_.Name)" + return + } + } + } + } + } + + #$FunctionStartTime = Get-Date $Results = [PSCustomObject]@{ TotalLogs = 0 @@ -41,6 +137,40 @@ function Test-CIPPAuditLogRules { } } + # Collect bulk data for users/groups/devices/applications + $Requests = @( + @{ + id = 'users' + url = '/users?$select=id,displayName,userPrincipalName,accountEnabled&$top=999' + method = 'GET' + } + @{ + id = 'groups' + url = '/groups?$select=id,displayName,mailEnabled,securityEnabled&$top=999' + method = 'GET' + } + @{ + id = 'devices' + url = '/devices?$select=id,displayName,deviceId&$top=999' + method = 'GET' + } + @{ + id = 'servicePrincipals' + url = '/servicePrincipals?$select=id,displayName&$top=999' + method = 'GET' + } + ) + $Response = New-GraphBulkRequest -TenantId $TenantFilter -Requests $Requests + + # partner users + $PartnerUsers = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$select=id,displayName,userPrincipalName,accountEnabled&`$top=999" -AsApp $true -NoAuthCheck $true + + + $Users = $Response | Where-Object { $_.id -eq 'users' } | Select-Object -ExpandProperty body | Select-Object -ExpandProperty value + $Groups = $Response | Where-Object { $_.id -eq 'groups' } | Select-Object -ExpandProperty body | Select-Object -ExpandProperty value + $Devices = $Response | Where-Object { $_.id -eq 'devices' } | Select-Object -ExpandProperty body | Select-Object -ExpandProperty value + $ServicePrincipals = $Response | Where-Object { $_.id -eq 'servicePrincipals' } | Select-Object -ExpandProperty body | Select-Object -ExpandProperty value + Write-Warning '## Audit Log Configuration ##' Write-Information ($Configuration | ConvertTo-Json -Depth 10) @@ -61,10 +191,18 @@ function Test-CIPPAuditLogRules { $LocationTable = Get-CIPPTable -TableName 'knownlocationdbv2' $ProcessedData = foreach ($AuditRecord in $SearchResults) { $RecordStartTime = Get-Date - Write-Host "Processing RowKey $($AuditRecord.id)" + Write-Information "Processing RowKey $($AuditRecord.id)" $RootProperties = $AuditRecord | Select-Object * -ExcludeProperty auditData $Data = $AuditRecord.auditData | Select-Object *, CIPPAction, CIPPClause, CIPPGeoLocation, CIPPBadRepIP, CIPPHostedIP, CIPPIPDetected, CIPPLocationInfo, CIPPExtendedProperties, CIPPDeviceProperties, CIPPParameters, CIPPModifiedProperties, AuditRecord -ErrorAction SilentlyContinue try { + # Attempt to locate GUIDs in $Data and match them with their corresponding user, group, device, or service principal recursively by checking each key/value once located lets store these mapped values in a CIPP$KeyName property + Write-Information 'Checking Data for GUIDs to map to users, groups, devices, or service principals' + Add-CIPPGuidMappings -DataObject $Data -Users $Users -Groups $Groups -Devices $Devices -ServicePrincipals $ServicePrincipals -PartnerUsers $PartnerUsers -PropertyPrefix 'CIPP' + + # Also check root properties for GUIDs and partner UPNs + Write-Information 'Checking RootProperties for GUIDs to map to users, groups, devices, or service principals' + Add-CIPPGuidMappings -DataObject $RootProperties -Users $Users -Groups $Groups -Devices $Devices -ServicePrincipals $ServicePrincipals -PartnerUsers $PartnerUsers + if ($Data.ExtendedProperties) { $Data.CIPPExtendedProperties = ($Data.ExtendedProperties | ConvertTo-Json -Compress) $Data.ExtendedProperties | ForEach-Object { @@ -173,10 +311,8 @@ function Test-CIPPAuditLogRules { #write-warning "Audit log: Error processing data: $($_.Exception.Message)`r`n$($_.InvocationInfo.PositionMessage)" Write-LogMessage -API 'Webhooks' -message 'Error Processing Audit Log Data' -LogData (Get-CippException -Exception $_) -sev Error -tenant $TenantFilter } - $RecordEndTime = Get-Date - $RecordSeconds = ($RecordEndTime - $RecordStartTime).TotalSeconds - Write-Warning "Task took $RecordSeconds seconds for RowKey $($AuditRecord.id)" - Write-Host "Removing row $($AuditRecord.id) from cache" + + Write-Information "Removing row $($AuditRecord.id) from cache" try { Write-Information 'Removing processed rows from cache' $RowEntity = Get-CIPPAzDataTableEntity @CacheWebhooksTable -Filter "PartitionKey eq '$TenantFilter' and RowKey eq '$($AuditRecord.id)'" @@ -184,6 +320,10 @@ function Test-CIPPAuditLogRules { Write-Information "Removed row $($AuditRecord.id) from cache" } catch { Write-Information "Error removing rows from cache: $($_.Exception.Message)" + } finally { + $RecordEndTime = Get-Date + $RecordSeconds = ($RecordEndTime - $RecordStartTime).TotalSeconds + Write-Warning "Task took $RecordSeconds seconds for RowKey $($AuditRecord.id)" } } #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." From 35f4170bde375a48674db29ae642b966f24f0727 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 25 Jul 2025 17:10:20 -0400 Subject: [PATCH 30/85] logging --- .../Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 index 387ca19a8814..d0fb549f3ca5 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 @@ -42,6 +42,8 @@ function Invoke-ExecAuditLogSearch { $Table = Get-CIPPTable -TableName 'AuditLogSearches' Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force | Out-Null + Write-LogMessage -headers $Headers -API $APIName -message "Queued search for processing: $($Search.displayName)" -Sev 'Info' -tenant $TenantFilter + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = @{ @@ -102,12 +104,14 @@ function Invoke-ExecAuditLogSearch { $NewSearch = New-CippAuditLogSearch @Query if ($NewSearch) { + Write-LogMessage -headers $Headers -API $APIName -message "Created audit log search: $($NewSearch.displayName)" -Sev 'Info' -tenant $TenantFilter $Results = @{ resultText = "Created audit log search: $($NewSearch.displayName)" state = 'success' details = $NewSearch } } else { + Write-LogMessage -headers $Headers -API $APIName -message 'Failed to create audit log search' -Sev 'Error' -tenant $TenantFilter $Results = @{ resultText = 'Failed to initiate search' state = 'error' From 7f877d5314968d33ed2f58491a3e56e48f716d8e Mon Sep 17 00:00:00 2001 From: ngms-psh Date: Sun, 27 Jul 2025 12:34:02 +0200 Subject: [PATCH 31/85] fixed policyMigrationState for 'new' tenants where is completed by default --- .../Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 index 2d40d7a7e266..d6e5a810c116 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 @@ -30,8 +30,12 @@ function Invoke-CIPPStandardAuthMethodsPolicyMigration { param($Tenant, $Settings) $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy' -tenantid $Tenant + if ($null -eq $CurrentInfo) { + throw + } + if ($Settings.remediate -eq $true) { - if ($CurrentInfo.policyMigrationState -eq 'migrationComplete') { + if ($CurrentInfo.policyMigrationState -eq 'migrationComplete' -or $null -eq $CurrentInfo.policyMigrationState) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Authentication methods policy migration is already complete.' -sev Info } else { try { @@ -44,14 +48,14 @@ function Invoke-CIPPStandardAuthMethodsPolicyMigration { } if ($Settings.alert -eq $true) { - if ($CurrentInfo.policyMigrationState -ne 'migrationComplete') { + if ($CurrentInfo.policyMigrationState -ne 'migrationComplete' -and $null -ne $CurrentInfo.policyMigrationState) { Write-StandardsAlert -message 'Authentication methods policy migration is not complete. Please check if you have legacy SSPR settings or MFA settings set: https://learn.microsoft.com/en-us/entra/identity/authentication/how-to-authentication-methods-manage' -object $CurrentInfo -tenant $tenant -standardName 'AuthMethodsPolicyMigration' -standardId $Settings.standardId Write-LogMessage -API 'Standards' -tenant $tenant -message 'Authentication methods policy migration is not complete' -sev Alert } } if ($Settings.report -eq $true) { - $migrationComplete = $CurrentInfo.policyMigrationState -eq 'migrationComplete' + $migrationComplete = $CurrentInfo.policyMigrationState -eq 'migrationComplete' -or $null -eq $CurrentInfo.policyMigrationState Set-CIPPStandardsCompareField -FieldName 'standards.AuthMethodsPolicyMigration' -FieldValue $migrationComplete -TenantFilter $tenant Add-CIPPBPAField -FieldName 'AuthMethodsPolicyMigration' -FieldValue $migrationComplete -StoreAs bool -Tenant $tenant } From cffa2d13a2656949e9943e42c378e02a34f56211 Mon Sep 17 00:00:00 2001 From: ngms-psh Date: Sun, 27 Jul 2025 12:50:31 +0200 Subject: [PATCH 32/85] Added error message if the request does not return any data. --- .../Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 index d6e5a810c116..867ef440fec8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 @@ -31,7 +31,7 @@ function Invoke-CIPPStandardAuthMethodsPolicyMigration { $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy' -tenantid $Tenant if ($null -eq $CurrentInfo) { - throw + throw "Failed to retrieve current authentication methods policy information" } if ($Settings.remediate -eq $true) { From 773088276227099ba83a467b83bdaa964d4d5cc3 Mon Sep 17 00:00:00 2001 From: ngms-psh Date: Sun, 27 Jul 2025 14:03:40 +0200 Subject: [PATCH 33/85] Fix EXO Duplicate Identity Error --- .../Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 | 2 +- .../Invoke-CIPPStandardDisableExchangeOnlinePowerShell.ps1 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 index da0f37a1b4d8..b871c75e7343 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 @@ -59,7 +59,7 @@ function Invoke-CIPPStandardDisableBasicAuthSMTP { # Disable SMTP Basic Authentication for all users $SMTPusers | ForEach-Object { try { - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-CASMailbox' -cmdParams @{ Identity = $_.Identity; SmtpClientAuthenticationDisabled = $null } -UseSystemMailbox $true + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-CASMailbox' -cmdParams @{ Identity = $_.Guid; SmtpClientAuthenticationDisabled = $null } -UseSystemMailbox $true Write-LogMessage -API 'Standards' -tenant $tenant -message "Disabled SMTP Basic Authentication for $($_.DisplayName), $($_.PrimarySmtpAddress)" -sev Info } catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExchangeOnlinePowerShell.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExchangeOnlinePowerShell.ps1 index 525cfd6149e2..82d25184ccaf 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExchangeOnlinePowerShell.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExchangeOnlinePowerShell.ps1 @@ -44,7 +44,7 @@ function Invoke-CIPPStandardDisableExchangeOnlinePowerShell { try { $AdminUsers = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments?$expand=principal' -tenantid $Tenant).principal.userPrincipalName - $UsersWithPowerShell = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-User' -Select 'userPrincipalName, identity, remotePowerShellEnabled' | Where-Object { $_.RemotePowerShellEnabled -eq $true -and $_.userPrincipalName -notin $AdminUsers } + $UsersWithPowerShell = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-User' -Select 'userPrincipalName, identity, guid, remotePowerShellEnabled' | Where-Object { $_.RemotePowerShellEnabled -eq $true -and $_.userPrincipalName -notin $AdminUsers } $PowerShellEnabledCount = ($UsersWithPowerShell | Measure-Object).Count $StateIsCorrect = $PowerShellEnabledCount -eq 0 } catch { @@ -61,7 +61,7 @@ function Invoke-CIPPStandardDisableExchangeOnlinePowerShell { @{ CmdletInput = @{ CmdletName = 'Set-User' - Parameters = @{Identity = $_.Identity; RemotePowerShellEnabled = $false } + Parameters = @{Identity = $_.Guid; RemotePowerShellEnabled = $false } } } } From 00eea6a14769af6cf429e97a7ed0a4ce56e760a7 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sun, 27 Jul 2025 17:29:05 -0400 Subject: [PATCH 34/85] Update Invoke-ListDirectoryObjects.ps1 --- .../HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 index 489bf3371654..ad8e708b4213 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 @@ -12,7 +12,7 @@ function Invoke-ListDirectoryObjects { $Headers = $Request.Headers Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' - $TenantFilter = $Request.Body.tenantFilter + $TenantFilter = $Request.Body.partnerLookup ? $env:TenantID : $Request.Body.tenantFilter $AsApp = $Request.Body.asApp $Ids = $Request.Body.ids From 7abcdc6918f4d8205e6c6a34943229f71198dcaa Mon Sep 17 00:00:00 2001 From: rvdwegen Date: Mon, 28 Jul 2025 10:10:03 +0200 Subject: [PATCH 35/85] Patch user endpoint, no verbose --- .../Identity/Administration/Users/Invoke-PatchUser.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 index 4e4abfef0eb2..0abc9286dd1e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 @@ -24,8 +24,7 @@ function Invoke-PatchUser { $HttpResponse.StatusCode = [HttpStatusCode]::BadRequest $HttpResponse.Body = @{'Results' = @('Failed to patch user. No user ID provided') } } else { - $UserObj - $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($UserObj.id)" -tenantid $tenantFilter -type PATCH -body $($UserObj | ConvertTo-Json) -Verbose + $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($UserObj.id)" -tenantid $tenantFilter -type PATCH -body $($UserObj | ConvertTo-Json) $HttpResponse.Body = @{'Results' = @("Properties on user $($UserObj.id) patched successfully") } } From 84918353a993998d4741aa03c458057fee4966c6 Mon Sep 17 00:00:00 2001 From: rvdwegen Date: Mon, 28 Jul 2025 10:22:26 +0200 Subject: [PATCH 36/85] Bulk the patch user endpoint --- .../Administration/Users/Invoke-PatchUser.ps1 | 80 +++++++++++++++++-- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 index 0abc9286dd1e..cb8315167baa 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 @@ -10,7 +10,6 @@ function Invoke-PatchUser { $APIName = $Request.Params.CIPPEndpoint $Headers = $Request.Headers - $tenantFilter = $Request.Body.tenantFilter Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' $HttpResponse = [HttpResponseContext]@{ @@ -19,18 +18,85 @@ function Invoke-PatchUser { } try { - $UserObj = $Request.Body | Select-Object -Property * -ExcludeProperty tenantFilter - if ([string]::IsNullOrWhiteSpace($UserObj.id)) { + # Handle array of user objects or single user object + $Users = if ($Request.Body -is [array]) { + $Request.Body + } else { + @($Request.Body) + } + + # Validate that all users have required properties + $InvalidUsers = $Users | Where-Object { + [string]::IsNullOrWhiteSpace($_.id) -or [string]::IsNullOrWhiteSpace($_.tenantFilter) + } + if ($InvalidUsers.Count -gt 0) { $HttpResponse.StatusCode = [HttpStatusCode]::BadRequest - $HttpResponse.Body = @{'Results' = @('Failed to patch user. No user ID provided') } + $HttpResponse.Body = @{'Results' = @('Failed to patch user(s). Some users are missing id or tenantFilter') } } else { - $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($UserObj.id)" -tenantid $tenantFilter -type PATCH -body $($UserObj | ConvertTo-Json) - $HttpResponse.Body = @{'Results' = @("Properties on user $($UserObj.id) patched successfully") } + # Group users by tenant filter + $UsersByTenant = $Users | Group-Object -Property tenantFilter + + $TotalSuccessCount = 0 + $AllErrorMessages = @() + + # Process each tenant separately + foreach ($TenantGroup in $UsersByTenant) { + $tenantFilter = $TenantGroup.Name + $TenantUsers = $TenantGroup.Group + + # Build bulk requests for this tenant + $int = 0 + $BulkRequests = foreach ($User in $TenantUsers) { + # Remove the id and tenantFilter properties from the body since they're not user properties + $PatchBody = $User | Select-Object -Property * -ExcludeProperty id, tenantFilter + + @{ + id = $int++ + method = 'PATCH' + url = "users/$($User.id)" + body = $PatchBody + 'headers' = @{ + 'Content-Type' = 'application/json' + } + } + } + + # Execute bulk request for this tenant + $BulkResults = New-GraphBulkRequest -tenantid $tenantFilter -Requests @($BulkRequests) + + # Process results for this tenant + for ($i = 0; $i -lt $BulkResults.Count; $i++) { + $result = $BulkResults[$i] + $user = $TenantUsers[$i] + + if ($result.status -eq 200 -or $result.status -eq 204) { + $TotalSuccessCount++ + Write-LogMessage -headers $Headers -API $APIName -tenant $tenantFilter -message "Successfully patched user $($user.id)" -Sev 'Info' + } else { + $errorMsg = if ($result.body.error.message) { + $result.body.error.message + } else { + "Unknown error (Status: $($result.status))" + } + $AllErrorMessages += "Failed to patch user $($user.id) in tenant $($tenantFilter): $errorMsg" + Write-LogMessage -headers $Headers -API $APIName -tenant $tenantFilter -message "Failed to patch user $($user.id). Error: $errorMsg" -Sev 'Error' + } + } + } + + # Build final response + if ($AllErrorMessages.Count -eq 0) { + $TenantCount = ($Users | Select-Object -Property tenantFilter -Unique).Count + $HttpResponse.Body = @{'Results' = @("Successfully patched $TotalSuccessCount user$(if($TotalSuccessCount -ne 1){'s'}) across $TenantCount tenant$(if($TenantCount -ne 1){'s'})") } + } else { + $HttpResponse.StatusCode = [HttpStatusCode]::BadRequest + $HttpResponse.Body = @{'Results' = $AllErrorMessages + @("Successfully patched $TotalSuccessCount of $($Users.Count) users") } + } } } catch { $HttpResponse.StatusCode = [HttpStatusCode]::InternalServerError - $HttpResponse.Body = @{'Results' = @("Failed to patch user. Error: $($_.Exception.Message)") } + $HttpResponse.Body = @{'Results' = @("Failed to patch user(s). Error: $($_.Exception.Message)") } } Push-OutputBinding -Name Response -Value $HttpResponse From 15cd3bb5623755235c1a493f943c6d0cc9bde2b0 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 28 Jul 2025 11:33:52 -0400 Subject: [PATCH 37/85] intunewin helpers fix issue in ticket 24988417853 --- AddMSPApp/datto.app.xml | 28 ++++----- AddMSPApp/datto.intunewin | Bin 768 -> 752 bytes .../Get-IntuneWinPackageContents.ps1 | 32 ++++++++++ .../IntuneWin}/IntuneWinAppUtil.exe | Bin Tools/IntuneWin/IntuneWinAppUtilDecoder.exe | Bin 0 -> 12800 bytes Tools/IntuneWin/New-IntuneWinPackage.ps1 | 58 ++++++++++++++++++ 6 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 Tools/IntuneWin/Get-IntuneWinPackageContents.ps1 rename {AddChocoApp/IntunePackage => Tools/IntuneWin}/IntuneWinAppUtil.exe (100%) create mode 100644 Tools/IntuneWin/IntuneWinAppUtilDecoder.exe create mode 100644 Tools/IntuneWin/New-IntuneWinPackage.ps1 diff --git a/AddMSPApp/datto.app.xml b/AddMSPApp/datto.app.xml index 145d910d8ef3..ca6f1636c64c 100644 --- a/AddMSPApp/datto.app.xml +++ b/AddMSPApp/datto.app.xml @@ -1,15 +1,15 @@ - - install.ps1 - 705 - datto.intunewin - install.ps1 - - sL/LP/JZ4F4cBSykm6usgJoV1PMoqd62C6JUwuo2z24= - PEpeqeoX7jAWxb0xHGfCkKFxh4/YRfoMTVXrP+uZWzM= - ulFPA+vYjaxX0pvq0BMAKQ== - 28ZFU4AT1OznwF8pfqO8i+WFUNSf9024H4Jw2H7UJWs= - ProfileVersion1 - YEb+QNQCko/uZyedA+JfcP/RDm+nZOIjFN04CfhwN4c= - SHA256 - + + install.ps1 + 693 + datto.intunewin + install.ps1 + + jobB9Ga7J3CbO6acWJyvBRE56nFXwqGfcnGfZRMsJC4= + 53SOzs0l6Po2btsGFSMZgkV8vwhH+PxTN8BZDUcfWfg= + VjM/osrvPElbu79J+mdXuw== + UZZXO53Np/tG6Ms+qvwLcNOeD1GRH6NRPFg/TuMz39M= + ProfileVersion1 + KtAWAl29064LG0eyDinbDs0JUbK+EK7GsJovu8obBM4= + SHA256 + \ No newline at end of file diff --git a/AddMSPApp/datto.intunewin b/AddMSPApp/datto.intunewin index 607b0725032b7f93ba2226ea9af588c77620ed96..96b3f27c3c62cf728410d97ab93acf57d0010527 100644 GIT binary patch literal 752 zcmVtFcyT3{L zXIH!6C17%_Bnx+$=w5e_?J^CR%6yZs(Tl-AWjPQey81LvL?vtwh?@_5zOXWg%$x%Q z6>7w1x?DDwnE3tP6y0v+n&euZE3aEMG=t+5qz~C&^HY<{Bj&i3r){%d4{fLm-uS&= z3T?6UJZb%-i=ZS=q{rg7qCUE7PivKCtrZ-P-CXs**M2rV1i==0UKlEO z&=yw49J#mQde4#s=Lw{@)s{Ft^>E?#wxYOn$Y^u*Uet}(((}xqvaR)#+TWd>OsING$_iD4ZV-TDC>RyH&gorb{c zL260Phv=aRHwU*LLNu;uk zk@JN{EzFnNIJ@DpWKj)hpJ1N*tQ*_7&dquJojbYMwf88aU zEu5Z@pw6C-SnvAIxisX$G?3zp;*tG-O(G+3QcS-ZEr681jD~%;8~!OF>Ls#U9V2w* ze<0h(;H2hfDi6xb2O)v_^v~4>C5-__1_Cwgo7M-0gVHx zf|=IxmL860>XT3ZRO4+!GjUlT{kK?H$40QW?@67x5ciDj_eG;&Zt+1F&CXa%eA5=9 zFPfzOPmsF<4k)aU4N!Q}Hd5~;uv{&uJ#3zZ@@C^z|I|zw#7wFirnW=bj!gHnDFBzI it(&@7C0eUo`BH?a^6#czrCU9Xi@mn&%#y`?Menv|pnymK literal 768 zcmV+b1ONQn#zj+r6V&YIz+WkTqr8jdg;3O=_f5DTf^gV=)Fo@WQBMQw*o~}L(wpkg z696gpMu|)NYJLjr%TdhbceLGQvAA|Rw?=D7s?71sb!hj>flu)7fRS&2F#ScOxJBP!Ach7YBiqM<;i#tw_eo-=kh}kQ~7HEBBUW*6TN~)a*d~XIKqN zI}Fw48A9=bkVcfnrD5f>r6YDW+R|__*Y9#{(GcwYmMoR;SdJseBPMSl+|dTDlP3fQ zGBAIuG20Ts_UV3QL_B*H2Dv8X{SJ(|J1`MS+M4n!30whDzUi_Yvq$)8%{tNr6LwEe zn&`BaO7@BVL1YqYYr#;Gvx5HWX!-R-v0fz1mj+7L9~)OvS7Y&K8gOY0`{WmfK0ReU z#cKd|L*JRtaIq}eMQtFKaUF?ks15>w)nQiT&9_^efEakNs6*|3XdB0VlJmD)B4($fYMQO?TX<88h~DG zeV5ZVvlz;S(ix&I7D(iq(-sg#z|oLS%SFR!nU8UW`1KK^DScagjah5hiYk3PZxIeW yot}Zx9?2Er1qjM_Nz>yn$v&pkqwTBb(stS~#sFe?T0*@_dBZ?}Vr3U(_4eNPW`&6W diff --git a/Tools/IntuneWin/Get-IntuneWinPackageContents.ps1 b/Tools/IntuneWin/Get-IntuneWinPackageContents.ps1 new file mode 100644 index 000000000000..c5f2caf2e66b --- /dev/null +++ b/Tools/IntuneWin/Get-IntuneWinPackageContents.ps1 @@ -0,0 +1,32 @@ +param( + $PackagePath, + $MetadataPath +) + +# Example: .\tools\IntuneWin\Get-IntuneWinPackageContents.ps1 -PackagePath .\AddMSPApp\datto.intunewin -MetadataPath .\AddMSPApp\datto.app.xml + +$Metadata = [xml](Get-Content -Path $MetadataPath) +$Encryption = $Metadata.ApplicationInfo.EncryptionInfo + +$Package = Get-Item $PackagePath + +Write-Host "Decrypting IntuneWin package $($Package.FullName)" +Write-Host "Using encryption key: $($Encryption.EncryptionKey)" +Write-Host "Using initialization vector: $($Encryption.InitializationVector)" + +$DecoderParams = @( + "`"$($Package.FullName)`"" + "/key:`"$($Encryption.EncryptionKey)`"" + "/iv:`"$($Encryption.InitializationVector)`"" +) + +Start-Process -FilePath "$PSScriptRoot\IntuneWinAppUtilDecoder.exe" -ArgumentList $DecoderParams -Wait -NoNewWindow +# replace filename.intunewin with filename.decoded.zip +$NewFileName = "$($Package.BaseName -replace '\.intunewin$', '').decoded.zip" + +#Extract zip +Write-Host "Extracting decrypted IntuneWin package: $($Package.DirectoryName)\$NewFileName" +Expand-Archive -Path "$($Package.DirectoryName)\$NewFileName" -DestinationPath "$($Package.DirectoryName)\$($Package.BaseName)" -Force + +# Remove the decoded zip file +Remove-Item -Path "$($Package.DirectoryName)\$NewFileName" -Force diff --git a/AddChocoApp/IntunePackage/IntuneWinAppUtil.exe b/Tools/IntuneWin/IntuneWinAppUtil.exe similarity index 100% rename from AddChocoApp/IntunePackage/IntuneWinAppUtil.exe rename to Tools/IntuneWin/IntuneWinAppUtil.exe diff --git a/Tools/IntuneWin/IntuneWinAppUtilDecoder.exe b/Tools/IntuneWin/IntuneWinAppUtilDecoder.exe new file mode 100644 index 0000000000000000000000000000000000000000..b0c2644d14af852f45e9414165b1d840c53697d6 GIT binary patch literal 12800 zcmeHNYiwM{bv}1r++C7OT~Z+8g3dJg&aGGQ^!VI+eI2UY1*WX^Jr0|NR76RYt%v8ABG!OL0TX!&?fE! zZIagNcV_nPLnP$~`asZJ^3LPTnRCvZbLPz4nLTme6XYkN0N$H7iN1=QpF=XeeX@w= zSljQ$=u45;Hhxt*{@TXzS<5wYjy>a;86#z8vv%H?6oymC8dlaA92qk*_LNB0*GD%O zNskT@9oPJH@X(K6@VMQecB<97h_(U}RnW7*BLnYcyeyr8%%|i=i9($4^Bi8l`SH`` zj}Wbr|K+?`RVd^K-4QNdM%NONVk2jZM0LoH4-)O;`g?s0qHqOn814I9=Zrpo0M-bk`qR4MqJ#HmJai}7+1I3nutAOTfs%dTt z@4ErX^cp?cy~y=THqzP zA>M*s9SKys%hfwk9Vl0s+iUXvZ@Qm*MdhJVnq|R z4!k%Q_w#7=aLf6oupZWH9=GmUbMxlSQ2Y+Y3D?NHBkKI2hYQ85-&O03YF=Mo^E34k zXCCz;|5(I%v{dUj>8=N*NK-T%-+)5LsyN&r8o!g1HY9b0#A5!&jEqSh4D1I}rt zz$}_VdQg(+xZ=!uWaB|_YpQLmjbqwG+a{8wVtscK_*((3Bq9~qv7-{y5{HLJcQ!k^ z-&2gUy;Ll!GojR(*b>o$p_+INxc2@Ie90LDX2dZWACpk&VFyL@P<%V8Z#CFEJm|L{ z0X`T%$+$eA(m3NgQERHx>+B8`dQUN)Dm(wi$ndeKos>NSTUYN4#yc6m9<(9>%IWoz zb5l%@N%g=z&H!9=k0PDt8B(It)Sx%?{xuAtH#lFfZds+T0)aQn1R9)O!K%Kxt^?zK z1}YDX=|O#!%`)k_%%pl?S*aYo{ub>>(<*=YEsuyYV`F1u*^I?vefNm7Jp^9Ttt#9> zS+a4dCCfgy!|CUqQcCAzr6OVoVh_>tzUb$C4G0Aq$Chqr9hiD^ban0Ox;P?bv<_A> z=sG-0tM{*6*BsvkVD0Lr!0M*p>ZXwMh9_4bzMCyl6I6LPoba#S8O5B`!!{y9v?<_Q z*P^<^r-N->4>a37fN!nQ11;`dj1g*Xitg;wLy=}ZV($S&57~P;i(-BwZSJgd-uC$I z)b(gv^kO1f2I;{_vlQ3Hq7>cI6lw1IZII|irL2&a=v#GVhkT3PYS%*@HSpCw<`hIR zsEPp;1NPm3{?%J;6+N12I^g|`9qtH)i8+du$Vcqf)#_*T% z{v%!lfzrc^Ngedwh_@1-;cEfA6EA|le44s6`lVvI!iE0TGpqAZP0`lQrm$DyD56~L zon>GnQLoi3mBO_i?4?ra<8>aaOz+it5B7AdSi%|?KLCyFpu=PL9@coJ!o%a5>q&Mc zySut~!5oA&HI4L3aOh1B!NCy0UdOv>Ebmy^8J97BfIZG)17K&yD2H&)?z-vdnc+d? zi>N;erEWT$wkH`G531U^))%x0SAL{*6ILwh@l|gHW{vp6= zv&QMHr1^s6;z(+*%XE&I`>%j6>jxVUJZ$4d1d|E>y@7sNV_PlA8t;~L>Z3Mo)L`6C zhLgc|O`~(gT5oVGYL`@v0@@w0K(<&rh?<`sEY=fdK$|d*FH+U@PWIro)P`R+6+d?R@dmyeXFn`$^})*knh2U8hr(=KKdq7zuZ;_ z0ECOdmrUqHoDjev5c? zPXf{g2#@VM^e4e%NPGP!k-p(OgVfOOr?=^!DMde|e-F$8av#l-rZJuG&>sY6(VFmQ zkbW07e4Aq0Rlsist^((?bd4e!_j;S&^rrwnO%H-&A@~U3d%^vOv{!pn;y+Hh_W9sb z^dfzno~2LGUG%F+&jmkC>$J7O&(cP1DELKso?h`iOJAcd?ax5rkNkf@!&7sGi%fEOgfec*cGA`)ofhU49k-F} zAG6XTn?HEv(xt;@>Iwqp5lf_}fRRg^DM7>T$wE3k;+&hc@?tExZ&)D$P*$fS<`7`dAS$EoY zGBlDCSvp}_S;>0@YDE!#KQx~gSthtV8xML~Os6p)7;k7kC8VV>K;8sNl{=P;rsAwR zCKiUX)3(?5d?ua5+_}1vrs+KT<>p-;Q_9TK$m9bUzsK^V z$djGBG@Qw$t(29gb7FEJZ6Vf4CsSdMT=-kDcz!4?GTeNA&`uR(iM{c(ZRZgvop;Pse%vm_uTqp9%H;A3yf)N! zVx1ARK=?GMO_a)7q!YV=Ifh0O#gZ26#A-whoLblk3(6ACy@CnUwFXYhP;E^oO&9B< zWCCxNX{|{t1Jw?U1H+*iP|Ra3okBkc zR0e3|q=GD|P63&D6jQ*v2B{gJN1{|@;A4VIc{yGI46Ye42#-X7pFz~Na^EC>jMOXY zS>QwZbxBZX1KBd40>+0xHOG69H5bq_4{i=*U=y-*wD_4pARDkHtKEPQY!Iqr4z^ew50;Ifhq;<`u^`e0yr!_lInDrs{0L@?A7vwY@lZ{teh8SOGm>mtPMs9Gv-l3W zP0GnSA`Pwrl|jcK023F0PD1G^gewF0Id%osm1S^}z_j63+abuWjiVEJ+%17HUt;$ z!V2YSJS`C`b!d>%VmIwZN2N=HDnpcK0JT1*iOm%U8?_30tKLkJc>yAMdg@$;`*N^E zg$xONT33dp1Z3g0E=LLSB-l>c!y{+#-Q7<|u?p}qSUIG3cZ(zl&f_&`KOK>gCM}1| z+G8e_)L5%oV5v3aAo|>oWuJBbVZ7Bls*A-|Z(qjF#csBTdXRF|@|Il{>B`%Wl`%Q4 zcyzs4!scOF`=G2o#BH8e8K_9D9OVrad)Ep0WZA@(h83m7|033vA!vkGlpMT?CkHKl zTB(8Ok=hUNPD$-L3=FjVSE;V|qx|-w_~RwC zVk%Fe#Wz_$YLY4!;Q6zh!2f;1Rxh;d5-YpIavEmQJ%{ec>>j{&tL(Vv;a>Lvf+OyS z_bwe=d52f_*$+NjmpaV<*5G)+OJ|=a&+X>l%F0$7Z+78V&tWoifKcBvYz1EOydY8i z9hAdt4WhRni)M}Kf=@G~9t z>8BpMxSs-sriJ|mX+acpovSfUd^q9PVi6;#`Spy?hbrmA8)}+j>S>LIV=;ZW@kA`v zc#8k@VP7Z~GJL+6UI-e#SU6lme#R+)P%MVT7sHuuEJ!|300w}JhbDvc48U3pH$8qP zFDo$!g^~sqz-T-bLUHkWtj5s5Zt?n>np$7uMB~NQV55m#v<6#);V`6ZycnZE<3vnf z{FJ`<%lhK;pwSwPg$-Y$$w)PZ4?o*j{9>))2f@!bn)>2PH7Ij;Rs{zjk z{lfwpwXAUJN7SQHr#b>O%&ei%44e*j?>2BiXpG_@8)wi)JZZ^u!)sO+ySkvpbk}LL z+D=eY?C#o)Tj=c?ZSLOF-7QS9CvjKLuAW3s z&*YxOzR9k;64Sk@sos6ld(G)Ce5=)IRI>~BlDL^9I-${qkb8`zBhh*s|EAh#@ zX9J-WKR?30loqSy=jkDT2e$;yc((unQTVeb@o;dc@ERolXSFm+&k$PNg#( zw7=JnPoUCyS_@jneQ%)uB-SW zfI*H8z*k%tR~@^SNA>>rdtYUQk?Ej&9Q1_Psd!7r)^a}@$Nb;k7-oeOr32^ zVPC@MAZ1O_`=pBnPTH!P-mMn9)n5B#pMH#*ze+Qh5$Y`S-THlDisT zna`vRtdV@_bf6uJTDu{#xN^fq%z^eZ<3|$v+6^~vW~a=wofQY#7lhk>&%yd=eRRKB zy!A9dBI_P#FF4sgH#Li!PB)RUQjYD~)AGB@-{T?M4=tr3cziEMVcsJ&(MaAda|NfSh6q_uO;1iJcMdd!*X!Q8n0otB75KvLxR?`ZBhBf7cGDfs&e>Ol z({2 Date: Mon, 28 Jul 2025 17:12:02 -0400 Subject: [PATCH 38/85] fix graph presets endpoint --- .../Tools/Invoke-ExecGraphExplorerPreset.ps1 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 index 9828d33ba5ef..349dc67f91d2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 @@ -41,31 +41,31 @@ function Invoke-ExecGraphExplorerPreset { $params.'$select' = ($params.'$select').value -join ',' } - if (!$Request.Body.preset.name) { + if (!$Request.Body.preset.name -and $Action -ne 'Delete') { $Message = 'Error: Preset name is required' $StatusCode = [HttpStatusCode]::BadRequest Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = $StatusCode Body = @{ - Results = @{ + Results = @(@{ resultText = $Message state = 'error' - } + }) } }) return } - if (!$Request.Body.preset.endpoint) { + if (!$Request.Body.preset.endpoint -and $Action -ne 'Delete') { $Message = 'Error: Preset endpoint is required' $StatusCode = [HttpStatusCode]::BadRequest Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = $StatusCode Body = @{ - Results = @{ + Results = @(@{ resultText = $Message state = 'error' - } + }) } }) return @@ -114,10 +114,10 @@ function Invoke-ExecGraphExplorerPreset { Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = $StatusCode Body = @{ - Results = @{ + Results = @(@{ resultText = $Message state = if ($Success) { 'success' } else { 'error' } - } + }) } }) } From ff3fbf6c2cc735453400f3b32865c9d8ceaed86b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:15:30 +0200 Subject: [PATCH 39/85] implements drift standards --- .../Invoke-ExecUpdateDriftDeviation.ps1 | 66 ++++ .../Standards/Invoke-ListTenantAlignment.ps1 | 1 + .../Standards/Invoke-ListTenantDrift.ps1 | 36 ++ .../Invoke-listStandardTemplates.ps1 | 1 - .../Functions/Get-CIPPTenantAlignment.ps1 | 2 + Modules/CIPPCore/Public/Get-CIPPDrift.ps1 | 370 ++++++++++++++++++ .../Public/Set-CIPPDriftDeviation.ps1 | 53 +++ 7 files changed, 528 insertions(+), 1 deletion(-) create mode 100644 Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantDrift.ps1 create mode 100644 Modules/CIPPCore/Public/Get-CIPPDrift.ps1 create mode 100644 Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 new file mode 100644 index 000000000000..deae5e3c0663 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 @@ -0,0 +1,66 @@ +using namespace System.Net + +function Invoke-ExecUpdateDriftDeviation { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Tenant.Standards.ReadWrite + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + try { + $TenantFilter = $Request.Body.TenantFilter + + if ($Request.Body.RemoveDriftCustomization) { + $Table = Get-CippTable -tablename 'tenantDrift' + $Filter = "PartitionKey eq '$TenantFilter'" + $ExistingDeviations = Get-CIPPAzDataTableEntity @Table -Filter $Filter + foreach ($Deviation in $ExistingDeviations) { + Remove-AzDataTableEntity @Table -Entity $Deviation + } + $Results = @([PSCustomObject]@{ + success = $true + result = "All drift customizations removed for tenant $TenantFilter" + }) + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed all drift customizations for tenant $TenantFilter" -Sev 'Info' + } else { + $Deviations = $Request.Body.deviations + $Results = foreach ($Deviation in $Deviations) { + try { + $Result = Set-CIPPDriftDeviation -TenantFilter $TenantFilter -StandardName $Deviation.standardName -Status $Deviation.status + [PSCustomObject]@{ + success = $true + result = $Result + } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Updated drift deviation status for $($Deviation.standardName) to $($Deviation.status)" -Sev 'Info' + } catch { + [PSCustomObject]@{ + standardName = $Deviation.standardName + success = $false + error = $_.Exception.Message + } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to update drift deviation for $($Deviation.standardName): $($_.Exception.Message)" -Sev 'Error' + } + } + } + + $Body = @{ Results = @($Results) } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) + + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to update drift deviation: $($_.Exception.Message)" -Sev 'Error' + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = @{error = $_.Exception.Message } + }) + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantAlignment.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantAlignment.ps1 index 4f4a48dd6766..93859625f996 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantAlignment.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantAlignment.ps1 @@ -22,6 +22,7 @@ function Invoke-ListTenantAlignment { [PSCustomObject]@{ tenantFilter = $_.TenantFilter standardName = $_.StandardName + standardType = $_.StandardType ? $_.StandardType : 'Classic Standard' standardId = $_.StandardId alignmentScore = $_.AlignmentScore LicenseMissingPercentage = $_.LicenseMissingPercentage diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantDrift.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantDrift.ps1 new file mode 100644 index 000000000000..8daf23f5861b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantDrift.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +function Invoke-ListTenantDrift { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Tenant.Standards.Read + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $Request.Params.CIPPEndpoint + + try { + # Use the new Get-CIPPTenantAlignment function to get alignment data + if ($Request.Query.TenantFilter) { + $TenantFilter = $Request.Query.TenantFilter + $Results = Get-CIPPDrift -TenantFilter $TenantFilter + } else { + $Tenants = Get-Tenants + $Results = $Tenants | ForEach-Object { Get-CIPPDrift -AllTenants -TenantFilter $_.defaultDomainName } + } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($Results) + }) + } catch { + Write-LogMessage -API $APIName -message "Failed to get tenant alignment data: $($_.Exception.Message)" -sev Error + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::InternalServerError + Body = @{ error = "Failed to get tenant alignment data: $($_.Exception.Message)" } + }) + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 index 258ce05cc801..b22164cd87a4 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 @@ -15,7 +15,6 @@ function Invoke-listStandardTemplates { Write-LogMessage -Headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' # Interact with query parameters or the body of the request. $ID = $Request.Query.id - $Table = Get-CippTable -tablename 'templates' $Filter = "PartitionKey eq 'StandardsTemplateV2'" $Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { diff --git a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 index 5671b2a79632..b9ce837ba405 100644 --- a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 +++ b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 @@ -236,6 +236,8 @@ function Get-CIPPTenantAlignment { TenantFilter = $TenantName StandardName = $Template.templateName StandardId = $Template.GUID + standardType = $Template.type + standardSettings = $Template.Standards AlignmentScore = $AlignmentPercentage LicenseMissingPercentage = $LicenseMissingPercentage CombinedScore = $AlignmentPercentage + $LicenseMissingPercentage diff --git a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 new file mode 100644 index 000000000000..1be95114d8d3 --- /dev/null +++ b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 @@ -0,0 +1,370 @@ +function Get-CIPPDrift { + <# + .SYNOPSIS + Gets comprehensive drift information for a tenant including standards compliance and policy deviations + .DESCRIPTION + This function collects drift information by executing Get-CIPPTenantAlignment and comparing + tenant policies against standards templates. It identifies both standards deviations and + extra policies not defined in templates. + .PARAMETER TenantFilter + The tenant to get drift data for + .PARAMETER TemplateId + Optional specific template GUID to check drift for. If not specified, processes all templates. + .FUNCTIONALITY + Internal + .EXAMPLE + Get-CIPPDrift -TenantFilter "contoso.onmicrosoft.com" + .EXAMPLE + Get-CIPPDrift -TenantFilter "contoso.onmicrosoft.com" -TemplateId "12345-67890-abcdef" + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$TenantFilter, + + [Parameter(Mandatory = $false)] + [string]$TemplateId, + + [Parameter(Mandatory = $false)] + [switch]$AllTenants + ) + + try { + $AlignmentData = Get-CIPPTenantAlignment -TenantFilter $TenantFilter -TemplateId $TemplateId + if (-not $AlignmentData) { + Write-Warning "No alignment data found for tenant $TenantFilter" + return @() + } + + # Get existing drift states from the tenantDrift table + $DriftTable = Get-CippTable -tablename 'tenantDrift' + $DriftFilter = "PartitionKey eq '$TenantFilter'" + $ExistingDriftStates = @{} + try { + $DriftEntities = Get-CIPPAzDataTableEntity @DriftTable -Filter $DriftFilter + foreach ($Entity in $DriftEntities) { + $ExistingDriftStates[$Entity.StandardName] = $Entity.Status + } + } catch { + Write-Warning "Failed to get existing drift states: $($_.Exception.Message)" + } + + $Results = [System.Collections.Generic.List[object]]::new() + foreach ($Alignment in $AlignmentData) { + # Initialize deviation collections + $StandardsDeviations = [System.Collections.Generic.List[object]]::new() + $PolicyDeviations = [System.Collections.Generic.List[object]]::new() + + # Process standards compliance deviations + if ($Alignment.ComparisonDetails) { + foreach ($ComparisonItem in $Alignment.ComparisonDetails) { + if ($ComparisonItem.Compliant -eq $false -and $ComparisonItem.ComplianceStatus -eq 'Non-Compliant') { + $Status = if ($ExistingDriftStates.ContainsKey($ComparisonItem.StandardName)) { + $ExistingDriftStates[$ComparisonItem.StandardName] + } else { + 'New' + } + $StandardsDeviations.Add([PSCustomObject]@{ + standardName = $ComparisonItem.StandardName + expectedValue = 'Compliant' + receivedValue = $ComparisonItem.StandardValue + state = 'current' + Status = $Status + }) + } + } + } + + # Perform full policy collection + if ($AllTenants) { + # Use cached data when processing all tenants + $CacheTable = Get-CippTable -tablename 'cacheDrift' + $CacheFilter = "PartitionKey eq 'drift' and RowKey eq '$TenantFilter'" + $CachedData = $null + + try { + $CachedData = Get-CIPPAzDataTableEntity @CacheTable -Filter $CacheFilter | Select-Object -First 1 + } catch { + # Cache doesn't exist or error reading + } + + if ($CachedData -and $CachedData.CAJson -and $CachedData.IntuneJson) { + # Use cached data + try { + $TenantIntunePolicies = $CachedData.IntuneJson | ConvertFrom-Json + $TenantCAPolicies = $CachedData.CAJson | ConvertFrom-Json + } catch { + Write-Warning "Failed to parse cached data for tenant $TenantFilter" + $TenantIntunePolicies = @() + $TenantCAPolicies = @() + } + } else { + # No cache available, skip policy collection for AllTenants mode + $TenantIntunePolicies = @() + $TenantCAPolicies = @() + } + } else { + # Always get live data when not in AllTenants mode + $IntuneRequests = @( + @{ + id = 'deviceAppManagement' + url = 'deviceAppManagement/managedAppPolicies' + method = 'GET' + } + @{ + id = 'deviceCompliancePolicies' + url = 'deviceManagement/deviceCompliancePolicies' + method = 'GET' + } + @{ + id = 'groupPolicyConfigurations' + url = 'deviceManagement/groupPolicyConfigurations' + method = 'GET' + } + @{ + id = 'deviceConfigurations' + url = 'deviceManagement/deviceConfigurations' + method = 'GET' + } + @{ + id = 'configurationPolicies' + url = 'deviceManagement/configurationPolicies' + method = 'GET' + } + @{ + id = 'windowsDriverUpdateProfiles' + url = 'deviceManagement/windowsDriverUpdateProfiles' + method = 'GET' + } + @{ + id = 'windowsFeatureUpdateProfiles' + url = 'deviceManagement/windowsFeatureUpdateProfiles' + method = 'GET' + } + @{ + id = 'windowsQualityUpdatePolicies' + url = 'deviceManagement/windowsQualityUpdatePolicies' + method = 'GET' + } + @{ + id = 'windowsQualityUpdateProfiles' + url = 'deviceManagement/windowsQualityUpdateProfiles' + method = 'GET' + } + ) + + $TenantIntunePolicies = [System.Collections.Generic.List[object]]::new() + + try { + $IntuneGraphRequest = New-GraphBulkRequest -Requests $IntuneRequests -tenantid $TenantFilter -asapp $true + + foreach ($Request in $IntuneGraphRequest) { + if ($Request.body.value) { + foreach ($Policy in $Request.body.value) { + $TenantIntunePolicies.Add([PSCustomObject]@{ + Type = $Request.id + Policy = $Policy + }) + } + } + } + } catch { + Write-Warning "Failed to get Intune policies: $($_.Exception.Message)" + } + + # Get Conditional Access policies + try { + $CARequests = @( + @{ + id = 'policies' + url = 'identity/conditionalAccess/policies' + method = 'GET' + } + ) + $CAGraphRequest = New-GraphBulkRequest -Requests $CARequests -tenantid $TenantFilter -asapp $true + $TenantCAPolicies = ($CAGraphRequest | Where-Object { $_.id -eq 'policies' }).body.value + } catch { + Write-Warning "Failed to get Conditional Access policies: $($_.Exception.Message)" + $TenantCAPolicies = @() + } + + # Always update cache with fresh data + try { + $CacheTable = Get-CippTable -tablename 'cacheDrift' + $IntuneJsonString = "`"$($TenantIntunePolicies | ConvertTo-Json -Depth 10 -Compress | ForEach-Object { $_ -replace '"', '\"' })`"" + $CAJsonString = "`"$($TenantCAPolicies | ConvertTo-Json -Depth 10 -Compress | ForEach-Object { $_ -replace '"', '\"' })`"" + + $CacheEntity = @{ + PartitionKey = 'drift' + RowKey = $TenantFilter + IntuneJson = $IntuneJsonString + CAJson = $CAJsonString + } + Add-CIPPAzDataTableEntity @CacheTable -Entity $CacheEntity -Force + } catch { + Write-Warning "Failed to cache policy data: $($_.Exception.Message)" + } + } + + if ($Alignment.standardSettings) { + if ($Alignment.standardSettings.IntuneTemplates) { + $IntuneTemplateIds = $Alignment.standardSettings.IntuneTemplate.TemplateList | ForEach-Object { $_.value } + } + if ($Alignment.standardSettings.ConditionalAccessTemplate) { + $CATemplateIds = $Alignment.standardSettings.ConditionalAccessTemplate.TemplateList | ForEach-Object { $_.value } + } + } + + # Get actual CA templates from templates table + if ($CATemplateIds.Count -gt 0) { + try { + $CATable = Get-CippTable -tablename 'templates' + $CAFilter = "PartitionKey eq 'CATemplate'" + $AllCATemplates = (Get-CIPPAzDataTableEntity @CATable -Filter $CAFilter) | ForEach-Object { + $data = $_.JSON | ConvertFrom-Json -Depth 100 + $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.GUID -Force + $data + } | Sort-Object -Property displayName + + $TemplateCATemplates = $AllCATemplates | Where-Object { $_.GUID -in $CATemplateIds } + } catch { + Write-Warning "Failed to get CA templates: $($_.Exception.Message)" + } + } + + # Get actual Intune templates from templates table + if ($IntuneTemplateIds.Count -gt 0) { + try { + $IntuneTable = Get-CippTable -tablename 'templates' + $IntuneFilter = "PartitionKey eq 'IntuneTemplate'" + $RawIntuneTemplates = (Get-CIPPAzDataTableEntity @IntuneTable -Filter $IntuneFilter) + $AllIntuneTemplates = $RawIntuneTemplates | ForEach-Object { + try { + $JSONData = $_.JSON | ConvertFrom-Json -Depth 100 -ErrorAction SilentlyContinue + $data = $JSONData.RAWJson | ConvertFrom-Json -Depth 100 -ErrorAction SilentlyContinue + $data | Add-Member -NotePropertyName 'displayName' -NotePropertyValue $JSONData.Displayname -Force + $data | Add-Member -NotePropertyName 'description' -NotePropertyValue $JSONData.Description -Force + $data | Add-Member -NotePropertyName 'Type' -NotePropertyValue $JSONData.Type -Force + $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.RowKey -Force + $data + } catch { + # Skip invalid templates + } + } | Sort-Object -Property displayName + + $TemplateIntuneTemplates = $AllIntuneTemplates | Where-Object { $_.GUID -in $IntuneTemplateIds } + } catch { + Write-Warning "Failed to get Intune templates: $($_.Exception.Message)" + } + } + + # Check for extra Intune policies not in template + foreach ($TenantPolicy in $TenantIntunePolicies) { + $PolicyFound = $false + $TenantPolicyName = if ($TenantPolicy.Policy.displayName) { $TenantPolicy.Policy.displayName } else { $TenantPolicy.Policy.name } + + foreach ($TemplatePolicy in $TemplateIntuneTemplates) { + $TemplatePolicyName = if ($TemplatePolicy.displayName) { $TemplatePolicy.displayName } else { $TemplatePolicy.name } + + if ($TemplatePolicy.displayName -eq $TenantPolicy.Policy.displayName -or + $TemplatePolicy.name -eq $TenantPolicy.Policy.name -or + $TemplatePolicy.displayName -eq $TenantPolicy.Policy.name -or + $TemplatePolicy.name -eq $TenantPolicy.Policy.displayName) { + $PolicyFound = $true + break + } + } + + if (-not $PolicyFound) { + $PolicyKey = "IntuneTemplates.$($TenantPolicy.Policy.id)" + $Status = if ($ExistingDriftStates.ContainsKey($PolicyKey)) { + $ExistingDriftStates[$PolicyKey] + } else { + 'New' + } + $PolicyDeviation = [PSCustomObject]@{ + standardName = $PolicyKey + standardDisplayName = "Intune - $TenantPolicyName" + expectedValue = 'Not defined in template' + receivedValue = ($TenantPolicy.Policy | ConvertTo-Json -Depth 10 -Compress) + state = 'current' + Status = $Status + } + $PolicyDeviations.Add($PolicyDeviation) + } + } + + # Check for extra Conditional Access policies not in template + foreach ($TenantCAPolicy in $TenantCAPolicies) { + $PolicyFound = $false + + foreach ($TemplateCAPolicy in $TemplateCATemplates) { + if ($TemplateCAPolicy.displayName -eq $TenantCAPolicy.displayName) { + $PolicyFound = $true + break + } + } + + if (-not $PolicyFound) { + $PolicyKey = "ConditionalAccessTemplates.$($TenantCAPolicy.id)" + $Status = if ($ExistingDriftStates.ContainsKey($PolicyKey)) { + $ExistingDriftStates[$PolicyKey] + } else { + 'New' + } + $PolicyDeviation = [PSCustomObject]@{ + standardName = $PolicyKey + standardDisplayName = "Conditional Access - $($TenantCAPolicy.displayName)" + expectedValue = 'Not defined in template' + receivedValue = ($TenantCAPolicy | ConvertTo-Json -Depth 10 -Compress) + state = 'current' + Status = $Status + } + $PolicyDeviations.Add($PolicyDeviation) + } + } + + + # Combine all deviations and filter by status + $AllDeviations = [System.Collections.Generic.List[object]]::new() + $AllDeviations.AddRange($StandardsDeviations) + $AllDeviations.AddRange($PolicyDeviations) + + # Filter deviations by status for counting + $NewDeviations = $AllDeviations | Where-Object { $_.Status -eq 'New' } + $AcceptedDeviations = $AllDeviations | Where-Object { $_.Status -eq 'Accepted' } + $DeniedDeviations = $AllDeviations | Where-Object { $_.Status -eq 'Denied' } + $CustomerSpecificDeviations = $AllDeviations | Where-Object { $_.Status -eq 'CustomerSpecific' } + + # Current deviations are New + Denied (not accepted or customer specific) + $CurrentDeviations = $AllDeviations | Where-Object { $_.Status -in @('New', 'Denied') } + + $Result = [PSCustomObject]@{ + tenantFilter = $TenantFilter + standardName = $Alignment.StandardName + standardId = $Alignment.StandardId + alignmentScore = $Alignment.AlignmentScore + acceptedDeviationsCount = $AcceptedDeviations.Count + currentDeviationsCount = $CurrentDeviations.Count + deniedDeviationsCount = $DeniedDeviations.Count + customerSpecificDeviationsCount = $CustomerSpecificDeviations.Count + newDeviationsCount = $NewDeviations.Count + alignedCount = $Alignment.CompliantStandards + currentDeviations = @($CurrentDeviations) + acceptedDeviations = @($AcceptedDeviations) + customerSpecificDeviations = @($CustomerSpecificDeviations) + deniedDeviations = @($DeniedDeviations) + allDeviations = @($AllDeviations) + latestDataCollection = $Alignment.LatestDataCollection + } + + $Results.Add($Result) + } + + return @($Results) + + } catch { + Write-Error "Error getting drift data: $($_.Exception.Message)" + throw + } +} diff --git a/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 b/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 new file mode 100644 index 000000000000..009eb21510f5 --- /dev/null +++ b/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 @@ -0,0 +1,53 @@ +function Set-CIPPDriftDeviation { + <# + .SYNOPSIS + Sets the status of a drift deviation in the tenant drift table + .DESCRIPTION + This function stores drift deviation status changes in the tenantDrift table. + It tracks when deviations are accepted, denied, or marked as customer specific. + .PARAMETER TenantFilter + The tenant filter (used as PartitionKey) + .PARAMETER StandardName + The standard name (used as RowKey, with '.' replaced by '_') + .PARAMETER Status + The status to set. Valid values: Accepted, New, Denied, CustomerSpecific + .FUNCTIONALITY + Internal + .EXAMPLE + Set-CIPPDriftDeviation -TenantFilter "contoso.onmicrosoft.com" -StandardName "IntuneTemplates.12345" -Status "Accepted" + .EXAMPLE + Set-CIPPDriftDeviation -TenantFilter "contoso.onmicrosoft.com" -StandardName "standards.passwordComplexity" -Status "CustomerSpecific" + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$TenantFilter, + + [Parameter(Mandatory = $true)] + [string]$StandardName, + + [Parameter(Mandatory = $true)] + [ValidateSet('Accepted', 'New', 'Denied', 'CustomerSpecific')] + [string]$Status + ) + + try { + $Table = Get-CippTable -tablename 'tenantDrift' + $RowKey = $StandardName -replace '\.', '_' + $Entity = @{ + PartitionKey = $TenantFilter + RowKey = $RowKey + StandardName = $StandardName + Status = $Status + LastModified = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + } + $Result = Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force + + Write-Verbose "Successfully set drift deviation status for $StandardName to $Status" + return "Successfully set drift deviation status for $StandardName to $Status" + + } catch { + Write-Error "Error setting drift deviation status: $($_.Exception.Message)" + throw + } +} From 41cb7f9d12d313eeb363a89787da3c6600acce07 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:54:55 +0200 Subject: [PATCH 40/85] change how drift detection works --- Modules/CIPPCore/Public/Get-CIPPDrift.ps1 | 14 +++++++------- ...nvoke-CIPPStandardConditionalAccessTemplate.ps1 | 7 +++++-- .../Invoke-CIPPStandardIntuneTemplate.ps1 | 4 ++++ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 index 1be95114d8d3..f4953b3c666a 100644 --- a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 @@ -30,7 +30,7 @@ function Get-CIPPDrift { ) try { - $AlignmentData = Get-CIPPTenantAlignment -TenantFilter $TenantFilter -TemplateId $TemplateId + $AlignmentData = Get-CIPPTenantAlignment -TenantFilter $TenantFilter -TemplateId $TemplateId | Where-Object -Property standardType -EQ 'drift' if (-not $AlignmentData) { Write-Warning "No alignment data found for tenant $TenantFilter" return @() @@ -58,7 +58,7 @@ function Get-CIPPDrift { # Process standards compliance deviations if ($Alignment.ComparisonDetails) { foreach ($ComparisonItem in $Alignment.ComparisonDetails) { - if ($ComparisonItem.Compliant -eq $false -and $ComparisonItem.ComplianceStatus -eq 'Non-Compliant') { + if ($ComparisonItem.Compliant -ne $true) { $Status = if ($ExistingDriftStates.ContainsKey($ComparisonItem.StandardName)) { $ExistingDriftStates[$ComparisonItem.StandardName] } else { @@ -196,9 +196,9 @@ function Get-CIPPDrift { $CacheEntity = @{ PartitionKey = 'drift' - RowKey = $TenantFilter - IntuneJson = $IntuneJsonString - CAJson = $CAJsonString + RowKey = $TenantFilter + IntuneJson = $IntuneJsonString + CAJson = $CAJsonString } Add-CIPPAzDataTableEntity @CacheTable -Entity $CacheEntity -Force } catch { @@ -285,7 +285,7 @@ function Get-CIPPDrift { $PolicyDeviation = [PSCustomObject]@{ standardName = $PolicyKey standardDisplayName = "Intune - $TenantPolicyName" - expectedValue = 'Not defined in template' + expectedValue = 'This policy only exists in the tenant, not in the template.' receivedValue = ($TenantPolicy.Policy | ConvertTo-Json -Depth 10 -Compress) state = 'current' Status = $Status @@ -315,7 +315,7 @@ function Get-CIPPDrift { $PolicyDeviation = [PSCustomObject]@{ standardName = $PolicyKey standardDisplayName = "Conditional Access - $($TenantCAPolicy.displayName)" - expectedValue = 'Not defined in template' + expectedValue = 'This policy only exists in the tenant, not in the template.' receivedValue = ($TenantCAPolicy | ConvertTo-Json -Depth 10 -Compress) state = 'current' Status = $Status diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 index f12c32b6e2e9..08bfff91403b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 @@ -30,10 +30,13 @@ function Invoke-CIPPStandardConditionalAccessTemplate { #> param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'ConditionalAccess' - $TestResult = Test-CIPPStandardLicense -StandardName 'ConditionalAccessTemplate' -TenantFilter $Tenant -RequiredCapabilities @('AAD_PREMIUM', 'AAD_PREMIUM_P2') $Table = Get-CippTable -tablename 'templates' - + $TestResult = Test-CIPPStandardLicense -StandardName 'ConditionalAccessTemplate' -TenantFilter $Tenant -RequiredCapabilities @('AAD_PREMIUM', 'AAD_PREMIUM_P2') if ($TestResult -eq $false) { + #writing to each item that the license is not present. + $settings.TemplateList | ForEach-Object { + Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($_.value)" -FieldValue 'This tenant does not have the required license for this standard.' -Tenant $Tenant + } Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 index 04b3f8eb6c86..ed875952af80 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 @@ -35,6 +35,10 @@ function Invoke-CIPPStandardIntuneTemplate { ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'intuneTemplate' if ($TestResult -eq $false) { + #writing to each item that the license is not present. + $settings.TemplateList | ForEach-Object { + Set-CIPPStandardsCompareField -FieldName "standards.IntuneTemplate.$($_.value)" -FieldValue 'This tenant does not have the required license for this standard.' -Tenant $Tenant + } Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. From d4e30a7711b2be011bd4c939eb25aebe3fb0ed41 Mon Sep 17 00:00:00 2001 From: Zac Richards <107489668+Zacgoose@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:19:27 +0800 Subject: [PATCH 41/85] Extra info for pending app approvals --- .../CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 index 883413405345..d9a386be6862 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 @@ -15,6 +15,7 @@ function Get-CIPPAlertNewAppApproval { try { $Approvals = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/identityGovernance/appConsent/appConsentRequests?`$filter=userConsentRequests/any (u:u/status eq 'InProgress')" -tenantid $TenantFilter if ($Approvals.count -gt 0) { + $TenantGUID = (Get-Tenants -TenantFilter $TenantFilter -SkipDomains).customerId $AlertData = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($App in $Approvals) { $userConsentRequests = New-GraphGetRequest -Uri "https://graph.microsoft.com/v1.0/identityGovernance/appConsent/appConsentRequests/$($App.id)/userConsentRequests" -tenantid $TenantFilter @@ -32,10 +33,13 @@ function Get-CIPPAlertNewAppApproval { AppName = $App.appDisplayName RequestUser = $_.createdBy.user.userPrincipalName Reason = $_.reason + RequestDate = $_.createdDateTime + Status = $_.status # Will allways be InProgress as we filter to only get these but this will reduce confusion when an alert is generated AppId = $App.appId Scopes = ($App.pendingScopes.displayName -join ', ') ConsentURL = $consentUrl Tenant = $TenantFilter + TenantId = $TenantGUID } $AlertData.Add($Message) } From 4e407e0cc96dfa033f8c2cfc1810aa3be6029ae5 Mon Sep 17 00:00:00 2001 From: Zac Richards <107489668+Zacgoose@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:28:14 +0800 Subject: [PATCH 42/85] Fixed CIPPOffboardingJob missing $DisplayName for the -DisplayName param --- .../Identity/Administration/Users/Invoke-CIPPOffboardingJob.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-CIPPOffboardingJob.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-CIPPOffboardingJob.ps1 index 0b1c0254759e..931fcd0af54b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-CIPPOffboardingJob.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-CIPPOffboardingJob.ps1 @@ -32,7 +32,7 @@ function Invoke-CIPPOffboardingJob { } { $_.ResetPass -eq $true } { try { - Set-CIPPResetPassword -tenantFilter $TenantFilter -DisplayName -UserID $username -Headers $Headers -APIName $APIName + Set-CIPPResetPassword -tenantFilter $TenantFilter -DisplayName $DisplayName -UserID $username -Headers $Headers -APIName $APIName } catch { $_.Exception.Message } From f5929c0f1e68d128c85fbd4702c45aabd3a24fee Mon Sep 17 00:00:00 2001 From: Zac Richards <107489668+Zacgoose@users.noreply.github.com> Date: Tue, 29 Jul 2025 12:04:34 +0800 Subject: [PATCH 43/85] Better exception message handling for failed json conversion --- .../Public/GraphHelper/New-GraphBulkRequest.ps1 | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/GraphHelper/New-GraphBulkRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-GraphBulkRequest.ps1 index 12b72accf29e..b6c925415458 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-GraphBulkRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-GraphBulkRequest.ps1 @@ -58,8 +58,20 @@ function New-GraphBulkRequest { } } catch { - $Message = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue).error.message - if ($null -eq $Message) { $Message = $($_.Exception.Message) } + # Try to parse ErrorDetails.Message as JSON + if ($_.ErrorDetails.Message) { + try { + $ErrorJson = $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction Stop + $Message = $ErrorJson.error.message + } catch { + $Message = $_.ErrorDetails.Message + } + } + + if ([string]::IsNullOrEmpty($Message)) { + $Message = $_.Exception.Message + } + if ($Message -ne 'Request not applicable to target tenant.') { $Tenant.LastGraphError = $Message ?? '' $Tenant.GraphErrorCount++ From 13c0523191350557261833ab1354dbc310e49ab1 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Tue, 29 Jul 2025 12:06:43 +0200 Subject: [PATCH 44/85] expand groups --- .../CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 index b9ce837ba405..7e6b9f1397ae 100644 --- a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 +++ b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 @@ -101,7 +101,13 @@ function Get-CIPPTenantAlignment { if ($Template.tenantFilter -and $Template.tenantFilter.Count -gt 0) { # Extract tenant values from the tenantFilter array - $TenantValues = $Template.tenantFilter | ForEach-Object { $_.value } + $TenantValues = $Template.tenantFilter | ForEach-Object { + if ($_.type -eq 'group') { + (Get-TenantGroups -GroupId $_.value).members.defaultDomainName + } else { + $_.value + } + } if ($TenantValues -contains 'AllTenants') { $AppliestoAllTenants = $true From bbd4deae75ff3db7a40f5cbd20b7bcd1cfa30bec Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Tue, 29 Jul 2025 18:55:50 +0200 Subject: [PATCH 45/85] fixes incorrect drift measurements --- .../Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 | 2 +- .../Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 index 08bfff91403b..fd220aab81b3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 @@ -31,7 +31,7 @@ function Invoke-CIPPStandardConditionalAccessTemplate { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'ConditionalAccess' $Table = Get-CippTable -tablename 'templates' - $TestResult = Test-CIPPStandardLicense -StandardName 'ConditionalAccessTemplate' -TenantFilter $Tenant -RequiredCapabilities @('AAD_PREMIUM', 'AAD_PREMIUM_P2') + $TestResult = Test-CIPPStandardLicense -StandardName 'ConditionalAccessTemplates_general' -TenantFilter $Tenant -RequiredCapabilities @('AAD_PREMIUM', 'AAD_PREMIUM_P2') if ($TestResult -eq $false) { #writing to each item that the license is not present. $settings.TemplateList | ForEach-Object { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 index ed875952af80..53fd2f4cee4e 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 @@ -31,7 +31,7 @@ function Invoke-CIPPStandardIntuneTemplate { https://docs.cipp.app/user-documentation/tenant/standards/list-standards #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'IntuneTemplate' -TenantFilter $Tenant -RequiredCapabilities @('INTUNE_A', 'MDM_Services', 'EMS', 'SCCM', 'MICROSOFTINTUNEPLAN1') + $TestResult = Test-CIPPStandardLicense -StandardName 'IntuneTemplate_general' -TenantFilter $Tenant -RequiredCapabilities @('INTUNE_A', 'MDM_Services', 'EMS', 'SCCM', 'MICROSOFTINTUNEPLAN1') ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'intuneTemplate' if ($TestResult -eq $false) { From 5c1822b191de0b50c9684a20e3da537546f39b03 Mon Sep 17 00:00:00 2001 From: David Szpunar Date: Tue, 29 Jul 2025 14:09:09 -0400 Subject: [PATCH 46/85] Add the ID of the consent request to output to enable client deduplication --- Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 index 883413405345..ca8c04d04b59 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 @@ -29,6 +29,7 @@ function Get-CIPPAlertNewAppApproval { } $Message = [PSCustomObject]@{ + RequestId = $_.id AppName = $App.appDisplayName RequestUser = $_.createdBy.user.userPrincipalName Reason = $_.reason From 87fb051f22d4902466038f44351cd7bdbb290827 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Tue, 29 Jul 2025 22:22:27 +0200 Subject: [PATCH 47/85] fix bug --- Modules/CIPPCore/Public/Get-CIPPDrift.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 index f4953b3c666a..0c3d0dbdc11f 100644 --- a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 @@ -207,7 +207,7 @@ function Get-CIPPDrift { } if ($Alignment.standardSettings) { - if ($Alignment.standardSettings.IntuneTemplates) { + if ($Alignment.standardSettings.IntuneTemplate) { $IntuneTemplateIds = $Alignment.standardSettings.IntuneTemplate.TemplateList | ForEach-Object { $_.value } } if ($Alignment.standardSettings.ConditionalAccessTemplate) { From 08ade0c81921dae71a3d18d3b73f0ee3f9a2da24 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 29 Jul 2025 17:37:52 -0400 Subject: [PATCH 48/85] check for empty allowlist arrays in spoofwarn --- .../Invoke-CIPPStandardSpoofWarn.ps1 | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 index 9bc4b571f543..86aee02d78de 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 @@ -48,16 +48,22 @@ function Invoke-CIPPStandardSpoofWarn { # Test if all entries in the AllowListAdd variable are in the AllowList $AllowListCorrect = $true - $AllowListAddEntries = foreach ($entry in $AllowListAdd) { - if ($CurrentInfo.AllowList -notcontains $entry) { - $AllowListCorrect = $false - Write-Host "AllowList entry $entry not found in current AllowList" - $entry - } else { - Write-Host "AllowList entry $entry found in current AllowList." + + if ($AllowListAdd -eq $null -or $AllowListAdd.Count -eq 0) { + Write-Host 'No AllowList entries provided, skipping AllowList check.' + $AllowListAdd = @{'@odata.type' = '#Exchange.GenericHashTable'; Add = @() } + } else { + $AllowListAddEntries = foreach ($entry in $AllowListAdd) { + if ($CurrentInfo.AllowList -notcontains $entry) { + $AllowListCorrect = $false + Write-Host "AllowList entry $entry not found in current AllowList" + $entry + } else { + Write-Host "AllowList entry $entry found in current AllowList." + } } + $AllowListAdd = @{'@odata.type' = '#Exchange.GenericHashTable'; Add = $AllowListAddEntries } } - $AllowListAdd = @{'@odata.type' = '#Exchange.GenericHashTable'; Add = $AllowListAddEntries } # Debug output # Write-Host ($CurrentInfo | ConvertTo-Json -Depth 10) From 12b7fc5e6373360b77f10ea075d56062577195e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Tue, 29 Jul 2025 23:48:27 +0200 Subject: [PATCH 49/85] Feat: Add securityEnabled field switch --- .../Administration/Groups/Invoke-EditGroup.ps1 | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-EditGroup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-EditGroup.ps1 index 18cd79effab4..9097ad19ab7a 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-EditGroup.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-EditGroup.ps1 @@ -45,12 +45,15 @@ function Invoke-EditGroup { target = $GroupId }) } else { + # Use new securityEnabled value if provided, otherwise keep original + $SecurityEnabled = $null -ne $UserObj.securityEnabled ? $UserObj.securityEnabled : $OrgGroup.securityEnabled + $PatchObj = @{ displayName = $UserObj.displayName description = $UserObj.description mailNickname = $UserObj.mailNickname mailEnabled = $OrgGroup.mailEnabled - securityEnabled = $OrgGroup.securityEnabled + securityEnabled = $SecurityEnabled } Write-Host "body: $($PatchObj | ConvertTo-Json -Depth 10 -Compress)" -ForegroundColor Yellow if ($UserObj.membershipRules) { $PatchObj | Add-Member -MemberType NoteProperty -Name 'membershipRule' -Value $UserObj.membershipRules -Force } @@ -58,6 +61,13 @@ function Invoke-EditGroup { $null = New-GraphPOSTRequest -type PATCH -uri "https://graph.microsoft.com/beta/groups/$($GroupId)" -tenantid $UserObj.tenantFilter -body ($PatchObj | ConvertTo-Json -Depth 10 -Compress) $Results.Add("Success - Edited group properties for $($GroupName) group. It might take some time to reflect the changes.") Write-LogMessage -headers $Headers -API $APIName -tenant $UserObj.tenantFilter -message "Edited group properties for $($GroupName) group" -Sev 'Info' + + # Log securityEnabled changes specifically + if ($null -ne $UserObj.securityEnabled -and $UserObj.securityEnabled -ne $OrgGroup.securityEnabled) { + $securityStatusText = "Security capabilities $($UserObj.securityEnabled ? 'enabled' : 'disabled') for group $($GroupName)" + Write-LogMessage -headers $Headers -API $APIName -tenant $UserObj.tenantFilter -message $securityStatusText -Sev 'Info' + $Results.Add($securityStatusText) + } } catch { $Results.Add("Error - Failed to edit group properties: $($_.Exception.Message)") Write-LogMessage -headers $Headers -API $APIName -tenant $UserObj.tenantFilter -message "Failed to patch group: $($_.Exception.Message)" -Sev 'Error' From 1051a2c69ebb3e8f79c36b7d89b474021298d8a4 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Tue, 29 Jul 2025 23:57:37 +0200 Subject: [PATCH 50/85] Work so far --- .../Invoke-ExecUpdateDriftDeviation.ps1 | 15 +++++++++++++++ Modules/CIPPCore/Public/Get-CIPPDrift.ps1 | 16 ---------------- .../CIPPCore/Public/Set-CIPPDriftDeviation.ps1 | 2 +- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 index deae5e3c0663..7e419defe23c 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 @@ -38,6 +38,21 @@ function Invoke-ExecUpdateDriftDeviation { result = $Result } Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Updated drift deviation status for $($Deviation.standardName) to $($Deviation.status)" -Sev 'Info' + if ($Deviation.status -eq 'DeniedRemediate') { + $Setting = $Deviation.standardName -replace 'standards.', '' + $StandardTemplate = Get-CIPPTenantAlignment -TenantFilter $TenantFilter | Where-Object -Property standardType -EQ 'drift' + $StandardTemplate = $StandardTemplate.$Setting + $StandardTemplate.action = @( + @{label = 'Report'; value = 'Report' }, + @{ label = 'Remediate'; value = 'Remediate' } + ) + #idea here is to make a system job that triggers the remediation process, so that users can click on "Deniedremediate" + #That job then launches a single standard run, it gets the same input as an orch, but is just a scheduled job. + + } + if ($Deviation.status -eq 'deniedDelete') { + #Here we look at the policy ID received and the type, and nuke it. + } } catch { [PSCustomObject]@{ standardName = $Deviation.standardName diff --git a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 index 0c3d0dbdc11f..8030867d3acc 100644 --- a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 @@ -188,22 +188,6 @@ function Get-CIPPDrift { $TenantCAPolicies = @() } - # Always update cache with fresh data - try { - $CacheTable = Get-CippTable -tablename 'cacheDrift' - $IntuneJsonString = "`"$($TenantIntunePolicies | ConvertTo-Json -Depth 10 -Compress | ForEach-Object { $_ -replace '"', '\"' })`"" - $CAJsonString = "`"$($TenantCAPolicies | ConvertTo-Json -Depth 10 -Compress | ForEach-Object { $_ -replace '"', '\"' })`"" - - $CacheEntity = @{ - PartitionKey = 'drift' - RowKey = $TenantFilter - IntuneJson = $IntuneJsonString - CAJson = $CAJsonString - } - Add-CIPPAzDataTableEntity @CacheTable -Entity $CacheEntity -Force - } catch { - Write-Warning "Failed to cache policy data: $($_.Exception.Message)" - } } if ($Alignment.standardSettings) { diff --git a/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 b/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 index 009eb21510f5..3135ebb2545d 100644 --- a/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 @@ -27,7 +27,7 @@ function Set-CIPPDriftDeviation { [string]$StandardName, [Parameter(Mandatory = $true)] - [ValidateSet('Accepted', 'New', 'Denied', 'CustomerSpecific')] + [ValidateSet('Accepted', 'New', 'Denied', 'CustomerSpecific', 'DeniedRemediate', 'DeniedDelete')] [string]$Status ) From 5ade4e13de203c3fc01af4c07e398d1437d7c5e8 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 29 Jul 2025 18:50:09 -0400 Subject: [PATCH 51/85] fix issue with multiple templates run at the same time --- .../Tenant/Standards/Invoke-ExecStandardsRun.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 index f2440e006895..865686e419ca 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-ExecStandardsRun { +function Invoke-ExecStandardsRun { <# .FUNCTIONALITY Entrypoint @@ -38,7 +38,7 @@ Function Invoke-ExecStandardsRun { $ProcessorFunction = [PSCustomObject]@{ PartitionKey = 'Function' - RowKey = "Invoke-CIPPStandardsRun-$TenantFilter" + RowKey = "Invoke-CIPPStandardsRun-$TenantFilter-$TemplateId" FunctionName = 'Invoke-CIPPStandardsRun' Parameters = [string](ConvertTo-Json -Compress -InputObject @{ TenantFilter = $TenantFilter From 3c432bbe205ee0053bbb2e53c241f0eb5489962f Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 29 Jul 2025 18:50:28 -0400 Subject: [PATCH 52/85] filter to the correct standards report --- Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 index 7e6b9f1397ae..811983f6e635 100644 --- a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 +++ b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 @@ -52,7 +52,7 @@ function Get-CIPPTenantAlignment { # Get standards comparison data $StandardsTable = Get-CIPPTable -TableName 'CippStandardsReports' - $AllStandards = Get-CIPPAzDataTableEntity @StandardsTable + $AllStandards = Get-CIPPAzDataTableEntity @StandardsTable -Filter "PartitionKey ne 'StandardReport'" # Filter by tenant if specified $Standards = if ($TenantFilter) { @@ -263,6 +263,7 @@ function Get-CIPPTenantAlignment { return $Results } catch { Write-Error "Error getting tenant alignment data: $($_.Exception.Message)" + Write-Information $_.InvocationInfo.PositionMessage throw } } From 8ae4a14c1496806f9dcb7b81a2f1d32c4a25d3f2 Mon Sep 17 00:00:00 2001 From: Zac Richards <107489668+Zacgoose@users.noreply.github.com> Date: Wed, 30 Jul 2025 22:18:36 +0800 Subject: [PATCH 53/85] Enhance mailbox permission modification and bulk request tracking Refactored Invoke-ExecModifyMBPerms.ps1 to support multiple mailbox request formats, improved error handling, and implemented precise result mapping using operation GUIDs for bulk permission changes. Updated New-ExoBulkRequest.ps1 to propagate and track operation GUIDs through batch requests, enabling accurate correlation of results and errors to specific permission operations. --- .../Invoke-ExecModifyMBPerms.ps1 | 358 ++++++++++++++---- .../Public/GraphHelper/New-ExoBulkRequest.ps1 | 85 ++++- 2 files changed, 355 insertions(+), 88 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecModifyMBPerms.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecModifyMBPerms.ps1 index cddc705a3556..ff03133f3675 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecModifyMBPerms.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecModifyMBPerms.ps1 @@ -11,188 +11,382 @@ Function Invoke-ExecModifyMBPerms { param($Request, $TriggerMetadata) $APIName = $Request.Params.CIPPEndpoint - Write-LogMessage -headers $Request.Headers -API $APINAME-message 'Accessed this API' -Sev 'Debug' + Write-LogMessage -headers $Request.Headers -API $APINAME -message 'Accessed this API' -Sev 'Debug' - $Username = $request.body.userID - $Tenantfilter = $request.body.tenantfilter - $Permissions = $request.body.permissions - - if ($username -eq $null) { exit } - - $userid = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($username)" -tenantid $Tenantfilter).id + # Extract mailbox requests - handle all three formats + $MailboxRequests = $null $Results = [System.Collections.ArrayList]::new() - # Convert permissions to array format if it's an object with numeric keys - if ($Permissions -is [PSCustomObject]) { - if ($Permissions.PSObject.Properties.Name -match '^\d+$') { - $Permissions = $Permissions.PSObject.Properties.Value - } - else { - $Permissions = @($Permissions) - } + # Direct array format + if ($request.body -is [array]) { + $MailboxRequests = $request.body + } + # Bulk format with mailboxRequests property + elseif ($request.body.mailboxRequests) { + $MailboxRequests = $request.body.mailboxRequests + } + # Legacy single mailbox format + elseif ($request.body.userID -and $request.body.permissions) { + $MailboxRequests = @([PSCustomObject]@{ + userID = $request.body.userID + tenantFilter = $request.body.tenantFilter + permissions = $request.body.permissions + }) + } + + if (-not $MailboxRequests -or $MailboxRequests.Count -eq 0) { + Write-LogMessage -headers $Request.Headers -API $APINAME -message 'No mailbox requests provided' -Sev 'Error' + $body = [pscustomobject]@{'Results' = @("No mailbox requests provided") } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = $Body + }) + return } - foreach ($Permission in $Permissions) { - $PermissionLevels = $Permission.PermissionLevel - $Modification = $Permission.Modification - $AutoMap = if ($Permission.PSObject.Properties.Name -contains 'AutoMap') { $Permission.AutoMap } else { $true } + $TenantFilter = $Request.body.tenantFilter + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Processing permission changes for $($MailboxRequests.Count) mailboxes" -Sev 'Info' -tenant $TenantFilter - # Handle multiple permission levels separated by commas - if ($PermissionLevels -like "*,*") { - $PermissionLevelArray = $PermissionLevels -split ',' | ForEach-Object { $_.Trim() } - } - else { - $PermissionLevelArray = @($PermissionLevels.Trim()) + # Build cmdlet array for processing + $CmdletArray = [System.Collections.ArrayList]::new() + $CmdletMetadataArray = [System.Collections.ArrayList]::new() + $GuidToMetadataMap = @{} # Map GUIDs to our metadata + $UserLookupCache = @{} + + foreach ($MailboxRequest in $MailboxRequests) { + $Username = $MailboxRequest.userID + $Permissions = $MailboxRequest.permissions + + if ([string]::IsNullOrEmpty($Username)) { + $null = $Results.Add("Skipped mailbox with missing userID") + continue } - # Handle UserID as array of objects or single value - $TargetUsers = if ($Permission.UserID -is [array]) { - $Permission.UserID | ForEach-Object { $_.value } + # User lookup with caching for bulk operations + if (-not $UserLookupCache.ContainsKey($Username)) { + try { + $UserObject = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($Username)" -tenantid $TenantFilter + $UserLookupCache[$Username] = $UserObject.userPrincipalName + } + catch { + try { + $UserObject = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$filter=userPrincipalName eq '$Username'" -tenantid $TenantFilter + if ($UserObject.value -and $UserObject.value.Count -gt 0) { + $UserLookupCache[$Username] = $UserObject.value[0].userPrincipalName + } else { + throw "User not found" + } + } + catch { + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Could not find user $($Username)" -Sev 'Error' -tenant $TenantFilter + $null = $Results.Add("Could not find user $($Username)") + continue + } + } } - else { - @($Permission.UserID) + $UserId = $UserLookupCache[$Username] + + # Convert permissions to array if needed + if ($Permissions -is [PSCustomObject]) { + if ($Permissions.PSObject.Properties.Name -match '^\d+$') { + $Permissions = $Permissions.PSObject.Properties.Value + } else { + $Permissions = @($Permissions) + } } - foreach ($TargetUser in $TargetUsers) { - foreach ($PermissionLevel in $PermissionLevelArray) { - try { + foreach ($Permission in $Permissions) { + $PermissionLevels = $Permission.PermissionLevel + $Modification = $Permission.Modification + $AutoMap = if ($Permission.PSObject.Properties.Name -contains 'AutoMap') { $Permission.AutoMap } else { $true } + + # Handle multiple permission levels + $PermissionLevelArray = if ($PermissionLevels -like "*,*") { + $PermissionLevels -split ',' | ForEach-Object { $_.Trim() } + } else { + @($PermissionLevels.Trim()) + } + + # Extract target users from UserID (handle array of objects or single values) + $TargetUsers = if ($Permission.UserID -is [array]) { + $Permission.UserID | ForEach-Object { + if ($_ -is [PSCustomObject] -and $_.value) { + $_.value + } else { + $_.ToString() + } + } + } else { + if ($Permission.UserID -is [PSCustomObject] -and $Permission.UserID.value) { + @($Permission.UserID.value) + } else { + @($Permission.UserID.ToString()) + } + } + + foreach ($TargetUser in $TargetUsers) { + foreach ($PermissionLevel in $PermissionLevelArray) { + + # Create cmdlet parameters based on permission type and action + $CmdletParams = @{} + $CmdletName = "" + $ExpectedResult = "" + switch ($PermissionLevel) { 'FullAccess' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-mailboxpermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('FullAccess') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Shared Mailbox permissions (FullAccess)") - } - else { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Add-MailboxPermission' -cmdParams @{ - Identity = $userid + $ExpectedResult = "Removed $($TargetUser) from $($Username) FullAccess permissions" + } else { + $CmdletName = 'Add-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('FullAccess') automapping = $AutoMap Confirm = $false } - $null = $results.Add("Granted $($TargetUser) access to $($username) Mailbox (FullAccess) with automapping set to $($AutoMap)") + $ExpectedResult = "Granted $($TargetUser) FullAccess to $($Username) with automapping $($AutoMap)" } } 'SendAs' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-RecipientPermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-RecipientPermission' + $CmdletParams = @{ + Identity = $UserId Trustee = $TargetUser accessRights = @('SendAs') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) with Send As permissions") - } - else { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Add-RecipientPermission' -cmdParams @{ - Identity = $userid + $ExpectedResult = "Removed $($TargetUser) SendAs permissions from $($Username)" + } else { + $CmdletName = 'Add-RecipientPermission' + $CmdletParams = @{ + Identity = $UserId Trustee = $TargetUser accessRights = @('SendAs') Confirm = $false } - $null = $results.Add("Granted $($TargetUser) access to $($username) with Send As permissions") + $ExpectedResult = "Granted $($TargetUser) SendAs permissions to $($Username)" } } 'SendOnBehalf' { + $CmdletName = 'Set-Mailbox' if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Set-Mailbox' -cmdParams @{ - Identity = $userid + $CmdletParams = @{ + Identity = $UserId GrantSendonBehalfTo = @{ '@odata.type' = '#Exchange.GenericHashTable' remove = $TargetUser } Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Send on Behalf Permissions") - } - else { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Set-Mailbox' -cmdParams @{ - Identity = $userid + $ExpectedResult = "Removed $($TargetUser) SendOnBehalf permissions from $($Username)" + } else { + $CmdletParams = @{ + Identity = $UserId GrantSendonBehalfTo = @{ '@odata.type' = '#Exchange.GenericHashTable' add = $TargetUser } Confirm = $false } - $null = $results.Add("Granted $($TargetUser) access to $($username) with Send On Behalf Permissions") + $ExpectedResult = "Granted $($TargetUser) SendOnBehalf permissions to $($Username)" } } 'ReadPermission' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-MailboxPermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('ReadPermission') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Read Permissions") + $ExpectedResult = "Removed $($TargetUser) ReadPermission from $($Username)" } } 'ExternalAccount' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-MailboxPermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('ExternalAccount') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Read Permissions") + $ExpectedResult = "Removed $($TargetUser) ExternalAccount permissions from $($Username)" } } 'DeleteItem' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-MailboxPermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('DeleteItem') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Read Permissions") + $ExpectedResult = "Removed $($TargetUser) DeleteItem permissions from $($Username)" } } 'ChangePermission' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-MailboxPermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('ChangePermission') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Read Permissions") + $ExpectedResult = "Removed $($TargetUser) ChangePermission from $($Username)" } } 'ChangeOwner' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-MailboxPermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('ChangeOwner') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Read Permissions") + $ExpectedResult = "Removed $($TargetUser) ChangeOwner permissions from $($Username)" } } } - Write-LogMessage -headers $Request.Headers -API $APINAME-message "Executed $($PermissionLevel) permission modification for $($TargetUser) on $($username)" -Sev 'Info' -tenant $TenantFilter - } - catch { - Write-LogMessage -headers $Request.Headers -API $APINAME-message "Could not execute $($PermissionLevel) permission modification for $($TargetUser) on $($username)" -Sev 'Error' -tenant $TenantFilter - $null = $results.Add("Could not execute $($PermissionLevel) permission modification for $($TargetUser) on $($username). Error: $($_.Exception.Message)") + + if ($CmdletName) { + # Generate unique GUID for this operation + $OperationGuid = [Guid]::NewGuid().ToString() + + $CmdletObj = @{ + CmdletInput = @{ + CmdletName = $CmdletName + Parameters = $CmdletParams + } + OperationGuid = $OperationGuid # Add GUID to cmdlet object + } + + $CmdletMetadata = [PSCustomObject]@{ + ExpectedResult = $ExpectedResult + Mailbox = $Username + TargetUser = $TargetUser + Permission = $PermissionLevel + Action = $Modification + OperationGuid = $OperationGuid + } + + $null = $CmdletArray.Add($CmdletObj) + $null = $CmdletMetadataArray.Add($CmdletMetadata) + + # Map GUID to metadata for precise result mapping + $GuidToMetadataMap[$OperationGuid] = $CmdletMetadata + } } } } } - $body = [pscustomobject]@{'Results' = @($results) } - - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + if ($CmdletArray.Count -eq 0) { + Write-LogMessage -headers $Request.Headers -API $APINAME -message 'No valid cmdlets to process' -Sev 'Warning' -tenant $TenantFilter + $body = [pscustomobject]@{'Results' = @("No valid permission changes to process") } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + return + } + + # Execute requests - use enhanced bulk processing with GUID mapping + if ($CmdletArray.Count -gt 1) { + # Use bulk processing with GUID tracking + try { + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Executing bulk request with $($CmdletArray.Count) cmdlets" -Sev 'Info' -tenant $TenantFilter + $BulkResults = New-ExoBulkRequest -tenantid $TenantFilter -cmdletArray @($CmdletArray) -ReturnWithCommand $true + + # Process bulk results using GUID mapping + if ($BulkResults -is [hashtable] -and $BulkResults.Keys.Count -gt 0) { + foreach ($cmdletName in $BulkResults.Keys) { + foreach ($result in $BulkResults[$cmdletName]) { + $operationGuid = $result.OperationGuid + + if ($operationGuid -and $GuidToMetadataMap.ContainsKey($operationGuid)) { + $metadata = $GuidToMetadataMap[$operationGuid] + + if ($result.error) { + $ErrorMessage = try { (Get-CippException -Exception $result.error).NormalizedError } catch { $result.error } + $null = $Results.Add("Error processing $($metadata.Permission) for $($metadata.TargetUser) on $($metadata.Mailbox): $ErrorMessage") + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Error for operation $operationGuid`: $ErrorMessage" -Sev 'Error' -tenant $TenantFilter + } else { + $null = $Results.Add($metadata.ExpectedResult) + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Success for operation $operationGuid`: $($metadata.ExpectedResult)" -Sev 'Info' -tenant $TenantFilter + } + } else { + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Could not map result to operation. GUID: $operationGuid, Available GUIDs: $($GuidToMetadataMap.Keys -join ', ')" -Sev 'Warning' -tenant $TenantFilter + + # Fallback for unmapped results + if ($result.error) { + $ErrorMessage = try { (Get-CippException -Exception $result.error).NormalizedError } catch { $result.error } + $null = $Results.Add("Error in $cmdletName`: $ErrorMessage") + } else { + $null = $Results.Add("Completed $cmdletName operation") + } + } + } + } + } else { + # If no results returned but no error thrown, assume all succeeded + foreach ($CmdletMetadata in $CmdletMetadataArray) { + if ($CmdletMetadata.ExpectedResult) { + $null = $Results.Add($CmdletMetadata.ExpectedResult) + } + } + } + + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Bulk request completed successfully" -Sev 'Info' -tenant $TenantFilter + } + catch { + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Bulk request failed, using fallback: $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter + + # Fallback to individual processing + for ($i = 0; $i -lt $CmdletArray.Count; $i++) { + $CmdletObj = $CmdletArray[$i] + $CmdletMetadata = $CmdletMetadataArray[$i] + try { + $null = New-ExoRequest -Anchor $CmdletMetadata.Mailbox -tenantid $TenantFilter -cmdlet $CmdletObj.CmdletInput.CmdletName -cmdParams $CmdletObj.CmdletInput.Parameters + $null = $Results.Add($CmdletMetadata.ExpectedResult) + } + catch { + $null = $Results.Add("Error processing $($CmdletMetadata.Permission) for $($CmdletMetadata.TargetUser) on $($CmdletMetadata.Mailbox): $($_.Exception.Message)") + } + } + } + } + else { + # Use individual processing for single operation + $CmdletObj = $CmdletArray[0] + $CmdletMetadata = $CmdletMetadataArray[0] + try { + $null = New-ExoRequest -Anchor $CmdletMetadata.Mailbox -tenantid $TenantFilter -cmdlet $CmdletObj.CmdletInput.CmdletName -cmdParams $CmdletObj.CmdletInput.Parameters + $null = $Results.Add($CmdletMetadata.ExpectedResult) + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Executed $($CmdletMetadata.Permission) permission modification" -Sev 'Info' -tenant $TenantFilter + } + catch { + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Permission modification failed: $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter + $null = $Results.Add("Error processing $($CmdletMetadata.Permission) for $($CmdletMetadata.TargetUser) on $($CmdletMetadata.Mailbox): $($_.Exception.Message)") + } + } + + $body = [pscustomobject]@{'Results' = @($Results) } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) } diff --git a/Modules/CIPPCore/Public/GraphHelper/New-ExoBulkRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-ExoBulkRequest.ps1 index 38275011282b..553331e6a328 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-ExoBulkRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-ExoBulkRequest.ps1 @@ -43,6 +43,7 @@ function New-ExoBulkRequest { # Initialize the ID to Cmdlet Name mapping $IdToCmdletName = @{} + $IdToOperationGuid = @{} # Track operation GUIDs when provided # Split the cmdletArray into batches of 10 $batches = [System.Collections.Generic.List[object]]::new() @@ -69,19 +70,32 @@ function New-ExoBulkRequest { $Headers['Accept'] = 'application/json; odata.metadata=minimal' $Headers['Accept-Encoding'] = 'gzip' - # Generate a unique ID for each request - $RequestId = [Guid]::NewGuid().ToString() + # Use provided OperationGuid if available, otherwise generate one + $RequestId = if ($cmd.OperationGuid) { + $cmd.OperationGuid + } else { + [Guid]::NewGuid().ToString() + } + + # Create clean cmdlet object for API (without OperationGuid) + $CleanCmd = @{ + CmdletInput = $cmd.CmdletInput + } + $BatchRequest = @{ url = $URL method = 'POST' - body = $cmd + body = $CleanCmd headers = $Headers.Clone() id = $RequestId } $BatchBodyObj['requests'] = $BatchBodyObj['requests'] + $BatchRequest - # Map the Request ID to the Cmdlet Name + # Map the Request ID to the Cmdlet Name and Operation GUID (if provided) $IdToCmdletName[$RequestId] = $cmd.CmdletInput.CmdletName + if ($cmd.OperationGuid) { + $IdToOperationGuid[$RequestId] = $cmd.OperationGuid + } } $BatchBodyJson = ConvertTo-Json -InputObject $BatchBodyObj -Depth 10 $BatchBodyJson = Get-CIPPTextReplacement -TenantFilter $tenantid -Text $BatchBodyJson @@ -104,6 +118,7 @@ function New-ExoBulkRequest { foreach ($item in $ReturnedData) { $itemId = $item.id $CmdletName = $IdToCmdletName[$itemId] + $OperationGuid = $IdToOperationGuid[$itemId] # Will be $null if not provided $body = $item.body.PSObject.Copy() if ($body.'@adminapi.warnings') { @@ -115,20 +130,50 @@ function New-ExoBulkRequest { } else { $msg = [pscustomobject]@{ error = $body.error.message; target = $body.error.details.target } } + + # Add OperationGuid to error if it was provided + if ($OperationGuid) { + $msg | Add-Member -MemberType NoteProperty -Name 'OperationGuid' -Value $OperationGuid -Force + } + $body | Add-Member -MemberType NoteProperty -Name 'value' -Value $msg -Force + } else { + # Handle successful operations - add OperationGuid if provided + if ($body.value) { + # Add GUID to existing results if provided + if ($OperationGuid) { + if ($body.value -is [array]) { + foreach ($val in $body.value) { + $val | Add-Member -MemberType NoteProperty -Name 'OperationGuid' -Value $OperationGuid -Force + } + } else { + $body.value | Add-Member -MemberType NoteProperty -Name 'OperationGuid' -Value $OperationGuid -Force + } + } + } else { + # Create success indicators when GUID was provided (caller wants tracking) + if ($OperationGuid) { + $body | Add-Member -MemberType NoteProperty -Name 'value' -Value ([pscustomobject]@{ + Success = $true + OperationGuid = $OperationGuid + }) -Force + } + } } + $resultValues = $body.value foreach ($resultValue in $resultValues) { if (-not $FinalData.ContainsKey($CmdletName)) { $FinalData[$CmdletName] = [System.Collections.Generic.List[object]]::new() - $FinalData.$CmdletName.Add($resultValue) + $FinalData[$CmdletName].Add($resultValue) } else { - $FinalData.$CmdletName.Add($resultValue) + $FinalData[$CmdletName].Add($resultValue) } } } } else { $FinalData = foreach ($item in $ReturnedData) { + $OperationGuid = $IdToOperationGuid[$item.id] # Will be $null if not provided $body = $item.body.PSObject.Copy() if ($body.'@adminapi.warnings') { @@ -140,7 +185,35 @@ function New-ExoBulkRequest { } else { $msg = [pscustomobject]@{ error = $body.error.message; target = $body.error.details.target } } + + # Add OperationGuid to error if it was provided + if ($OperationGuid) { + $msg | Add-Member -MemberType NoteProperty -Name 'OperationGuid' -Value $OperationGuid -Force + } + $body | Add-Member -MemberType NoteProperty -Name 'value' -Value $msg -Force + } else { + # Handle successful operations + if ($body.value) { + # Add GUID to existing results if provided + if ($OperationGuid) { + if ($body.value -is [array]) { + foreach ($val in $body.value) { + $val | Add-Member -MemberType NoteProperty -Name 'OperationGuid' -Value $OperationGuid -Force + } + } else { + $body.value | Add-Member -MemberType NoteProperty -Name 'OperationGuid' -Value $OperationGuid -Force + } + } + } else { + # Create success indicators when GUID was provided (caller wants tracking) + if ($OperationGuid) { + $body | Add-Member -MemberType NoteProperty -Name 'value' -Value ([pscustomobject]@{ + Success = $true + OperationGuid = $OperationGuid + }) -Force + } + } } $body.value } From a2d86cc0160ef0ab413847fa8ef3be921456446f Mon Sep 17 00:00:00 2001 From: Zac Richards <107489668+Zacgoose@users.noreply.github.com> Date: Wed, 30 Jul 2025 23:27:19 +0800 Subject: [PATCH 54/85] Add Invoke-CIPPStandardSPFileRequests function Introduces a new PowerShell function to enable or disable SharePoint and OneDrive File Requests, including optional configuration for link expiration. The function supports remediation, alerting, and reporting, and includes license checks and input validation. --- .../Invoke-CIPPStandardSPFileRequests.ps1 | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 new file mode 100644 index 000000000000..64ff49d50fef --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 @@ -0,0 +1,129 @@ +function Invoke-CIPPStandardSPFileRequests { + <# + .FUNCTIONALITY + Internal + .COMPONENT + (APIName) SPFileRequests + .SYNOPSIS + (Label) Set SharePoint and OneDrive File Requests + .DESCRIPTION + (Helptext) Enables or disables File Requests for SharePoint and OneDrive, allowing users to create secure upload-only links. Optionally sets the maximum number of days for the link to remain active. + (DocsDescription) File Requests allow users to create secure upload-only share links where uploads are hidden from other people using the link. This creates a secure and private way for people to upload files to a folder. This standard enables or disables this feature and optionally configures link expiration settings. + .NOTES + CAT + SharePoint Standards + TAG + ADDEDCOMPONENT + {"type":"switch","name":"standards.SPFileRequests.state","label":"Enable File Requests"} + {"type":"number","name":"standards.SPFileRequests.expirationDays","label":"Link Expiration Days (Optional - 1-730)","required":false} + IMPACT + Medium Impact + ADDEDDATE + 2025-07-30 + POWERSHELLEQUIVALENT + Set-SPOTenant -CoreRequestFilesLinkEnabled \$true -OneDriveRequestFilesLinkEnabled \$true -CoreRequestFilesLinkExpirationInDays 30 -OneDriveRequestFilesLinkExpirationInDays 30 + RECOMMENDEDBY + "CIPP" + UPDATECOMMENTBLOCK + Run the Tools\Update-StandardsComments.ps1 script to update this comment block + .LINK + https://docs.cipp.app/user-documentation/tenant/standards/list-standards + #> + + param($Tenant, $Settings) + $TestResult = Test-CIPPStandardLicense -StandardName 'SPFileRequests' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + + if ($TestResult -eq $false) { + Write-Host "We're exiting as the correct license is not present for this standard." + return $true + } + + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object _ObjectIdentity_, TenantFilter, CoreRequestFilesLinkEnabled, OneDriveRequestFilesLinkEnabled, CoreRequestFilesLinkExpirationInDays, OneDriveRequestFilesLinkExpirationInDays + + # Input validation + if (($Settings.state -eq $null) -and ($Settings.remediate -eq $true -or $Settings.alert -eq $true)) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'SPFileRequests: Invalid state parameter set' -sev Error + return + } + + $WantedState = $Settings.state + $ExpirationDays = $Settings.expirationDays + $HumanReadableState = if ($WantedState -eq $true) { 'enabled' } else { 'disabled' } + + # Check if current state matches desired state + $CoreStateIsCorrect = if ($CurrentState.CoreRequestFilesLinkEnabled -eq $WantedState) { $true } else { $false } + $OneDriveStateIsCorrect = if ($CurrentState.OneDriveRequestFilesLinkEnabled -eq $WantedState) { $true } else { $false } + $StateIsCorrect = $CoreStateIsCorrect -and $OneDriveStateIsCorrect + + # Check expiration settings if specified + $ExpirationIsCorrect = $true + if ($ExpirationDays -ne $null -and $WantedState -eq $true) { + $CoreExpirationIsCorrect = ($CurrentState.CoreRequestFilesLinkExpirationInDays -eq $ExpirationDays) + $OneDriveExpirationIsCorrect = ($CurrentState.OneDriveRequestFilesLinkExpirationInDays -eq $ExpirationDays) + $ExpirationIsCorrect = $CoreExpirationIsCorrect -and $OneDriveExpirationIsCorrect + } + + $AllSettingsCorrect = $StateIsCorrect -and $ExpirationIsCorrect + + if ($Settings.remediate -eq $true) { + Write-Host 'Time to remediate' + + if ($AllSettingsCorrect -eq $false) { + try { + $Properties = @{ + CoreRequestFilesLinkEnabled = $WantedState + OneDriveRequestFilesLinkEnabled = $WantedState + } + + # Add expiration settings if specified and feature is being enabled + if ($ExpirationDays -ne $null -and $WantedState -eq $true) { + $Properties['CoreRequestFilesLinkExpirationInDays'] = $ExpirationDays + $Properties['OneDriveRequestFilesLinkExpirationInDays'] = $ExpirationDays + } + + $CurrentState | Set-CIPPSPOTenant -Properties $Properties + + $ExpirationMessage = if ($ExpirationDays -ne $null -and $WantedState -eq $true) { " with $ExpirationDays day expiration" } else { "" } + Write-LogMessage -API 'Standards' -tenant $tenant -message "Successfully set File Requests to $HumanReadableState$ExpirationMessage" -sev Info + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set File Requests to $HumanReadableState. Error: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage + } + } else { + $ExpirationMessage = if ($ExpirationDays -ne $null -and $WantedState -eq $true) { " with $ExpirationDays day expiration" } else { "" } + Write-LogMessage -API 'Standards' -tenant $tenant -message "File Requests are already set to the wanted state of $HumanReadableState$ExpirationMessage" -sev Info + } + } + + if ($Settings.alert -eq $true) { + if ($AllSettingsCorrect -eq $true) { + $ExpirationMessage = if ($ExpirationDays -ne $null -and $WantedState -eq $true) { " with $ExpirationDays day expiration" } else { "" } + Write-LogMessage -API 'Standards' -tenant $tenant -message "File Requests are already set to the wanted state of $HumanReadableState$ExpirationMessage" -sev Info + } else { + $AlertMessage = "File Requests are not set to the wanted state of $HumanReadableState" + if ($ExpirationDays -ne $null -and $WantedState -eq $true) { + $AlertMessage += " with $ExpirationDays day expiration" + } + Write-StandardsAlert -message $AlertMessage -object $CurrentState -tenant $tenant -standardName 'SPFileRequests' -standardId $Settings.standardId + Write-LogMessage -API 'Standards' -tenant $tenant -message $AlertMessage -sev Info + } + } + + if ($Settings.report -eq $true) { + Add-CIPPBPAField -FieldName 'SPFileRequestsEnabled' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $Tenant + Add-CIPPBPAField -FieldName 'SPCoreFileRequestsEnabled' -FieldValue $CurrentState.CoreRequestFilesLinkEnabled -StoreAs bool -Tenant $Tenant + Add-CIPPBPAField -FieldName 'SPOneDriveFileRequestsEnabled' -FieldValue $CurrentState.OneDriveRequestFilesLinkEnabled -StoreAs bool -Tenant $Tenant + + if ($ExpirationDays -ne $null) { + Add-CIPPBPAField -FieldName 'SPCoreFileRequestsExpirationDays' -FieldValue $CurrentState.CoreRequestFilesLinkExpirationInDays -StoreAs string -Tenant $Tenant + Add-CIPPBPAField -FieldName 'SPOneDriveFileRequestsExpirationDays' -FieldValue $CurrentState.OneDriveRequestFilesLinkExpirationInDays -StoreAs string -Tenant $Tenant + } + + if ($AllSettingsCorrect) { + $FieldValue = $true + } else { + $FieldValue = $CurrentState + } + Set-CIPPStandardsCompareField -FieldName 'standards.SPFileRequests' -FieldValue $FieldValue -Tenant $Tenant + } +} From f84f77268384f5f3bc006dfff066a82567624de6 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 30 Jul 2025 12:36:01 -0400 Subject: [PATCH 55/85] update permissions for app manifests when updates are detected --- .../Push-ExecAppApprovalTemplate.ps1 | 54 +++++++++++++------ .../Invoke-ExecAppApprovalTemplate.ps1 | 1 - .../Invoke-CIPPStandardAppDeploy.ps1 | 21 ++++++++ 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecAppApprovalTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecAppApprovalTemplate.ps1 index acde7892b480..e6eec613a662 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecAppApprovalTemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecAppApprovalTemplate.ps1 @@ -10,7 +10,7 @@ function Push-ExecAppApprovalTemplate { $TemplateId = $Item.templateId if (!$TemplateId) { Write-LogMessage -message 'No template specified' -tenant $Item.Tenant -API 'Add Multitenant App' -sev Error - return + return $false } # Get the template data to determine if it's a Gallery Template or Enterprise App @@ -19,7 +19,7 @@ function Push-ExecAppApprovalTemplate { if (!$Template) { Write-LogMessage -message "Template $TemplateId not found" -tenant $Item.Tenant -API 'Add Multitenant App' -sev Error - return + return $false } $TemplateData = $Template.JSON | ConvertFrom-Json @@ -37,14 +37,14 @@ function Push-ExecAppApprovalTemplate { $GalleryTemplateId = $TemplateData.GalleryTemplateId if (!$GalleryTemplateId) { Write-LogMessage -message 'Gallery Template ID not found in template data' -tenant $Item.Tenant -API 'Add Multitenant App' -sev Error - return + return $false } # Check if the app already exists in the tenant $ServicePrincipalList = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/servicePrincipals?`$select=AppId,id,displayName&`$top=999" -tenantid $Item.Tenant if ($TemplateData.GalleryTemplateId -in $ServicePrincipalList.applicationTemplateId) { Write-LogMessage -message "Gallery Template app $($TemplateData.AppName) already exists in tenant $($Item.Tenant)" -tenant $Item.Tenant -API 'Add Gallery App' -sev Info - return + return $true } # Instantiate the gallery template @@ -74,14 +74,36 @@ function Push-ExecAppApprovalTemplate { $ApplicationManifest = $TemplateData.ApplicationManifest if (!$ApplicationManifest) { Write-LogMessage -message 'Application Manifest not found in template data' -tenant $Item.Tenant -API 'Add Multitenant App' -sev Error - return + return $false } # Check for existing application by display name - $ExistingApp = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/applications?`$filter=displayName eq '$($TemplateData.AppName)'&`$top=1" -tenantid $Item.Tenant -NoAuthCheck $true - if ($ExistingApp -and $ExistingApp.value) { - Write-LogMessage -message "Application Manifest $($TemplateData.AppName) already exists in tenant $($Item.Tenant)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Info - return + $ServicePrincipalList = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/servicePrincipals?`$select=AppId,id,displayName&`$top=999" -tenantid $Item.Tenant + $ExistingApp = $ServicePrincipalList | Where-Object { $_.displayName -eq $TemplateData.AppName } + if ($ExistingApp) { + Write-LogMessage -message "Application with name '$($TemplateData.AppName)' already exists in tenant $($Item.Tenant)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Info + + # get existing application + $App = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/applications(appId='$($ExistingApp.appId)')" -tenantid $Item.Tenant + + # compare permissions + $ExistingPermissions = $App.requiredResourceAccess | ConvertTo-Json -Depth 10 + $NewPermissions = $ApplicationManifest.requiredResourceAccess | ConvertTo-Json -Depth 10 + if ($ExistingPermissions -ne $NewPermissions) { + Write-LogMessage -message "Updating permissions for existing application '$($TemplateData.AppName)' in tenant $($Item.Tenant)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Info + + # Update permissions for existing application + $UpdateBody = @{ + requiredResourceAccess = $ApplicationManifest.requiredResourceAccess + } | ConvertTo-Json -Depth 10 + $null = New-GraphPostRequest -type PATCH -uri "https://graph.microsoft.com/beta/applications(appId='$($ExistingApp.appId)')" -tenantid $Item.Tenant -body $UpdateBody + + # consent new permissions + Add-CIPPDelegatedPermission -RequiredResourceAccess $ApplicationManifest.requiredResourceAccess -ApplicationId $ExistingApp.appId -Tenantfilter $Item.Tenant + Add-CIPPApplicationPermission -RequiredResourceAccess $ApplicationManifest.requiredResourceAccess -ApplicationId $ExistingApp.appId -Tenantfilter $Item.Tenant + } + + return $true } $PropertiesToRemove = @('appId', 'id', 'createdDateTime', 'publisherDomain', 'servicePrincipalLockConfiguration', 'identifierUris', 'applicationIdUris') @@ -103,22 +125,20 @@ function Push-ExecAppApprovalTemplate { appId = $CreatedApp.appId } | ConvertTo-Json - $ServicePrincipal = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/servicePrincipals' -type POST -tenantid $Item.tenant -body $ServicePrincipalBody + $null = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/servicePrincipals' -type POST -tenantid $Item.tenant -body $ServicePrincipalBody Write-LogMessage -message "Successfully deployed Application Manifest $($TemplateData.AppName) to tenant $($Item.Tenant). Application ID: $($CreatedApp.appId)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Info - $DelegateResourceAccess = $ApplicationManifest.requiredResourceAccess - $ApplicationResourceAccess = $ApplicationManifest.requiredResourceAccess - if ($ApplicationManifest.requiredResourceAccess) { - Add-CIPPDelegatedPermission -RequiredResourceAccess $ApplicationManifest.requiredResourceAccess -ApplicationId $App -Tenantfilter $Tenant - Add-CIPPApplicationPermission -RequiredResourceAccess $ApplicationManifest.requiredResourceAccess -ApplicationId $App -Tenantfilter $Tenant - } + if ($CreatedApp.requiredResourceAccess) { + Add-CIPPDelegatedPermission -RequiredResourceAccess $CreatedApp.requiredResourceAccess -ApplicationId $CreatedApp.appId -Tenantfilter $Item.Tenant + Add-CIPPApplicationPermission -RequiredResourceAccess $CreatedApp.requiredResourceAccess -ApplicationId $CreatedApp.appId -Tenantfilter $Item.Tenant + } } else { Write-LogMessage -message "Application Manifest deployment failed - no application ID returned for $($TemplateData.AppName) in tenant $($Item.Tenant)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Error } } catch { Write-LogMessage -message "Error creating application from manifest in tenant $($Item.Tenant) - $($_.Exception.Message)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Error - throw + throw $_.Exception.Message } } else { diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Application Approval/Invoke-ExecAppApprovalTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Application Approval/Invoke-ExecAppApprovalTemplate.ps1 index 9368600072bb..9adb1dc1c6b1 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Application Approval/Invoke-ExecAppApprovalTemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Application Approval/Invoke-ExecAppApprovalTemplate.ps1 @@ -102,7 +102,6 @@ function Invoke-ExecAppApprovalTemplate { if ($Request.Query.TemplateId) { $templateId = $Request.Query.TemplateId $filter = "PartitionKey eq 'AppApprovalTemplate' and RowKey eq '$templateId'" - Write-LogMessage -headers $Headers -API $APIName -message "Retrieved specific template: $templateId" -Sev 'Info' } $Templates = Get-CIPPAzDataTableEntity @Table -Filter $filter diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAppDeploy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAppDeploy.ps1 index 1848e47918a6..5af50f4b7d83 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAppDeploy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAppDeploy.ps1 @@ -186,6 +186,27 @@ function Invoke-CIPPStandardAppDeploy { $ExistingApp = $AppExists | Where-Object { $_.displayName -eq $TemplateData.AppName } if ($ExistingApp) { Write-LogMessage -API 'Standards' -tenant $tenant -message "Application with name '$($TemplateData.AppName)' already exists in tenant $Tenant" -sev Info + + # get existing application + $App = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/applications(appId='$($ExistingApp.appId)')" -tenantid $Tenant + + # compare permissions + $ExistingPermissions = $App.requiredResourceAccess | ConvertTo-Json -Depth 10 + $NewPermissions = $ApplicationManifest.requiredResourceAccess | ConvertTo-Json -Depth 10 + if ($ExistingPermissions -ne $NewPermissions) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Updating permissions for existing application '$($TemplateData.AppName)' in tenant $Tenant" -sev Info + + # Update permissions for existing application + $UpdateBody = @{ + requiredResourceAccess = $ApplicationManifest.requiredResourceAccess + } | ConvertTo-Json -Depth 10 + $null = New-GraphPostRequest -type PATCH -uri "https://graph.microsoft.com/beta/applications(appId='$($ExistingApp.appId)')" -tenantid $Tenant -body $UpdateBody + + # consent new permissions + Add-CIPPDelegatedPermission -RequiredResourceAccess $ApplicationManifest.requiredResourceAccess -ApplicationId $ExistingApp.appId -Tenantfilter $Tenant + Add-CIPPApplicationPermission -RequiredResourceAccess $ApplicationManifest.requiredResourceAccess -ApplicationId $ExistingApp.appId -Tenantfilter $Tenant + } + continue } From 59eb5f12d8e3cbb1f08a9b35c2481b4d1ff2cc99 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 30 Jul 2025 16:47:54 -0400 Subject: [PATCH 56/85] fix restore logging --- .../CIPP/Settings/Invoke-ExecRestoreBackup.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 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 9a12d734adce..25fbf73dc0ed 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 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-ExecRestoreBackup { +function Invoke-ExecRestoreBackup { <# .FUNCTIONALITY Entrypoint @@ -27,7 +27,7 @@ Function Invoke-ExecRestoreBackup { $Table.Entity = $ht2 Add-CIPPAzDataTableEntity @Table -Force } - Write-LogMessage -headers $Request.Headers -API $APINAME -message 'Created backup' -Sev 'Debug' + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Restored backup $($Request.Body.BackupName)" -Sev 'Info' $body = [pscustomobject]@{ 'Results' = 'Successfully restored backup.' } @@ -44,7 +44,7 @@ Function Invoke-ExecRestoreBackup { $Table.Entity = $ht2 Add-AzDataTableEntity @Table -Force } - Write-LogMessage -headers $Request.Headers -API $APINAME -message 'Created backup' -Sev 'Debug' + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Restored backup $($Request.Body.BackupName)" -Sev 'Info' $body = [pscustomobject]@{ 'Results' = 'Successfully restored backup.' From fff114c5d065da708d755048df8dedb493d0bcca Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Wed, 30 Jul 2025 22:49:44 +0200 Subject: [PATCH 57/85] prep for drift emailing --- .../Standards/Push-CIPPDriftManagement.ps1 | 75 ++++++++ .../Endpoint/MEM/Invoke-RemovePolicy.ps1 | 2 +- .../Functions/Get-CIPPTenantAlignment.ps1 | 15 ++ Modules/CIPPCore/Public/Get-CIPPDrift.ps1 | 177 ++++++++---------- .../CIPPCore/Public/New-CIPPAlertTemplate.ps1 | 8 + Modules/CIPPCore/Public/Send-CIPPAlert.ps1 | 22 ++- ...-CIPPStandardConditionalAccessTemplate.ps1 | 2 +- 7 files changed, 188 insertions(+), 113 deletions(-) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 new file mode 100644 index 000000000000..a24e4878e105 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 @@ -0,0 +1,75 @@ +function Push-CippDriftManagement { + <# + .FUNCTIONALITY + Entrypoint + #> + param ( + $Item + ) + + Write-Information "Received queue item for $($Item.Tenant)" + + try { + $Drift = Get-CIPPDrift -TenantFilter $item.tenant + if ($Drift.newDeviationsCount -gt 0) { + $email = (Get-CIPPTenantAlignment -TenantFilter $Item.Tenant | Where-Object -Property standardType -EQ 'drift').standardSettings.email + $webhook = (Get-CIPPTenantAlignment -TenantFilter $Item.Tenant | Where-Object -Property standardType -EQ 'drift').standardSettings.webhook + $CippConfigTable = Get-CippTable -tablename Config + $CippConfig = Get-CIPPAzDataTableEntity @CippConfigTable -Filter "PartitionKey eq 'InstanceProperties' and RowKey eq 'CIPPURL'" + $CIPPURL = 'https://{0}' -f $CippConfig.Value + $Data = $Drift.currentDeviations | ForEach-Object { + $currentValue = if ($_.receivedValue -and $_.receivedValue.Length -gt 200) { + $_.receivedValue.Substring(0, 200) + '...' + } else { + $_.receivedValue + } + [PSCustomObject]@{ + Standard = $_.standardDisplayName ? $_.standardDisplayName : $_.standardName + 'Expected Value' = $_.expectedValue + 'Current Value' = $currentValue + Status = $_.status + } + } + $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data -CIPPURL $CIPPURL -Tenant $item.tenant -InputObject 'driftStandard' + $CIPPAlert = @{ + Type = 'email' + Title = $GenerateEmail.title + HTMLContent = $GenerateEmail.htmlcontent + TenantFilter = $item.Tenant + } + Write-Host 'Going to send the mail' + Send-CIPPAlert @CIPPAlert -altEmail $email + $WebhookData = @{ + Title = $GenerateEmail.title + ActionUrl = $GenerateEmail.ButtonUrl + ActionText = $GenerateEmail.ButtonText + AlertData = $Data + Tenant = $Item.Tenant + } | ConvertTo-Json -Depth 15 -Compress + $CippAlert = @{ + Type = 'webhook' + Title = $GenerateEmail.title + JSONContent = $WebhookData + TenantFilter = $item.tenant + } + Write-Host 'Sending Webhook Content' + Send-CIPPAlert @CippAlert -altWebhook $webhook + #Always do PSA. + $CIPPAlert = @{ + Type = 'psa' + Title = $GenerateEmail.title + HTMLContent = $GenerateEmail.htmlcontent + TenantFilter = $TenantFilter + } + Send-CIPPAlert @CIPPAlert + } else { + Write-LogMessage -API 'DriftStandards' -tenant $Item.Tenant -message "No new drift deviations found for tenant $($Item.Tenant)" -sev Info + } + Write-Information "Drift management completed for tenant $($Item.Tenant)" + } catch { + Write-LogMessage -API 'DriftStandards' -tenant $Item.Tenant -message "Error running Drift Check for tenant $($Item.Tenant) - $($_.Exception.Message)" -sev Error -LogData (Get-CippException -Exception $_) + Write-Warning "Error running standard $($Item.Standard) for tenant $($Item.Tenant) - $($_.Exception.Message)" + Write-Information $_.InvocationInfo.PositionMessage + throw $_.Exception.Message + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-RemovePolicy.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-RemovePolicy.ps1 index af0206cc0619..a0941f3854e6 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-RemovePolicy.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-RemovePolicy.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-RemovePolicy { +function Invoke-RemovePolicy { <# .FUNCTIONALITY Entrypoint diff --git a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 index 811983f6e635..ac71b7633e27 100644 --- a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 +++ b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 @@ -150,6 +150,21 @@ function Get-CIPPTenantAlignment { } } } + } + # Handle Conditional Access templates specially + elseif ($StandardKey -eq 'ConditionalAccessTemplate' -and $StandardConfig -is [array]) { + foreach ($CATemplate in $StandardConfig) { + if ($CATemplate.TemplateList.value) { + $CAStandardId = "standards.ConditionalAccessTemplate.$($CATemplate.TemplateList.value)" + $CAActions = if ($CATemplate.action) { $CATemplate.action } else { @() } + $CAReportingEnabled = ($CAActions | Where-Object { $_.value -and ($_.value.ToLower() -eq 'report' -or $_.value.ToLower() -eq 'remediate') }).Count -gt 0 + + [PSCustomObject]@{ + StandardId = $CAStandardId + ReportingEnabled = $CAReportingEnabled + } + } + } } else { [PSCustomObject]@{ StandardId = $StandardId diff --git a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 index 8030867d3acc..127607edb86e 100644 --- a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 @@ -76,118 +76,89 @@ function Get-CIPPDrift { } # Perform full policy collection - if ($AllTenants) { - # Use cached data when processing all tenants - $CacheTable = Get-CippTable -tablename 'cacheDrift' - $CacheFilter = "PartitionKey eq 'drift' and RowKey eq '$TenantFilter'" - $CachedData = $null - try { - $CachedData = Get-CIPPAzDataTableEntity @CacheTable -Filter $CacheFilter | Select-Object -First 1 - } catch { - # Cache doesn't exist or error reading + # Always get live data when not in AllTenants mode + $IntuneRequests = @( + @{ + id = 'deviceAppManagement' + url = 'deviceAppManagement/managedAppPolicies' + method = 'GET' } - - if ($CachedData -and $CachedData.CAJson -and $CachedData.IntuneJson) { - # Use cached data - try { - $TenantIntunePolicies = $CachedData.IntuneJson | ConvertFrom-Json - $TenantCAPolicies = $CachedData.CAJson | ConvertFrom-Json - } catch { - Write-Warning "Failed to parse cached data for tenant $TenantFilter" - $TenantIntunePolicies = @() - $TenantCAPolicies = @() - } - } else { - # No cache available, skip policy collection for AllTenants mode - $TenantIntunePolicies = @() - $TenantCAPolicies = @() + @{ + id = 'deviceCompliancePolicies' + url = 'deviceManagement/deviceCompliancePolicies' + method = 'GET' } - } else { - # Always get live data when not in AllTenants mode - $IntuneRequests = @( - @{ - id = 'deviceAppManagement' - url = 'deviceAppManagement/managedAppPolicies' - method = 'GET' - } - @{ - id = 'deviceCompliancePolicies' - url = 'deviceManagement/deviceCompliancePolicies' - method = 'GET' - } - @{ - id = 'groupPolicyConfigurations' - url = 'deviceManagement/groupPolicyConfigurations' - method = 'GET' - } - @{ - id = 'deviceConfigurations' - url = 'deviceManagement/deviceConfigurations' - method = 'GET' - } - @{ - id = 'configurationPolicies' - url = 'deviceManagement/configurationPolicies' - method = 'GET' - } - @{ - id = 'windowsDriverUpdateProfiles' - url = 'deviceManagement/windowsDriverUpdateProfiles' - method = 'GET' - } - @{ - id = 'windowsFeatureUpdateProfiles' - url = 'deviceManagement/windowsFeatureUpdateProfiles' - method = 'GET' - } - @{ - id = 'windowsQualityUpdatePolicies' - url = 'deviceManagement/windowsQualityUpdatePolicies' - method = 'GET' - } - @{ - id = 'windowsQualityUpdateProfiles' - url = 'deviceManagement/windowsQualityUpdateProfiles' - method = 'GET' - } - ) + @{ + id = 'groupPolicyConfigurations' + url = 'deviceManagement/groupPolicyConfigurations' + method = 'GET' + } + @{ + id = 'deviceConfigurations' + url = 'deviceManagement/deviceConfigurations' + method = 'GET' + } + @{ + id = 'configurationPolicies' + url = 'deviceManagement/configurationPolicies' + method = 'GET' + } + @{ + id = 'windowsDriverUpdateProfiles' + url = 'deviceManagement/windowsDriverUpdateProfiles' + method = 'GET' + } + @{ + id = 'windowsFeatureUpdateProfiles' + url = 'deviceManagement/windowsFeatureUpdateProfiles' + method = 'GET' + } + @{ + id = 'windowsQualityUpdatePolicies' + url = 'deviceManagement/windowsQualityUpdatePolicies' + method = 'GET' + } + @{ + id = 'windowsQualityUpdateProfiles' + url = 'deviceManagement/windowsQualityUpdateProfiles' + method = 'GET' + } + ) - $TenantIntunePolicies = [System.Collections.Generic.List[object]]::new() + $TenantIntunePolicies = [System.Collections.Generic.List[object]]::new() - try { - $IntuneGraphRequest = New-GraphBulkRequest -Requests $IntuneRequests -tenantid $TenantFilter -asapp $true - - foreach ($Request in $IntuneGraphRequest) { - if ($Request.body.value) { - foreach ($Policy in $Request.body.value) { - $TenantIntunePolicies.Add([PSCustomObject]@{ - Type = $Request.id - Policy = $Policy - }) - } - } - } - } catch { - Write-Warning "Failed to get Intune policies: $($_.Exception.Message)" - } + try { + $IntuneGraphRequest = New-GraphBulkRequest -Requests $IntuneRequests -tenantid $TenantFilter -asapp $true - # Get Conditional Access policies - try { - $CARequests = @( - @{ - id = 'policies' - url = 'identity/conditionalAccess/policies' - method = 'GET' + foreach ($Request in $IntuneGraphRequest) { + if ($Request.body.value) { + foreach ($Policy in $Request.body.value) { + $TenantIntunePolicies.Add([PSCustomObject]@{ + Type = $Request.id + Policy = $Policy + }) } - ) - $CAGraphRequest = New-GraphBulkRequest -Requests $CARequests -tenantid $TenantFilter -asapp $true - $TenantCAPolicies = ($CAGraphRequest | Where-Object { $_.id -eq 'policies' }).body.value - } catch { - Write-Warning "Failed to get Conditional Access policies: $($_.Exception.Message)" - $TenantCAPolicies = @() + } } + } catch { + Write-Warning "Failed to get Intune policies: $($_.Exception.Message)" + } + # Get Conditional Access policies + try { + $CARequests = @( + @{ + id = 'policies' + url = 'identity/conditionalAccess/policies' + method = 'GET' + } + ) + $CAGraphRequest = New-GraphBulkRequest -Requests $CARequests -tenantid $TenantFilter -asapp $true + $TenantCAPolicies = ($CAGraphRequest | Where-Object { $_.id -eq 'policies' }).body.value + } catch { + Write-Warning "Failed to get Conditional Access policies: $($_.Exception.Message)" + $TenantCAPolicies = @() } if ($Alignment.standardSettings) { diff --git a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 index bf93a9098812..338d4abc637b 100644 --- a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 @@ -27,6 +27,14 @@ function New-CIPPAlertTemplate { if ($Data -is [array] -and $Data[0] -is [string]) { $Data = $Data | ForEach-Object { @{ message = $_ } } } + if ($InputObject -eq 'driftStandard') { + $Title = "CIPP Alert - Standard Drift Detected for $($Tenant)" + $DataHTML = ($Data | ConvertTo-Html | Out-String).Replace('', '
') + $IntroText = "

You've setup your instance to receive alerts when a tenant is drifting away from your standard. This seems to have happened! We've found the following deviations.

$dataHTML" + $ButtonUrl = "$CIPPURL/cipp/logs" + $ButtonText = 'Investigate and remediate deviations' + $AfterButtonText = 'Click the button above to go to the logbook and investigate the deviations. You can also use the standards page to remediate the deviations.' + } if ($InputObject -eq 'sherwebmig') { $DataHTML = ($Data | ConvertTo-Html | Out-String).Replace('
', '
') diff --git a/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 b/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 index 90f3e36c3649..e1d60e66bb87 100644 --- a/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 +++ b/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 @@ -7,6 +7,8 @@ function Send-CIPPAlert { $HTMLContent, $JSONContent, $TenantFilter, + $altEmail, + $altWebhook, $APIName = 'Send Alert', $Headers, $TableName, @@ -19,8 +21,11 @@ function Send-CIPPAlert { if ($Type -eq 'email') { Write-Information 'Trying to send email' try { - if ($Config.email -like '*@*') { - $Recipients = $Config.email.split($(if ($Config.email -like '*,*') { ',' } else { ';' })).trim() | ForEach-Object { if ($_ -like '*@*') { [pscustomobject]@{EmailAddress = @{Address = $_ } } } } + if ($Config.email -like '*@*' -or $altEmail -like '*@*') { + $Recipients = if ($AltEmail) { $AltEmail } + else { + $Config.email.split($(if ($Config.email -like '*,*') { ',' } else { ';' })).trim() | ForEach-Object { if ($_ -like '*@*') { [pscustomobject]@{EmailAddress = @{Address = $_ } } } } + } $PowerShellBody = [PSCustomObject]@{ message = @{ subject = $Title @@ -72,16 +77,17 @@ function Send-CIPPAlert { Write-Information 'Trying to send webhook' try { - if ($Config.webhook -ne '') { + if ($Config.webhook -ne '' -or $AltWebhook -ne '') { if ($PSCmdlet.ShouldProcess($Config.webhook, 'Sending webhook')) { - switch -wildcard ($config.webhook) { + $webhook = if ($AltWebhook) { $AltWebhook } else { $Config.webhook } + switch -wildcard ($webhook) { '*webhook.office.com*' { $JSONBody = "{`"text`": `"You've setup your alert policies to be alerted whenever specific events happen. We've found some of these events in the log.

$JSONContent`"}" - Invoke-RestMethod -Uri $config.webhook -Method POST -ContentType 'Application/json' -Body $JSONBody + Invoke-RestMethod -Uri $webhook -Method POST -ContentType 'Application/json' -Body $JSONBody } '*discord.com*' { $JSONBody = "{`"content`": `"You've setup your alert policies to be alerted whenever specific events happen. We've found some of these events in the log. $JSONContent`"}" - Invoke-RestMethod -Uri $config.webhook -Method POST -ContentType 'Application/json' -Body $JSONBody + Invoke-RestMethod -Uri $webhook -Method POST -ContentType 'Application/json' -Body $JSONBody } '*slack.com*' { $SlackBlocks = Get-SlackAlertBlocks -JSONBody $JSONContent @@ -90,10 +96,10 @@ function Send-CIPPAlert { } else { $JSONBody = "{`"text`": `"You've setup your alert policies to be alerted whenever specific events happen. We've found some of these events in the log. $JSONContent`"}" } - Invoke-RestMethod -Uri $config.webhook -Method POST -ContentType 'Application/json' -Body $JSONBody + Invoke-RestMethod -Uri $webhook -Method POST -ContentType 'Application/json' -Body $JSONBody } default { - Invoke-RestMethod -Uri $config.webhook -Method POST -ContentType 'Application/json' -Body $JSONContent + Invoke-RestMethod -Uri $webhook -Method POST -ContentType 'Application/json' -Body $JSONContent } } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 index fd220aab81b3..685fd0e65c27 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 @@ -31,7 +31,7 @@ function Invoke-CIPPStandardConditionalAccessTemplate { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'ConditionalAccess' $Table = Get-CippTable -tablename 'templates' - $TestResult = Test-CIPPStandardLicense -StandardName 'ConditionalAccessTemplates_general' -TenantFilter $Tenant -RequiredCapabilities @('AAD_PREMIUM', 'AAD_PREMIUM_P2') + $TestResult = Test-CIPPStandardLicense -StandardName 'ConditionalAccessTemplate_general' -TenantFilter $Tenant -RequiredCapabilities @('AAD_PREMIUM', 'AAD_PREMIUM_P2') if ($TestResult -eq $false) { #writing to each item that the license is not present. $settings.TemplateList | ForEach-Object { From c906c4edb997b262317432fcb178bfe19e5ab90d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Wed, 30 Jul 2025 23:32:47 +0200 Subject: [PATCH 58/85] fix denied bug --- .../Invoke-ExecUpdateDriftDeviation.ps1 | 5 +++-- Modules/CIPPCore/Public/Get-CIPPDrift.ps1 | 2 +- .../Public/Set-CIPPDriftDeviation.ps1 | 22 ++++++++++++++----- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 index 7e419defe23c..3723f349d703 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 @@ -30,14 +30,15 @@ function Invoke-ExecUpdateDriftDeviation { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed all drift customizations for tenant $TenantFilter" -Sev 'Info' } else { $Deviations = $Request.Body.deviations + $Reason = $Request.Body.reason $Results = foreach ($Deviation in $Deviations) { try { - $Result = Set-CIPPDriftDeviation -TenantFilter $TenantFilter -StandardName $Deviation.standardName -Status $Deviation.status + $Result = Set-CIPPDriftDeviation -TenantFilter $TenantFilter -StandardName $Deviation.standardName -Status $Deviation.status -Reason $Reason -user $request.headers.'x-ms-client-principal' [PSCustomObject]@{ success = $true result = $Result } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Updated drift deviation status for $($Deviation.standardName) to $($Deviation.status)" -Sev 'Info' + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Updated drift deviation status for $($Deviation.standardName) to $($Deviation.status) with reason: $Reason" -Sev 'Info' if ($Deviation.status -eq 'DeniedRemediate') { $Setting = $Deviation.standardName -replace 'standards.', '' $StandardTemplate = Get-CIPPTenantAlignment -TenantFilter $TenantFilter | Where-Object -Property standardType -EQ 'drift' diff --git a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 index 127607edb86e..79614368e5c4 100644 --- a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 @@ -288,7 +288,7 @@ function Get-CIPPDrift { # Filter deviations by status for counting $NewDeviations = $AllDeviations | Where-Object { $_.Status -eq 'New' } $AcceptedDeviations = $AllDeviations | Where-Object { $_.Status -eq 'Accepted' } - $DeniedDeviations = $AllDeviations | Where-Object { $_.Status -eq 'Denied' } + $DeniedDeviations = $AllDeviations | Where-Object { $_.Status -like 'Denied*' } $CustomerSpecificDeviations = $AllDeviations | Where-Object { $_.Status -eq 'CustomerSpecific' } # Current deviations are New + Denied (not accepted or customer specific) diff --git a/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 b/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 index 3135ebb2545d..f2d13ea545fd 100644 --- a/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 @@ -10,25 +10,28 @@ function Set-CIPPDriftDeviation { .PARAMETER StandardName The standard name (used as RowKey, with '.' replaced by '_') .PARAMETER Status - The status to set. Valid values: Accepted, New, Denied, CustomerSpecific + The status to set. Valid values: Accepted, New, Denied, CustomerSpecific, DeniedRemediate, DeniedDelete + .PARAMETER Reason + Optional reason for the status change .FUNCTIONALITY Internal .EXAMPLE - Set-CIPPDriftDeviation -TenantFilter "contoso.onmicrosoft.com" -StandardName "IntuneTemplates.12345" -Status "Accepted" + Set-CIPPDriftDeviation -TenantFilter "contoso.onmicrosoft.com" -StandardName "IntuneTemplates.12345" -Status "Accepted" -Reason "Business requirement" .EXAMPLE - Set-CIPPDriftDeviation -TenantFilter "contoso.onmicrosoft.com" -StandardName "standards.passwordComplexity" -Status "CustomerSpecific" + Set-CIPPDriftDeviation -TenantFilter "contoso.onmicrosoft.com" -StandardName "standards.passwordComplexity" -Status "CustomerSpecific" -Reason "Custom security policy" #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$TenantFilter, - [Parameter(Mandatory = $true)] [string]$StandardName, - [Parameter(Mandatory = $true)] [ValidateSet('Accepted', 'New', 'Denied', 'CustomerSpecific', 'DeniedRemediate', 'DeniedDelete')] - [string]$Status + [string]$Status, + [Parameter(Mandatory = $false)] + [string]$Reason, + [string]$user ) try { @@ -39,8 +42,15 @@ function Set-CIPPDriftDeviation { RowKey = $RowKey StandardName = $StandardName Status = $Status + User = $user LastModified = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') } + + # Add reason if provided + if ($Reason) { + $Entity.Reason = $Reason + } + $Result = Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force Write-Verbose "Successfully set drift deviation status for $StandardName to $Status" From 37e7435c4c42b87e274bc41de8304ae34a91560d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Wed, 30 Jul 2025 23:51:47 +0200 Subject: [PATCH 59/85] update drift settings --- .../Invoke-ExecUpdateDriftDeviation.ps1 | 4 +++- Modules/CIPPCore/Public/Get-CIPPDrift.ps1 | 22 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 index 3723f349d703..6ad626c79a1d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 @@ -33,7 +33,9 @@ function Invoke-ExecUpdateDriftDeviation { $Reason = $Request.Body.reason $Results = foreach ($Deviation in $Deviations) { try { - $Result = Set-CIPPDriftDeviation -TenantFilter $TenantFilter -StandardName $Deviation.standardName -Status $Deviation.status -Reason $Reason -user $request.headers.'x-ms-client-principal' + $user = $request.headers.'x-ms-client-principal' + $username = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($user)) | ConvertFrom-Json).userDetails + $Result = Set-CIPPDriftDeviation -TenantFilter $TenantFilter -StandardName $Deviation.standardName -Status $Deviation.status -Reason $Reason -user $username [PSCustomObject]@{ success = $true result = $Result diff --git a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 index 79614368e5c4..36c632428e51 100644 --- a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 @@ -43,7 +43,7 @@ function Get-CIPPDrift { try { $DriftEntities = Get-CIPPAzDataTableEntity @DriftTable -Filter $DriftFilter foreach ($Entity in $DriftEntities) { - $ExistingDriftStates[$Entity.StandardName] = $Entity.Status + $ExistingDriftStates[$Entity.StandardName] = $Entity } } catch { Write-Warning "Failed to get existing drift states: $($_.Exception.Message)" @@ -60,16 +60,20 @@ function Get-CIPPDrift { foreach ($ComparisonItem in $Alignment.ComparisonDetails) { if ($ComparisonItem.Compliant -ne $true) { $Status = if ($ExistingDriftStates.ContainsKey($ComparisonItem.StandardName)) { - $ExistingDriftStates[$ComparisonItem.StandardName] + $ExistingDriftStates[$ComparisonItem.StandardName].Status } else { 'New' } + $reason = if ($ExistingDriftStates.ContainsKey($ComparisonItem.StandardName)) { $ExistingDriftStates[$ComparisonItem.StandardName].Reason } + $User = if ($ExistingDriftStates.ContainsKey($ComparisonItem.StandardName)) { $ExistingDriftStates[$ComparisonItem.StandardName].User } $StandardsDeviations.Add([PSCustomObject]@{ - standardName = $ComparisonItem.StandardName - expectedValue = 'Compliant' - receivedValue = $ComparisonItem.StandardValue - state = 'current' - Status = $Status + standardName = $ComparisonItem.StandardName + expectedValue = 'Compliant' + receivedValue = $ComparisonItem.StandardValue + state = 'current' + Status = $Status + Reason = $reason + lastChangedByUser = $User }) } } @@ -233,7 +237,7 @@ function Get-CIPPDrift { if (-not $PolicyFound) { $PolicyKey = "IntuneTemplates.$($TenantPolicy.Policy.id)" $Status = if ($ExistingDriftStates.ContainsKey($PolicyKey)) { - $ExistingDriftStates[$PolicyKey] + $ExistingDriftStates[$PolicyKey].Status } else { 'New' } @@ -263,7 +267,7 @@ function Get-CIPPDrift { if (-not $PolicyFound) { $PolicyKey = "ConditionalAccessTemplates.$($TenantCAPolicy.id)" $Status = if ($ExistingDriftStates.ContainsKey($PolicyKey)) { - $ExistingDriftStates[$PolicyKey] + $ExistingDriftStates[$PolicyKey].Status } else { 'New' } From b950f2e45660f5c9412b8c812e3b91636b7eb387 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 31 Jul 2025 00:02:43 +0200 Subject: [PATCH 60/85] updates to drift management --- .../Standards/Push-CIPPDriftManagement.ps1 | 2 ++ Modules/CippExtensions/Public/New-CippExtAlert.ps1 | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 index a24e4878e105..6f0cd7ddb9c7 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 @@ -62,8 +62,10 @@ function Push-CippDriftManagement { TenantFilter = $TenantFilter } Send-CIPPAlert @CIPPAlert + return $true } else { Write-LogMessage -API 'DriftStandards' -tenant $Item.Tenant -message "No new drift deviations found for tenant $($Item.Tenant)" -sev Info + return $true } Write-Information "Drift management completed for tenant $($Item.Tenant)" } catch { diff --git a/Modules/CippExtensions/Public/New-CippExtAlert.ps1 b/Modules/CippExtensions/Public/New-CippExtAlert.ps1 index 28a4b957c2cb..bae549bf0719 100644 --- a/Modules/CippExtensions/Public/New-CippExtAlert.ps1 +++ b/Modules/CippExtensions/Public/New-CippExtAlert.ps1 @@ -6,13 +6,13 @@ function New-CippExtAlert { ) #Get the current CIPP Alerts table and see what system is configured to receive alerts $Table = Get-CIPPTable -TableName Extensionsconfig - $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 + $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 -ErrorAction SilentlyContinue $MappingTable = Get-CIPPTable -TableName CippMapping foreach ($ConfigItem in $Configuration.psobject.properties.name) { switch ($ConfigItem) { 'HaloPSA' { - If ($Configuration.HaloPSA.enabled) { + if ($Configuration.HaloPSA.enabled) { $MappingFile = Get-CIPPAzDataTableEntity @MappingTable -Filter "PartitionKey eq 'HaloMapping'" $TenantId = (Get-Tenants | Where-Object defaultDomainName -EQ $Alert.TenantId).customerId Write-Host "TenantId: $TenantId" @@ -24,7 +24,7 @@ function New-CippExtAlert { } } 'Gradient' { - If ($Configuration.Gradient.enabled) { + if ($Configuration.Gradient.enabled) { New-GradientAlert -Title $Alert.AlertTitle -Description $Alert.AlertText -Client $Alert.TenantId } } From f9faf88e0035e7e7b1ee78c4b943a58a4c711a04 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 31 Jul 2025 01:25:51 +0200 Subject: [PATCH 61/85] hsitory timeline for tenant --- Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 index 6e360b25a6a0..2ad36488d435 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 @@ -28,6 +28,7 @@ function Invoke-ListLogs { $LogLevel = if ($Request.Query.Severity) { ($Request.query.Severity).split(',') } else { 'Info', 'Warn', 'Error', 'Critical', 'Alert' } $PartitionKey = $Request.Query.DateFilter $username = $Request.Query.User ?? '*' + $TenantFilter = $Request.Query.Tenant $StartDate = $Request.Query.StartDate ?? $Request.Query.DateFilter $EndDate = $Request.Query.EndDate ?? $Request.Query.DateFilter @@ -48,12 +49,17 @@ function Invoke-ListLogs { $LogLevel = 'Info', 'Warn', 'Error', 'Critical', 'Alert' $PartitionKey = Get-Date -UFormat '%Y%m%d' $username = '*' + $TenantFilter = $null $Filter = "PartitionKey eq '{0}'" -f $PartitionKey } $AllowedTenants = Test-CIPPAccess -Request $Request -TenantList Write-Host "Getting logs for filter: $Filter, LogLevel: $LogLevel, Username: $username" - $Rows = Get-AzDataTableEntity @Table -Filter $Filter | Where-Object { $_.Severity -in $LogLevel -and $_.Username -like $username } + $Rows = Get-AzDataTableEntity @Table -Filter $Filter | Where-Object { + $_.Severity -in $LogLevel -and + $_.Username -like $username -and + ($TenantFilter -eq $null -or $TenantFilter -eq 'AllTenants' -or $_.Tenant -like "*$TenantFilter*" -or $_.TenantID -eq $TenantFilter) + } if ($AllowedTenants -notcontains 'AllTenants') { $TenantList = Get-Tenants -IncludeErrors | Where-Object { $_.customerId -in $AllowedTenants } From da19911d45007279f156872b0217781cf487dff4 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 31 Jul 2025 01:30:23 +0200 Subject: [PATCH 62/85] apiname bug --- Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 index a6c6ede0714d..5079bbe5c119 100644 --- a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 @@ -44,14 +44,14 @@ function New-CIPPCAPolicy { $GroupIds = [System.Collections.Generic.List[string]]::new() $groupNames | ForEach-Object { if (Test-IsGuid $_) { - Write-LogMessage -Headers $User -API $APINAME -message "Already GUID, no need to replace: $_" -Sev 'Debug' + Write-LogMessage -Headers $User -API 'Create CA Policy' -message "Already GUID, no need to replace: $_" -Sev 'Debug' $GroupIds.Add($_) # it's a GUID, so we keep it } else { $groupId = ($groups | Where-Object -Property displayName -EQ $_).id # it's a display name, so we get the group ID if ($groupId) { foreach ($gid in $groupId) { Write-Warning "Replaced group name $_ with ID $gid" - $null = Write-LogMessage -Headers $User -API $APINAME -message "Replaced group name $_ with ID $gid" -Sev 'Debug' + $null = Write-LogMessage -Headers $User -API 'Create CA Policy' -message "Replaced group name $_ with ID $gid" -Sev 'Debug' $GroupIds.Add($gid) # add the ID to the list } } else { @@ -68,14 +68,14 @@ function New-CIPPCAPolicy { $UserIds = [System.Collections.Generic.List[string]]::new() $userNames | ForEach-Object { if (Test-IsGuid $_) { - Write-LogMessage -Headers $User -API $APINAME -message "Already GUID, no need to replace: $_" -Sev 'Debug' + Write-LogMessage -Headers $User -API 'Create CA Policy' -message "Already GUID, no need to replace: $_" -Sev 'Debug' $UserIds.Add($_) # it's a GUID, so we keep it } else { $userId = ($users | Where-Object -Property displayName -EQ $_).id # it's a display name, so we get the user ID if ($userId) { foreach ($uid in $userId) { Write-Warning "Replaced user name $_ with ID $uid" - $null = Write-LogMessage -Headers $User -API $APINAME -message "Replaced user name $_ with ID $uid" -Sev 'Debug' + $null = Write-LogMessage -Headers $User -API 'Create CA Policy' -message "Replaced user name $_ with ID $uid" -Sev 'Debug' $UserIds.Add($uid) # add the ID to the list } } else { @@ -245,7 +245,7 @@ function New-CIPPCAPolicy { } else { Write-Information "overwriting $($CheckExististing.id)" $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExististing.id)" -tenantid $tenantfilter -type PATCH -body $RawJSON -asApp $true - Write-LogMessage -Headers $User -API $APINAME -tenant $($Tenant) -message "Updated Conditional Access Policy $($JSONObj.Displayname) to the template standard." -Sev 'Info' + Write-LogMessage -Headers $User -API 'Create CA Policy' -tenant $($Tenant) -message "Updated Conditional Access Policy $($JSONObj.Displayname) to the template standard." -Sev 'Info' return "Updated policy $displayname for $tenantfilter" } } else { @@ -254,7 +254,7 @@ function New-CIPPCAPolicy { Start-Sleep 3 } $null = New-GraphPOSTRequest -uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies' -tenantid $tenantfilter -type POST -body $RawJSON -asApp $true - Write-LogMessage -Headers $User -API $APINAME -tenant $($Tenant) -message "Added Conditional Access Policy $($JSONObj.Displayname)" -Sev 'Info' + Write-LogMessage -Headers $User -API 'Create CA Policy' -tenant $($Tenant) -message "Added Conditional Access Policy $($JSONObj.Displayname)" -Sev 'Info' return "Created policy $displayname for $tenantfilter" } } catch { From 53f6dfe435921c528a1b7fa9b84b0af135e3a924 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 31 Jul 2025 01:30:56 +0200 Subject: [PATCH 63/85] disabled --- Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 index 5079bbe5c119..e14bf71d6999 100644 --- a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 @@ -230,7 +230,7 @@ function New-CIPPCAPolicy { #Send request to disable security defaults. $body = '{ "isEnabled": false }' $null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -Type patch -Body $body -ContentType 'application/json' - Write-LogMessage -Headers $User -API $APINAME -tenant $($Tenant) -message "Disabled Security Defaults for tenant $($TenantFilter)" -Sev 'Info' + Write-LogMessage -Headers $User -API 'Create CA Policy' -tenant $($Tenant) -message "Disabled Security Defaults for tenant $($TenantFilter)" -Sev 'Info' Start-Sleep 3 } $RawJSON = ConvertTo-Json -InputObject $JSONObj -Depth 10 -Compress From da6880c5ea7f67ec12ffaf23a2da331a8f982538 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 30 Jul 2025 21:14:20 -0400 Subject: [PATCH 64/85] fix webhook alerts --- .../Activity Triggers/Push-SchedulerCIPPNotifications.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 index 23e199078efc..438511222545 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 @@ -106,7 +106,7 @@ function Push-SchedulerCIPPNotifications { try { Write-Information $($config | ConvertTo-Json) Write-Information $config.webhook - if ($Config.webhook -ne '' -and $null) { + if (![string]::IsNullOrEmpty($config.webhook)) { if ($Currentlog) { $JSONContent = $Currentlog | ConvertTo-Json -Compress Send-CIPPAlert -Type 'webhook' -JSONContent $JSONContent -TenantFilter $Tenant -APIName 'Alerts' From 1346baf7c94d029fbdfdfae2f20b1723176ab6c1 Mon Sep 17 00:00:00 2001 From: rvdwegen Date: Thu, 31 Jul 2025 11:07:02 +0200 Subject: [PATCH 65/85] Re-bulk listlogs --- .../Public/Entrypoints/Invoke-ListLogs.ps1 | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 index 2ad36488d435..c5b900c50a10 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 @@ -23,6 +23,42 @@ function Invoke-ListLogs { label = $_.PartitionKey } } + } elseif ($Request.Query.logentryid) { + # Return single log entry by RowKey + $Filter = "RowKey eq '{0}'" -f $Request.Query.logentryid + $AllowedTenants = Test-CIPPAccess -Request $Request -TenantList + Write-Host "Getting single log entry for RowKey: $($Request.Query.logentryid)" + + $Row = Get-AzDataTableEntity @Table -Filter $Filter + + if ($Row) { + if ($AllowedTenants -notcontains 'AllTenants') { + $TenantList = Get-Tenants -IncludeErrors | Where-Object { $_.customerId -in $AllowedTenants } + } + + if ($AllowedTenants -contains 'AllTenants' -or ($AllowedTenants -notcontains 'AllTenants' -and ($TenantList.defaultDomainName -contains $Row.Tenant -or $Row.Tenant -eq 'CIPP' -or $TenantList.customerId -contains $Row.TenantId)) ) { + $LogData = if ($Row.LogData -and (Test-Json -Json $Row.LogData -ErrorAction SilentlyContinue)) { + $Row.LogData | ConvertFrom-Json + } else { $Row.LogData } + [PSCustomObject]@{ + DateTime = $Row.Timestamp + Tenant = $Row.Tenant + API = $Row.API + Message = $Row.Message + User = $Row.Username + Severity = $Row.Severity + LogData = $LogData + TenantID = if ($Row.TenantID -ne $null) { + $Row.TenantID + } else { + 'None' + } + AppId = $Row.AppId + IP = $Row.IP + RowKey = $Row.RowKey + } + } + } } else { if ($request.Query.Filter -eq 'True') { $LogLevel = if ($Request.Query.Severity) { ($Request.query.Severity).split(',') } else { 'Info', 'Warn', 'Error', 'Critical', 'Alert' } @@ -86,6 +122,7 @@ function Invoke-ListLogs { } AppId = $Row.AppId IP = $Row.IP + RowKey = $Row.RowKey } } } From 2cb186bfc81e01108e55e019f11e56c6aeaa4dd9 Mon Sep 17 00:00:00 2001 From: Esco Date: Wed, 30 Jul 2025 16:06:15 +0200 Subject: [PATCH 66/85] chore: add try catch to CurrentState --- ...nvoke-CIPPStandardActivityBasedTimeout.ps1 | 9 ++++++++- .../Invoke-CIPPStandardAnonReportDisable.ps1 | 9 ++++++++- ...CIPPStandardAuthMethodsPolicyMigration.ps1 | 9 ++++++++- .../Invoke-CIPPStandardAutoAddProxy.ps1 | 19 +++++++++++++------ .../Invoke-CIPPStandardAutoExpandArchive.ps1 | 9 ++++++++- .../Standards/Invoke-CIPPStandardBookings.ps1 | 9 ++++++++- .../Standards/Invoke-CIPPStandardBranding.ps1 | 6 ++++-- .../Invoke-CIPPStandardCloudMessageRecall.ps1 | 9 ++++++++- ...-CIPPStandardConditionalAccessTemplate.ps1 | 10 +++++++++- ...IPPStandardDefaultPlatformRestrictions.ps1 | 6 ++++-- .../Invoke-CIPPStandardDefaultSharingLink.ps1 | 11 +++++++++-- ...voke-CIPPStandardDeletedUserRentention.ps1 | 9 ++++++++- ...PStandardDisableAddShortcutsToOneDrive.ps1 | 10 +++++++++- ...ndardDisableAdditionalStorageProviders.ps1 | 9 ++++++++- .../Invoke-CIPPStandardDisableAppCreation.ps1 | 9 ++++++++- ...nvoke-CIPPStandardDisableBasicAuthSMTP.ps1 | 13 +++++++++++-- .../Invoke-CIPPStandardDisableEmail.ps1 | 11 +++++++++-- .../Invoke-CIPPStandardDisableEntraPortal.ps1 | 9 ++++++++- ...StandardDisableExternalCalendarSharing.ps1 | 10 +++++++++- ...voke-CIPPStandardDisableGuestDirectory.ps1 | 9 ++++++++- .../Invoke-CIPPStandardDisableGuests.ps1 | 9 ++++++++- ...voke-CIPPStandardDisableM365GroupUsers.ps1 | 10 +++++++++- ...nvoke-CIPPStandardDisableOutlookAddins.ps1 | 10 +++++++++- .../Invoke-CIPPStandardDisableQRCodePin.ps1 | 9 ++++++++- .../Invoke-CIPPStandardDisableReshare.ps1 | 9 ++++++++- ...oke-CIPPStandardDisableResourceMailbox.ps1 | 11 +++++++++-- .../Invoke-CIPPStandardDisableSMS.ps1 | 9 ++++++++- ...-CIPPStandardDisableSecurityGroupUsers.ps1 | 9 ++++++++- ...IPPStandardDisableSharePointLegacyAuth.ps1 | 9 ++++++++- ...nvoke-CIPPStandardDisableSharedMailbox.ps1 | 11 +++++++++-- .../Invoke-CIPPStandardDisableTNEF.ps1 | 10 +++++++++- ...voke-CIPPStandardDisableTenantCreation.ps1 | 9 ++++++++- ...voke-CIPPStandardDisableUserSiteCreate.ps1 | 9 ++++++++- .../Invoke-CIPPStandardDisableVoice.ps1 | 9 ++++++++- ...oke-CIPPStandardDisablex509Certificate.ps1 | 9 ++++++++- ...e-CIPPStandardEXODisableAutoForwarding.ps1 | 9 ++++++++- ...voke-CIPPStandardEXOOutboundSpamLimits.ps1 | 10 +++++++++- ...e-CIPPStandardEnableAppConsentRequests.ps1 | 9 ++++++++- ...voke-CIPPStandardEnableCustomerLockbox.ps1 | 10 +++++++++- .../Invoke-CIPPStandardEnableFIDO2.ps1 | 9 ++++++++- ...Invoke-CIPPStandardEnableHardwareOAuth.ps1 | 9 ++++++++- ...nvoke-CIPPStandardEnableLitigationHold.ps1 | 10 +++++++++- .../Invoke-CIPPStandardEnableMailTips.ps1 | 9 ++++++++- ...voke-CIPPStandardEnableMailboxAuditing.ps1 | 9 ++++++++- .../Invoke-CIPPStandardExcludedfileExt.ps1 | 9 ++++++++- .../Invoke-CIPPStandardExternalMFATrusted.ps1 | 9 ++++++++- .../Invoke-CIPPStandardFocusedInbox.ps1 | 9 ++++++++- ...PStandardGlobalQuarantineNotifications.ps1 | 11 ++++++++++- .../Invoke-CIPPStandardGuestInvite.ps1 | 9 ++++++++- ...e-CIPPStandardIntuneComplianceSettings.ps1 | 10 +++++++++- .../Standards/Invoke-CIPPStandardMDMScope.ps1 | 9 ++++++++- .../Invoke-CIPPStandardMailContacts.ps1 | 11 +++++++++-- ...oke-CIPPStandardMailboxRecipientLimits.ps1 | 9 ++++++++- ...Invoke-CIPPStandardMalwareFilterPolicy.ps1 | 13 ++++++++++--- .../Invoke-CIPPStandardMessageExpiration.ps1 | 9 ++++++++- .../Invoke-CIPPStandardOauthConsent.ps1 | 9 ++++++++- .../Invoke-CIPPStandardOauthConsentLowSec.ps1 | 13 +++++++++++-- .../Invoke-CIPPStandardOutBoundSpamAlert.ps1 | 9 ++++++++- ...CIPPStandardPWcompanionAppAllowedState.ps1 | 9 ++++++++- ...rdPWdisplayAppInformationRequiredState.ps1 | 10 +++++++++- ...oke-CIPPStandardPasswordExpireDisabled.ps1 | 10 +++++++++- .../Invoke-CIPPStandardPerUserMFA.ps1 | 9 ++++++++- ...-CIPPStandardPhishSimSpoofIntelligence.ps1 | 11 +++++++++-- ...Invoke-CIPPStandardPhishingSimulations.ps1 | 13 ++++++++++--- .../Invoke-CIPPStandardProfilePhotos.ps1 | 11 +++++++++-- ...oke-CIPPStandardQuarantineRequestAlert.ps1 | 13 ++++++++++--- .../Invoke-CIPPStandardRetentionPolicyTag.ps1 | 16 ++++++++++++---- .../Invoke-CIPPStandardRotateDKIM.ps1 | 9 ++++++++- .../Invoke-CIPPStandardSPAzureB2B.ps1 | 9 ++++++++- .../Invoke-CIPPStandardSPDirectSharing.ps1 | 11 +++++++++-- ...e-CIPPStandardSPDisableLegacyWorkflows.ps1 | 9 ++++++++- ...ke-CIPPStandardSPDisallowInfectedFiles.ps1 | 9 ++++++++- .../Invoke-CIPPStandardSPEmailAttestation.ps1 | 10 +++++++++- ...e-CIPPStandardSPExternalUserExpiration.ps1 | 9 ++++++++- .../Invoke-CIPPStandardSPSyncButtonState.ps1 | 9 ++++++++- ...nvoke-CIPPStandardSafeAttachmentPolicy.ps1 | 13 ++++++++++--- .../Invoke-CIPPStandardSafeLinksPolicy.ps1 | 9 ++++++++- .../Invoke-CIPPStandardSecurityDefaults.ps1 | 9 ++++++++- .../Invoke-CIPPStandardSendFromAlias.ps1 | 9 ++++++++- ...oke-CIPPStandardSendReceiveLimitTenant.ps1 | 10 +++++++++- ...IPPStandardSharePointMassDeletionAlert.ps1 | 13 ++++++++++--- .../Invoke-CIPPStandardShortenMeetings.ps1 | 11 ++++++++++- .../Invoke-CIPPStandardSpamFilterPolicy.ps1 | 11 ++++++++++- .../Invoke-CIPPStandardSpoofWarn.ps1 | 9 ++++++++- .../Invoke-CIPPStandardStaleEntraDevices.ps1 | 11 ++++++++++- .../Standards/Invoke-CIPPStandardTAP.ps1 | 9 ++++++++- ...voke-CIPPStandardTeamsEmailIntegration.ps1 | 16 ++++++++++++---- .../Invoke-CIPPStandardTeamsEnrollUser.ps1 | 10 +++++++++- ...-CIPPStandardTeamsExternalAccessPolicy.ps1 | 10 +++++++++- ...e-CIPPStandardTeamsExternalFileSharing.ps1 | 12 ++++++++++-- ...PPStandardTeamsFederationConfiguration.ps1 | 10 +++++++++- ...e-CIPPStandardTeamsGlobalMeetingPolicy.ps1 | 11 ++++++++++- .../Invoke-CIPPStandardTeamsGuestAccess.ps1 | 16 ++++++++++++---- ...tandardTeamsMeetingRecordingExpiration.ps1 | 10 +++++++++- ...e-CIPPStandardTeamsMeetingVerification.ps1 | 12 +++++++++++- ...oke-CIPPStandardTeamsMeetingsByDefault.ps1 | 10 +++++++++- ...nvoke-CIPPStandardTeamsMessagingPolicy.ps1 | 10 +++++++++- ...voke-CIPPStandardTenantDefaultTimezone.ps1 | 10 +++++++++- .../Invoke-CIPPStandardUndoOauth.ps1 | 10 +++++++++- ...voke-CIPPStandardUserPreferredLanguage.ps1 | 10 +++++++++- .../Invoke-CIPPStandardUserSubmissions.ps1 | 10 ++++++++-- .../Invoke-CIPPStandardallowOAuthTokens.ps1 | 9 ++++++++- .../Invoke-CIPPStandardallowOTPTokens.ps1 | 9 ++++++++- .../Invoke-CIPPStandarddisableMacSync.ps1 | 9 ++++++++- ...voke-CIPPStandardintuneBrandingProfile.ps1 | 9 ++++++++- .../Invoke-CIPPStandardintuneDeviceReg.ps1 | 9 ++++++++- ...CIPPStandardintuneDeviceRetirementDays.ps1 | 10 +++++++++- .../Invoke-CIPPStandardintuneRequireMFA.ps1 | 9 ++++++++- .../Standards/Invoke-CIPPStandardlaps.ps1 | 9 ++++++++- .../Invoke-CIPPStandardsharingCapability.ps1 | 9 ++++++++- ...e-CIPPStandardsharingDomainRestriction.ps1 | 9 ++++++++- .../Invoke-CIPPStandardunmanagedSync.ps1 | 10 +++++++++- 112 files changed, 969 insertions(+), 150 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardActivityBasedTimeout.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardActivityBasedTimeout.ps1 index f9516efe5c20..9cb907acc42c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardActivityBasedTimeout.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardActivityBasedTimeout.ps1 @@ -43,7 +43,14 @@ function Invoke-CIPPStandardActivityBasedTimeout { Return } - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/activityBasedTimeoutPolicies' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/activityBasedTimeoutPolicies' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the ActivityBasedTimeout state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = if ($CurrentState.definition -like "*$timeout*") { $true } else { $false } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAnonReportDisable.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAnonReportDisable.ps1 index 5a0d65510fe5..83accf8b4274 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAnonReportDisable.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAnonReportDisable.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardAnonReportDisable { param($Tenant, $Settings) #$Rerun -Type Standard -Tenant $Tenant -API 'allowOTPTokens' -Settings $Settings - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/reportSettings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/reportSettings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the AnonReportDisable state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 index 2d40d7a7e266..dffa9b3f24ef 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 @@ -28,7 +28,14 @@ function Invoke-CIPPStandardAuthMethodsPolicyMigration { #> param($Tenant, $Settings) - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the AuthMethodsPolicyMigration state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { if ($CurrentInfo.policyMigrationState -eq 'migrationComplete') { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoAddProxy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoAddProxy.ps1 index 09a97aabe0d2..bca072529478 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoAddProxy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoAddProxy.ps1 @@ -35,9 +35,16 @@ function Invoke-CIPPStandardAutoAddProxy { $QueueItem ) - $Domains = New-ExoRequest -TenantId $Tenant -Cmdlet 'Get-AcceptedDomain' | Select-Object -ExpandProperty DomainName - $AllMailboxes = New-ExoRequest -TenantId $Tenant -Cmdlet 'Get-Mailbox' - + try { + $Domains = New-ExoRequest -TenantId $Tenant -Cmdlet 'Get-AcceptedDomain' | Select-Object -ExpandProperty DomainName + $AllMailboxes = New-ExoRequest -TenantId $Tenant -Cmdlet 'Get-Mailbox' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the AutoAddProxy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $MissingProxies = 0 foreach ($Domain in $Domains) { $ProcessMailboxes = $AllMailboxes | Where-Object { @@ -47,15 +54,15 @@ function Invoke-CIPPStandardAutoAddProxy { } $MissingProxies += $ProcessMailboxes.Count } - + $StateIsCorrect = $MissingProxies -eq 0 - + if ($Settings.report -eq $true) { $state = $StateIsCorrect ? $true : $MissingProxies Set-CIPPStandardsCompareField -FieldName 'standards.AutoAddProxy' -FieldValue $state -TenantFilter $Tenant Add-CIPPBPAField -FieldName 'AutoAddProxy' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $Tenant } - + if ($Settings.alert -eq $true) { if ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -tenant $Tenant -message 'All mailboxes have proxy addresses for all domains' -sev Info diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoExpandArchive.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoExpandArchive.ps1 index 940f2c236ac3..c0ee4bc5bdf3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoExpandArchive.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoExpandArchive.ps1 @@ -36,7 +36,14 @@ function Invoke-CIPPStandardAutoExpandArchive { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'AutoExpandArchive' - $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').AutoExpandingArchiveEnabled + try { + $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').AutoExpandingArchiveEnabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the AutoExpandArchive state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { Write-Host 'Time to remediate' diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBookings.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBookings.ps1 index e1b10d9944cb..6c8f529b2de0 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBookings.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBookings.ps1 @@ -40,7 +40,14 @@ function Invoke-CIPPStandardBookings { # Get state value using null-coalescing operator $state = $Settings.state.value ?? $Settings.state - $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').BookingsEnabled + try { + $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').BookingsEnabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the Bookings state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $WantedState = if ($state -eq 'true') { $true } else { $false } $StateIsCorrect = if ($CurrentState -eq $WantedState) { $true } else { $false } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBranding.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBranding.ps1 index 24ddefaffd0b..b3388943900b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBranding.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBranding.ps1 @@ -40,9 +40,11 @@ function Invoke-CIPPStandardBranding { try { $CurrentState = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/organization/$($TenantId.customerId)/branding/localizations/0" -tenantID $Tenant -AsApp $true - } catch { + } + catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the branding for $Tenant. This tenant might not have premium licenses available: $ErrorMessage" -Sev Error + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the Branding state for $Tenant. Error: $ErrorMessage" -Sev Error + return } # Get layoutTemplateType value using null-coalescing operator diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 index ccd6411243c1..c7ff49f9cc94 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 @@ -40,7 +40,14 @@ function Invoke-CIPPStandardCloudMessageRecall { # Get state value using null-coalescing operator $state = $Settings.state.value ?? $Settings.state - $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').MessageRecallEnabled + try { + $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').MessageRecallEnabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the CloudMessageRecall state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $WantedState = if ($state -eq 'true') { $true } else { $false } $StateIsCorrect = if ($CurrentState -eq $WantedState) { $true } else { $false } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 index 685fd0e65c27..406f02485f1a 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 @@ -40,7 +40,15 @@ function Invoke-CIPPStandardConditionalAccessTemplate { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $AllCAPolicies = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies?$top=999' -tenantid $Tenant + + try { + $AllCAPolicies = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies?$top=999' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the ConditionalAccessTemplate state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { foreach ($Setting in $Settings) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultPlatformRestrictions.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultPlatformRestrictions.ps1 index 133f804ad5e9..c75ee0ecd60b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultPlatformRestrictions.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultPlatformRestrictions.ps1 @@ -48,9 +48,11 @@ function Invoke-CIPPStandardDefaultPlatformRestrictions { try { $CurrentState = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations?`$expand=assignments&orderBy=priority&`$filter=deviceEnrollmentConfigurationType eq 'SinglePlatformRestriction'" -tenantID $Tenant -AsApp $true | Select-Object -Property id, androidForWorkRestriction, androidRestriction, iosRestriction, macOSRestriction, windowsRestriction - } catch { + } + catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DefaultPlatformRestrictions for $Tenant. This tenant might not have premium licenses available: $ErrorMessage" -Sev Error + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DefaultPlatformRestrictions state for $Tenant. Error: $ErrorMessage" -Sev Error + return } $StateIsCorrect = ($CurrentState.androidForWorkRestriction.platformBlocked -eq $Settings.platformAndroidForWorkBlocked) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultSharingLink.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultSharingLink.ps1 index f7fd81002f10..ae4c238ce051 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultSharingLink.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultSharingLink.ps1 @@ -48,8 +48,15 @@ function Invoke-CIPPStandardDefaultSharingLink { } $DesiredSharingLinkTypeValue = $SharingLinkTypeMap[$DesiredSharingLinkType] - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | - Select-Object -Property _ObjectIdentity_, TenantFilter, DefaultSharingLinkType, DefaultLinkPermission + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + Select-Object -Property _ObjectIdentity_, TenantFilter, DefaultSharingLinkType, DefaultLinkPermission + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DefaultSharingLink state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Check if the current state matches the desired configuration $StateIsCorrect = ($CurrentState.DefaultSharingLinkType -eq $DesiredSharingLinkTypeValue) -and ($CurrentState.DefaultLinkPermission -eq 1) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 index 3f9e1d4bece1..aca77117f96b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 @@ -37,7 +37,14 @@ function Invoke-CIPPStandardDeletedUserRentention { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DeletedUserRetention state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $Days = $Settings.Days.value ?? $Settings.Days if ($Settings.report -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAddShortcutsToOneDrive.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAddShortcutsToOneDrive.ps1 index d8582c299a21..d926b5a69270 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAddShortcutsToOneDrive.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAddShortcutsToOneDrive.ps1 @@ -37,7 +37,15 @@ function Invoke-CIPPStandardDisableAddShortcutsToOneDrive { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object _ObjectIdentity_, TenantFilter, DisableAddToOneDrive + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + Select-Object _ObjectIdentity_, TenantFilter, DisableAddToOneDrive + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableAddShortcutsToOneDrive state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Input validation $StateValue = $Settings.state.value ?? $Settings.state diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAdditionalStorageProviders.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAdditionalStorageProviders.ps1 index ab555b551825..f0275a9eb92c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAdditionalStorageProviders.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAdditionalStorageProviders.ps1 @@ -39,7 +39,14 @@ function Invoke-CIPPStandardDisableAdditionalStorageProviders { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableAdditionalStorageProviders' - $AdditionalStorageProvidersState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OwaMailboxPolicy' -cmdParams @{Identity = 'OwaMailboxPolicy-Default' } -Select 'Identity, AdditionalStorageProvidersAvailable' + try { + $AdditionalStorageProvidersState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OwaMailboxPolicy' -cmdParams @{Identity = 'OwaMailboxPolicy-Default' } -Select 'Identity, AdditionalStorageProvidersAvailable' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableAddShortcutsToOneDrive state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAppCreation.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAppCreation.ps1 index e295bf78dec1..477cc75827e6 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAppCreation.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAppCreation.ps1 @@ -34,7 +34,14 @@ function Invoke-CIPPStandardDisableAppCreation { ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableAppCreation' - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy?$select=defaultUserRolePermissions' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy?$select=defaultUserRolePermissions' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableAppCreation state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { if ($CurrentInfo.defaultUserRolePermissions.allowedToCreateApps -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 index da0f37a1b4d8..f473595b3c53 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 @@ -38,8 +38,17 @@ function Invoke-CIPPStandardDisableBasicAuthSMTP { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableBasicAuthSMTP' - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-TransportConfig' - $SMTPusers = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-CASMailbox' -cmdParams @{ ResultSize = 'Unlimited' } | Where-Object { ($_.SmtpClientAuthenticationDisabled -eq $false) } + try { + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-TransportConfig' + + $SMTPusers = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-CASMailbox' -cmdParams @{ ResultSize = 'Unlimited' } | + Where-Object { ($_.SmtpClientAuthenticationDisabled -eq $false) } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableBasicAuthSMTP state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { Write-Host 'Time to remediate' diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 index 2316844428a2..3ce32493c7e3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardDisableEmail { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableEmail' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Email' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Email' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableEmail state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'disabled') If ($Settings.remediate -eq $true) { @@ -54,7 +61,7 @@ function Invoke-CIPPStandardDisableEmail { } if ($Settings.report -eq $true) { - $state = $StateIsCorrect -eq $true ? $true : $CurrentState + $state = $StateIsCorrect -eq $true ? $true : $CurrentState Set-CIPPStandardsCompareField -FieldName 'standards.DisableEmail' -FieldValue $state -TenantFilter $Tenant Add-CIPPBPAField -FieldName 'DisableEmail' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEntraPortal.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEntraPortal.ps1 index 85f86c5d55aa..1f791cbd4b26 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEntraPortal.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEntraPortal.ps1 @@ -12,7 +12,14 @@ function Invoke-CIPPStandardDisableEntraPortal { param($Tenant, $Settings) #$Rerun -Type Standard -Tenant $Tenant -API 'allowOTPTokens' -Settings $Settings #This standard is still unlisted due to MS fixing some permissions. This will be added to the list once it is fixed. - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/admin/entra/uxSetting' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/admin/entra/uxSetting' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableEntraPortal state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { if ($CurrentInfo.restrictNonAdminAccess) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExternalCalendarSharing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExternalCalendarSharing.ps1 index 9873bf4e088c..6b4c29c2be3b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExternalCalendarSharing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExternalCalendarSharing.ps1 @@ -39,7 +39,15 @@ function Invoke-CIPPStandardDisableExternalCalendarSharing { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableExternalCalendarSharing' - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SharingPolicy' | Where-Object { $_.Default -eq $true } + try { + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SharingPolicy' | + Where-Object { $_.Default -eq $true } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableExternalCalendarSharing state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { if ($CurrentInfo.Enabled) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuestDirectory.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuestDirectory.ps1 index bd3f35789266..99b096c0ccd8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuestDirectory.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuestDirectory.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardDisableGuestDirectory { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableGuestDirectory' - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableGuestDirectory state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 index 56ac2349159d..1c9dfcc644b4 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 @@ -36,8 +36,15 @@ function Invoke-CIPPStandardDisableGuests { $Lookup = $90Days.ToString('o') $AuditLookup = (Get-Date).AddDays(-7).ToUniversalTime().ToString('o') - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$filter=createdDateTime le $Lookup and userType eq 'Guest' and accountEnabled eq true &`$select=id,UserPrincipalName,signInActivity,mail,userType,accountEnabled,createdDateTime,externalUserState" -scope 'https://graph.microsoft.com/.default' -tenantid $Tenant | + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$filter=createdDateTime le $Lookup and userType eq 'Guest' and accountEnabled eq true &`$select=id,UserPrincipalName,signInActivity,mail,userType,accountEnabled,createdDateTime,externalUserState" -scope 'https://graph.microsoft.com/.default' -tenantid $Tenant | Where-Object { $_.signInActivity.lastSuccessfulSignInDateTime -le $90Days } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableGuests state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $RecentlyReactivatedUsers = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/auditLogs/directoryAudits?`$filter=activityDisplayName eq 'Enable account' and activityDateTime ge $AuditLookup" -scope 'https://graph.microsoft.com/.default' -tenantid $Tenant | ForEach-Object { $_.targetResources[0].id } | Select-Object -Unique) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 index 44bc07465732..ad06e06ce457 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 @@ -36,7 +36,15 @@ function Invoke-CIPPStandardDisableM365GroupUsers { return $true } #we're done. - $CurrentState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/settings' -tenantid $tenant) | Where-Object -Property displayname -EQ 'Group.unified' + try { + $CurrentState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/settings' -tenantid $tenant) | + Where-Object -Property displayname -EQ 'Group.unified' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableM365GroupUsers state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { if (($CurrentState.values | Where-Object { $_.name -eq 'EnableGroupCreation' }).value -eq 'false') { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableOutlookAddins.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableOutlookAddins.ps1 index 39ab24e016bf..11e3db4739cf 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableOutlookAddins.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableOutlookAddins.ps1 @@ -39,7 +39,15 @@ function Invoke-CIPPStandardDisableOutlookAddins { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableOutlookAddins' - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RoleAssignmentPolicy' | Where-Object { $_.IsDefault -eq $true } + try { + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RoleAssignmentPolicy' | + Where-Object { $_.IsDefault -eq $true } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableOutlookAddins state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $Roles = @('My Custom Apps', 'My Marketplace Apps', 'My ReadWriteMailbox Apps') $RolesToRemove = foreach ($Role in $Roles) { if ($CurrentInfo.AssignedRoles -contains $Role) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableQRCodePin.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableQRCodePin.ps1 index 358f1bfb76e5..d724a8f779c3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableQRCodePin.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableQRCodePin.ps1 @@ -29,7 +29,14 @@ function Invoke-CIPPStandardDisableQRCodePin { param($Tenant, $Settings) - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/QRCodePin' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/QRCodePin' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableQRCodePin state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'disabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 index 94504d7792a6..ffb0dba555dc 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 @@ -39,7 +39,14 @@ function Invoke-CIPPStandardDisableReshare { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableReshare state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableResourceMailbox.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableResourceMailbox.ps1 index 0635a3eeef52..4f284d19857f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableResourceMailbox.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableResourceMailbox.ps1 @@ -40,10 +40,17 @@ function Invoke-CIPPStandardDisableResourceMailbox { ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableResourceMailbox' # Get all users that are able to be - $UserList = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?$top=999&$filter=accountEnabled eq true and onPremisesSyncEnabled ne true and assignedLicenses/$count eq 0&$count=true' -Tenantid $Tenant -ComplexFilter | + try { + $UserList = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?$top=999&$filter=accountEnabled eq true and onPremisesSyncEnabled ne true and assignedLicenses/$count eq 0&$count=true' -Tenantid $Tenant -ComplexFilter | Where-Object { $_.userType -eq 'Member' } - $ResourceMailboxList = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{ Filter = "RecipientTypeDetails -eq 'RoomMailbox' -or RecipientTypeDetails -eq 'EquipmentMailbox'" } -Select 'UserPrincipalName,DisplayName,RecipientTypeDetails,ExternalDirectoryObjectId' | + $ResourceMailboxList = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{ Filter = "RecipientTypeDetails -eq 'RoomMailbox' -or RecipientTypeDetails -eq 'EquipmentMailbox'" } -Select 'UserPrincipalName,DisplayName,RecipientTypeDetails,ExternalDirectoryObjectId' | Where-Object { $_.ExternalDirectoryObjectId -in $UserList.id } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableResourceMailbox state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { Write-Host 'Time to remediate' diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 index 076ea346d5d3..3361dbde8160 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardDisableSMS { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableSMS' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/SMS' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/SMS' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableSMS state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'disabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSecurityGroupUsers.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSecurityGroupUsers.ps1 index 434754a89f43..d010fb24668d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSecurityGroupUsers.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSecurityGroupUsers.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardDisableSecurityGroupUsers { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableSecurityGroupUsers' - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableSecurityGroupUsers state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { if ($CurrentInfo.defaultUserRolePermissions.allowedToCreateSecurityGroups -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharePointLegacyAuth.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharePointLegacyAuth.ps1 index 99535b28cf89..eda6a2a4014f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharePointLegacyAuth.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharePointLegacyAuth.ps1 @@ -40,7 +40,14 @@ function Invoke-CIPPStandardDisableSharePointLegacyAuth { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings?$select=isLegacyAuthProtocolsEnabled' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings?$select=isLegacyAuthProtocolsEnabled' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableSharePointLegacyAuth state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharedMailbox.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharedMailbox.ps1 index 257229cb8aea..f9daa926db02 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharedMailbox.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharedMailbox.ps1 @@ -33,8 +33,15 @@ function Invoke-CIPPStandardDisableSharedMailbox { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableSharedMailbox' - $UserList = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?$top=999&$filter=accountEnabled eq true and onPremisesSyncEnabled ne true&$count=true' -Tenantid $Tenant -ComplexFilter - $SharedMailboxList = (New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($Tenant)/Mailbox" -Tenantid $Tenant -scope ExchangeOnline | Where-Object { $_.RecipientTypeDetails -EQ 'SharedMailbox' -or $_.RecipientTypeDetails -eq 'SchedulingMailbox' -and $_.UserPrincipalName -in $UserList.UserPrincipalName }) + try { + $UserList = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?$top=999&$filter=accountEnabled eq true and onPremisesSyncEnabled ne true&$count=true' -Tenantid $Tenant -ComplexFilter + $SharedMailboxList = (New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($Tenant)/Mailbox" -Tenantid $Tenant -scope ExchangeOnline | Where-Object { $_.RecipientTypeDetails -EQ 'SharedMailbox' -or $_.RecipientTypeDetails -eq 'SchedulingMailbox' -and $_.UserPrincipalName -in $UserList.UserPrincipalName }) + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableSharedMailbox state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTNEF.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTNEF.ps1 index f9d16a185bd2..e6bd16f501ba 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTNEF.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTNEF.ps1 @@ -36,7 +36,15 @@ function Invoke-CIPPStandardDisableTNEF { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RemoteDomain' -cmdParams @{Identity = 'Default' } + + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RemoteDomain' -cmdParams @{Identity = 'Default' } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableTNEF state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { Write-Host 'Time to remediate' diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTenantCreation.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTenantCreation.ps1 index dcdab63d0073..f65a7c8ae124 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTenantCreation.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTenantCreation.ps1 @@ -33,7 +33,14 @@ function Invoke-CIPPStandardDisableTenantCreation { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableTenantCreation' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableTenantCreation state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.defaultUserRolePermissions.allowedToCreateTenants -eq $false) If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 index d88f4cbc3fc4..74e2628e219e 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 @@ -36,7 +36,14 @@ function Invoke-CIPPStandardDisableUserSiteCreate { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableUserSiteCreate state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 index 3ad258c913e1..cad37045dada 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardDisableVoice { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableVoice' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Voice' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Voice' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableVoice state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'disabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 index 518c4ab96cd1..4367b445c686 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardDisablex509Certificate { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'Disablex509Certificate' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/x509Certificate' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/x509Certificate' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the Disablex509Certificate state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'disabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXODisableAutoForwarding.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXODisableAutoForwarding.ps1 index 9ad98d78c2cf..b98f28fe2fd3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXODisableAutoForwarding.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXODisableAutoForwarding.ps1 @@ -41,7 +41,14 @@ function Invoke-CIPPStandardEXODisableAutoForwarding { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EXODisableAutoForwarding' - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -cmdParams @{Identity = 'Default' } -useSystemMailbox $true + try { + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -cmdParams @{Identity = 'Default' } -useSystemMailbox $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EXODisableAutoForwarding state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = $CurrentInfo.AutoForwardingMode -eq 'Off' if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXOOutboundSpamLimits.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXOOutboundSpamLimits.ps1 index 10d8a82011b0..d29e2af9634f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXOOutboundSpamLimits.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXOOutboundSpamLimits.ps1 @@ -68,7 +68,15 @@ function Invoke-CIPPStandardEXOOutboundSpamLimits { } # Get current settings - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -cmdParams @{Identity = 'Default' } -Select 'RecipientLimitExternalPerHour, RecipientLimitInternalPerHour, RecipientLimitPerDay, ActionWhenThresholdReached' -useSystemMailbox $true | Select-Object -ExcludeProperty *data.type* + try { + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -cmdParams @{Identity = 'Default' } -Select 'RecipientLimitExternalPerHour, RecipientLimitInternalPerHour, RecipientLimitPerDay, ActionWhenThresholdReached' -useSystemMailbox $true | + Select-Object -ExcludeProperty *data.type* + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EXOOutboundSpamLimits state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Check if settings are already correct $StateIsCorrect = ($CurrentInfo.RecipientLimitExternalPerHour -eq $Settings.RecipientLimitExternalPerHour) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableAppConsentRequests.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableAppConsentRequests.ps1 index 4f2fe48c8f42..36ea59379a42 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableAppConsentRequests.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableAppConsentRequests.ps1 @@ -33,7 +33,14 @@ function Invoke-CIPPStandardEnableAppConsentRequests { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EnableAppConsentRequests' - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/adminConsentRequestPolicy' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/adminConsentRequestPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableAppConsentRequests state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { try { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableCustomerLockbox.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableCustomerLockbox.ps1 index ee6badad343b..aec70509a6d5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableCustomerLockbox.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableCustomerLockbox.ps1 @@ -39,7 +39,15 @@ function Invoke-CIPPStandardEnableCustomerLockbox { return $true } #we're done. - $CustomerLockboxStatus = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').CustomerLockboxEnabled + try { + $CustomerLockboxStatus = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').CustomerLockboxEnabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableCustomerLockbox state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + if ($Settings.remediate -eq $true) { Write-Host 'Time to remediate' try { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 index dc5b9e9e5da7..bb1940101a45 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardEnableFIDO2 { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EnableFIDO2' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableFIDO2 state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'enabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 index 405a2747e0bc..1cc932350136 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardEnableHardwareOAuth { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EnableHardwareOAuth' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/HardwareOath' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/HardwareOath' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableHardwareOAuth state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'enabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableLitigationHold.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableLitigationHold.ps1 index 97173ce9afc5..1e1df95f041d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableLitigationHold.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableLitigationHold.ps1 @@ -37,7 +37,15 @@ function Invoke-CIPPStandardEnableLitigationHold { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EnableLitigationHold' - $MailboxesNoLitHold = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{ Filter = 'LitigationHoldEnabled -eq "False"' } -Select 'UserPrincipalName,PersistedCapabilities,LitigationHoldEnabled' | Where-Object { $_.PersistedCapabilities -contains 'BPOS_S_DlpAddOn' -or $_.PersistedCapabilities -contains 'BPOS_S_Enterprise' } + try { + $MailboxesNoLitHold = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{ Filter = 'LitigationHoldEnabled -eq "False"' } -Select 'UserPrincipalName,PersistedCapabilities,LitigationHoldEnabled' | + Where-Object { $_.PersistedCapabilities -contains 'BPOS_S_DlpAddOn' -or $_.PersistedCapabilities -contains 'BPOS_S_Enterprise' } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableLitigationHold state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { if ($null -eq $MailboxesNoLitHold) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailTips.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailTips.ps1 index 1532e458a2ab..4e251944fc17 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailTips.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailTips.ps1 @@ -41,7 +41,14 @@ function Invoke-CIPPStandardEnableMailTips { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EnableMailTips' - $MailTipsState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig' | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold + try { + $MailTipsState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig' | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableMailTips state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = if ($MailTipsState.MailTipsAllTipsEnabled -and $MailTipsState.MailTipsExternalRecipientsTipsEnabled -and $MailTipsState.MailTipsGroupMetricsEnabled -and $MailTipsState.MailTipsLargeAudienceThreshold -eq $Settings.MailTipsLargeAudienceThreshold) { $true } else { $false } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 index 6a2397ce7139..2676d7577a04 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 @@ -40,7 +40,14 @@ function Invoke-CIPPStandardEnableMailboxAuditing { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EnableMailboxAuditing' - $AuditState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').AuditDisabled + try { + $AuditState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').AuditDisabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableMailboxAuditing state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { if ($AuditState) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 index fc1fd74618ce..fac3406a91f8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 @@ -37,7 +37,14 @@ function Invoke-CIPPStandardExcludedfileExt { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the ExcludedfileExt state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $Exts = ($Settings.ext -replace ' ', '') -split ',' # Add a wildcard to the extensions since thats what the SP admin center does $Exts = $Exts | ForEach-Object { if ($_ -notlike '*.*') { "*.$_" } else { $_ } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExternalMFATrusted.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExternalMFATrusted.ps1 index 05320c8fbb03..166709d0b71e 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExternalMFATrusted.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExternalMFATrusted.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardExternalMFATrusted { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'ExternalMFATrusted' - $ExternalMFATrusted = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/default?$select=inboundTrust' -tenantid $Tenant) + try { + $ExternalMFATrusted = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/default?$select=inboundTrust' -tenantid $Tenant) + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the ExternalMFATrusted state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Get state value using null-coalescing operator $state = $Settings.state.value ?? $Settings.state diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardFocusedInbox.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardFocusedInbox.ps1 index e45a8920d3e3..268a521c020c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardFocusedInbox.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardFocusedInbox.ps1 @@ -46,7 +46,14 @@ function Invoke-CIPPStandardFocusedInbox { Return } - $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').FocusedInboxOn + try { + $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').FocusedInboxOn + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the FocusedInbox state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $WantedState = if ($state -eq 'enabled') { $true } else { $false } $StateIsCorrect = if ($CurrentState -eq $WantedState) { $true } else { $false } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGlobalQuarantineNotifications.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGlobalQuarantineNotifications.ps1 index 138fb8a2464d..ea0284c0e2ba 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGlobalQuarantineNotifications.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGlobalQuarantineNotifications.ps1 @@ -36,7 +36,16 @@ function Invoke-CIPPStandardGlobalQuarantineNotifications { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-QuarantinePolicy' -cmdParams @{ QuarantinePolicyType = 'GlobalQuarantinePolicy' } | Select-Object -ExcludeProperty '*data.type' + + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-QuarantinePolicy' -cmdParams @{ QuarantinePolicyType = 'GlobalQuarantinePolicy' } | + Select-Object -ExcludeProperty '*data.type' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the GlobalQuarantineNotifications state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # This might take the cake on ugly hacky stuff i've done, # but i just cant understand why the API returns the values it does and not a timespan like the equivalent powershell command does diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGuestInvite.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGuestInvite.ps1 index 4cb15592d663..b767665fc283 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGuestInvite.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGuestInvite.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardGuestInvite { param($Tenant, $Settings) - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the GuestInvite state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Input validation and value handling $AllowInvitesFromValue = $Settings.allowInvitesFrom.value ?? $Settings.allowInvitesFrom diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneComplianceSettings.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneComplianceSettings.ps1 index bbdd69edd3c6..62477214d4f3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneComplianceSettings.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneComplianceSettings.ps1 @@ -37,7 +37,15 @@ function Invoke-CIPPStandardIntuneComplianceSettings { return $true } #we're done. - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/settings' -tenantid $Tenant | Select-Object secureByDefault, deviceComplianceCheckinThresholdDays + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/settings' -tenantid $Tenant | + Select-Object secureByDefault, deviceComplianceCheckinThresholdDays + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the intuneDeviceReg state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($null -eq $Settings.deviceComplianceCheckinThresholdDays) { $Settings.deviceComplianceCheckinThresholdDays = $CurrentState.deviceComplianceCheckinThresholdDays } $SecureByDefault = [bool]($Settings.secureByDefault.value ? $Settings.secureByDefault.value : $Settings.secureByDefault) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMDMScope.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMDMScope.ps1 index ab52addfe26d..193ab399da83 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMDMScope.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMDMScope.ps1 @@ -37,7 +37,14 @@ function Invoke-CIPPStandardMDMScope { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/mobileDeviceManagementPolicies/0000000a-0000-0000-c000-000000000000?$expand=includedGroups' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/mobileDeviceManagementPolicies/0000000a-0000-0000-c000-000000000000?$expand=includedGroups' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the MDM Scope state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentInfo.termsOfUseUrl -eq 'https://portal.manage.microsoft.com/TermsofUse.aspx') -and ($CurrentInfo.discoveryUrl -eq 'https://enrollment.manage.microsoft.com/enrollmentserver/discovery.svc') -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 index 55655ccc1d8f..1cd033c26a67 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 @@ -34,8 +34,15 @@ function Invoke-CIPPStandardMailContacts { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'MailContacts' - $TenantID = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/organization' -tenantid $tenant) - $CurrentInfo = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/organization/$($TenantID.id)" -tenantid $Tenant + try { + $TenantID = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/organization' -tenantid $tenant) + $CurrentInfo = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/organization/$($TenantID.id)" -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the MailContacts state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $contacts = $settings $TechAndSecurityContacts = @($Contacts.SecurityContact, $Contacts.TechContact) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailboxRecipientLimits.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailboxRecipientLimits.ps1 index 0225f6423bfe..cf2459c7071c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailboxRecipientLimits.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailboxRecipientLimits.ps1 @@ -44,7 +44,14 @@ function Invoke-CIPPStandardMailboxRecipientLimits { } # Get mailbox plans first - $MailboxPlans = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxPlan' -cmdParams @{ ResultSize = 'Unlimited' } + try { + $MailboxPlans = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxPlan' -cmdParams @{ ResultSize = 'Unlimited' } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the MailboxRecipientLimits state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Create a hashtable of mailbox plans for quick lookup $MailboxPlanLookup = @{} diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMalwareFilterPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMalwareFilterPolicy.ps1 index 632ae123f34e..dfc4135d4fa8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMalwareFilterPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMalwareFilterPolicy.ps1 @@ -63,9 +63,16 @@ function Invoke-CIPPStandardMalwareFilterPolicy { $RuleName = $ExistingRule.Name } - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MalwareFilterPolicy' | - Where-Object -Property Name -EQ $PolicyName | - Select-Object Name, EnableFileFilter, FileTypeAction, FileTypes, ZapEnabled, QuarantineTag, EnableInternalSenderAdminNotifications, InternalSenderAdminAddress, EnableExternalSenderAdminNotifications, ExternalSenderAdminAddress + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MalwareFilterPolicy' | + Where-Object -Property Name -EQ $PolicyName | + Select-Object Name, EnableFileFilter, FileTypeAction, FileTypes, ZapEnabled, QuarantineTag, EnableInternalSenderAdminNotifications, InternalSenderAdminAddress, EnableExternalSenderAdminNotifications, ExternalSenderAdminAddress + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the MalwareFilterPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $DefaultFileTypes = @('ace', 'ani', 'apk', 'app', 'appx', 'arj', 'bat', 'cab', 'cmd', 'com', 'deb', 'dex', 'dll', 'docm', 'elf', 'exe', 'hta', 'img', 'iso', 'jar', 'jnlp', 'kext', 'lha', 'lib', 'library', 'lnk', 'lzh', 'macho', 'msc', 'msi', 'msix', 'msp', 'mst', 'pif', 'ppa', 'ppam', 'reg', 'rev', 'scf', 'scr', 'sct', 'sys', 'uif', 'vb', 'vbe', 'vbs', 'vxd', 'wsc', 'wsf', 'wsh', 'xll', 'xz', 'z') diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMessageExpiration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMessageExpiration.ps1 index fcbf37358bd4..af905f43d0a3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMessageExpiration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMessageExpiration.ps1 @@ -36,7 +36,14 @@ function Invoke-CIPPStandardMessageExpiration { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'MessageExpiration' - $MessageExpiration = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-TransportConfig').messageExpiration + try { + $MessageExpiration = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-TransportConfig').messageExpiration + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the MessageExpiration state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { Write-Host 'Time to remediate' diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsent.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsent.ps1 index 9d00b6a527e7..d18f6fb68a3f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsent.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsent.ps1 @@ -33,7 +33,14 @@ function Invoke-CIPPStandardOauthConsent { param($tenant, $settings) - $State = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $tenant + try { + $State = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the OauthConsent state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = if ($State.permissionGrantPolicyIdsAssignedToDefaultUserRole -eq 'managePermissionGrantsForSelf.cipp-consent-policy') { $true } else { $false } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsentLowSec.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsentLowSec.ps1 index 8338bcf4b108..22c465de76a7 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsentLowSec.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsentLowSec.ps1 @@ -29,8 +29,17 @@ function Invoke-CIPPStandardOauthConsentLowSec { param($Tenant, $Settings) - $State = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $tenant) - $PermissionState = (New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/servicePrincipals(appId='00000003-0000-0000-c000-000000000000')/delegatedPermissionClassifications" -tenantid $tenant) | Select-Object -Property permissionName + try { + $State = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $tenant) + + $PermissionState = (New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/servicePrincipals(appId='00000003-0000-0000-c000-000000000000')/delegatedPermissionClassifications" -tenantid $tenant) | + Select-Object -Property permissionName + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the OauthConsentLowSec state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $requiredPermissions = @('offline_access', 'openid', 'User.Read', 'profile', 'email') $missingPermissions = $requiredPermissions | Where-Object { $PermissionState.permissionName -notcontains $_ } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOutBoundSpamAlert.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOutBoundSpamAlert.ps1 index 5216f08af174..51049bddc07d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOutBoundSpamAlert.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOutBoundSpamAlert.ps1 @@ -38,7 +38,14 @@ function Invoke-CIPPStandardOutBoundSpamAlert { return $true } #we're done. - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -cmdParams @{ Identity = 'Default' } -useSystemMailbox $true + try { + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -cmdParams @{ Identity = 'Default' } -useSystemMailbox $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the OutBoundSpamAlert state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWcompanionAppAllowedState.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWcompanionAppAllowedState.ps1 index a52afe311852..7497a5e5e797 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWcompanionAppAllowedState.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWcompanionAppAllowedState.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardPWcompanionAppAllowedState { param($Tenant, $Settings) - $AuthenticatorFeaturesState = (New-GraphGetRequest -tenantid $Tenant -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator') + try { + $AuthenticatorFeaturesState = (New-GraphGetRequest -tenantid $Tenant -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator') + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the PWcompanionAppAllowedState state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Get state value using null-coalescing operator $CurrentState = $AuthenticatorFeaturesState.featureSettings.companionAppAllowedState.state diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 index 7dacd622acbd..f8616c61c62f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 @@ -31,7 +31,15 @@ function Invoke-CIPPStandardPWdisplayAppInformationRequiredState { param($Tenant, $Settings) - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the PWdisplayAppInformationRequiredState state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $StateIsCorrect = ($CurrentState.state -eq 'enabled') -and ($CurrentState.featureSettings.numberMatchingRequiredState.state -eq 'enabled') -and ($CurrentState.featureSettings.displayAppInformationRequiredState.state -eq 'enabled') diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 index a801f3a32244..a540deb8c0cc 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 @@ -33,7 +33,15 @@ function Invoke-CIPPStandardPasswordExpireDisabled { param($Tenant, $Settings) - $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant + try { + $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the PasswordExpireDisabled state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $DomainsWithoutPassExpire = $GraphRequest | Where-Object { $_.isVerified -eq $true -and $_.passwordValidityPeriodInDays -ne 2147483647 } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 index a9cb8cf01c4e..2dc4d907d921 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 @@ -29,7 +29,14 @@ function Invoke-CIPPStandardPerUserMFA { param($Tenant, $Settings) - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=userPrincipalName,displayName,accountEnabled,perUserMfaState&`$filter=userType eq 'Member' and accountEnabled eq true and displayName ne 'On-Premises Directory Synchronization Service Account'&`$count=true" -tenantid $Tenant -ComplexFilter + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=userPrincipalName,displayName,accountEnabled,perUserMfaState&`$filter=userType eq 'Member' and accountEnabled eq true and displayName ne 'On-Premises Directory Synchronization Service Account'&`$count=true" -tenantid $Tenant -ComplexFilter + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the PerUserMFA state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $UsersWithoutMFA = $GraphRequest | Where-Object -Property perUserMfaState -NE 'enforced' | Select-Object -Property userPrincipalName, displayName, accountEnabled, perUserMfaState If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishSimSpoofIntelligence.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishSimSpoofIntelligence.ps1 index d385120c7449..1cf8c915b826 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishSimSpoofIntelligence.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishSimSpoofIntelligence.ps1 @@ -37,8 +37,15 @@ function Invoke-CIPPStandardPhishSimSpoofIntelligence { return $true } #we're done. # Fetch current Phishing Simulations Spoof Intelligence domains and ensure it is correctly configured - $DomainState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-TenantAllowBlockListSpoofItems' | - Select-Object -Property Identity,SendingInfrastructure + try { + $DomainState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-TenantAllowBlockListSpoofItems' | + Select-Object -Property Identity, SendingInfrastructure + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the PhishSimSpoofIntelligence state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } [String[]]$AddDomain = $Settings.AllowedDomains.value | Where-Object { $_ -notin $DomainState.SendingInfrastructure } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 index 6b4b4005846f..0fa3af87bf61 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 @@ -41,9 +41,16 @@ function Invoke-CIPPStandardPhishingSimulations { $PolicyName = 'CIPPPhishSim' # Fetch current Phishing Simulations Policy settings and ensure it is correctly configured - $PolicyState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-PhishSimOverridePolicy' | - Where-Object -Property Name -EQ 'PhishSimOverridePolicy' | - Select-Object -Property Identity,Name,Mode,Enabled + try { + $PolicyState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-PhishSimOverridePolicy' | + Where-Object -Property Name -EQ 'PhishSimOverridePolicy' | + Select-Object -Property Identity, Name, Mode, Enabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the PhishingSimulations state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $PolicyIsCorrect = ($PolicyState.Name -eq 'PhishSimOverridePolicy') -and ($PolicyState.Enabled -eq $true) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardProfilePhotos.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardProfilePhotos.ps1 index 58ced8cd5211..979f690a1222 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardProfilePhotos.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardProfilePhotos.ps1 @@ -49,8 +49,15 @@ function Invoke-CIPPStandardProfilePhotos { $DesiredState = $StateValue -eq 'enabled' # Get current Graph policy state - $Uri = 'https://graph.microsoft.com/beta/admin/people/photoUpdateSettings' - $CurrentGraphState = New-GraphGetRequest -uri $Uri -tenantid $Tenant + try { + $Uri = 'https://graph.microsoft.com/beta/admin/people/photoUpdateSettings' + $CurrentGraphState = New-GraphGetRequest -uri $Uri -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the ProfilePhotos state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $UsersCanChangePhotos = if ([string]::IsNullOrWhiteSpace($CurrentGraphState.allowedRoles) ) { $true } else { $false } $GraphStateCorrect = $UsersCanChangePhotos -eq $DesiredState diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardQuarantineRequestAlert.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardQuarantineRequestAlert.ps1 index 9fd1b7426f98..9a61e0478eb2 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardQuarantineRequestAlert.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardQuarantineRequestAlert.ps1 @@ -37,9 +37,16 @@ function Invoke-CIPPStandardQuarantineRequestAlert { } #we're done. $PolicyName = 'CIPP User requested to release a quarantined message' - $CurrentState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-ProtectionAlert' -Compliance | - Where-Object { $_.Name -eq $PolicyName } | - Select-Object -Property * + try { + $CurrentState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-ProtectionAlert' -Compliance | + Where-Object { $_.Name -eq $PolicyName } | + Select-Object -Property * + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the QuarantineRequestAlert state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.NotifyUser -contains $Settings.NotifyUser) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRetentionPolicyTag.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRetentionPolicyTag.ps1 index cc96f1cb4053..a1135e0dc558 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRetentionPolicyTag.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRetentionPolicyTag.ps1 @@ -37,11 +37,19 @@ function Invoke-CIPPStandardRetentionPolicyTag { } #we're done. $PolicyName = 'CIPP Deleted Items' - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RetentionPolicyTag' | - Where-Object -Property Identity -EQ $PolicyName - $PolicyState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RetentionPolicy' | - Where-Object -Property Identity -EQ 'Default MRM Policy' + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RetentionPolicyTag' | + Where-Object -Property Identity -EQ $PolicyName + + $PolicyState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RetentionPolicy' | + Where-Object -Property Identity -EQ 'Default MRM Policy' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the RetentionPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and ($CurrentState.RetentionEnabled -eq $true) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRotateDKIM.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRotateDKIM.ps1 index d99d8e2800e3..4756d667c395 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRotateDKIM.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRotateDKIM.ps1 @@ -38,7 +38,14 @@ function Invoke-CIPPStandardRotateDKIM { return $true } #we're done. - $DKIM = (New-ExoRequest -tenantid $tenant -cmdlet 'Get-DkimSigningConfig') | Where-Object { $_.Selector1KeySize -eq 1024 -and $_.Enabled -eq $true } + try { + $DKIM = (New-ExoRequest -tenantid $tenant -cmdlet 'Get-DkimSigningConfig') | Where-Object { $_.Selector1KeySize -eq 1024 -and $_.Enabled -eq $true } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DKIM state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPAzureB2B.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPAzureB2B.ps1 index b27311d068a9..7112e90148c1 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPAzureB2B.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPAzureB2B.ps1 @@ -37,8 +37,15 @@ function Invoke-CIPPStandardSPAzureB2B { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object -Property _ObjectIdentity_, TenantFilter, EnableAzureADB2BIntegration + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPAzureB2B state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.EnableAzureADB2BIntegration -eq $true) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDirectSharing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDirectSharing.ps1 index 08e3e1ca1c81..b30e945af564 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDirectSharing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDirectSharing.ps1 @@ -40,8 +40,15 @@ function Invoke-CIPPStandardSPDirectSharing { Write-LogMessage -API 'Standards' -Tenant $Tenant -Message 'The default sharing to Direct users standard has been deprecated in favor of the "Set Default Sharing Link Settings" standard. Please update your standards to use new standard. However this will continue to function.' -Sev Alert - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | - Select-Object -Property _ObjectIdentity_, TenantFilter, DefaultSharingLinkType + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + Select-Object -Property _ObjectIdentity_, TenantFilter, DefaultSharingLinkType + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPDirectSharing state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.DefaultSharingLinkType -eq 'Direct' -or $CurrentState.DefaultSharingLinkType -eq 1) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisableLegacyWorkflows.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisableLegacyWorkflows.ps1 index ae96250dba3e..59d08d079528 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisableLegacyWorkflows.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisableLegacyWorkflows.ps1 @@ -34,8 +34,15 @@ function Invoke-CIPPStandardSPDisableLegacyWorkflows { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object -Property * + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPDisableLegacyWorkflows state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.StopNew2010Workflows -eq $true) -and ($CurrentState.StopNew2013Workflows -eq $true) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisallowInfectedFiles.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisallowInfectedFiles.ps1 index 812f35819c74..f1883357e1c3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisallowInfectedFiles.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisallowInfectedFiles.ps1 @@ -38,8 +38,15 @@ function Invoke-CIPPStandardSPDisallowInfectedFiles { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object -Property _ObjectIdentity_, TenantFilter, DisallowInfectedFileDownload + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPDisallowInfectedFiles state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.DisallowInfectedFileDownload -eq $true) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPEmailAttestation.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPEmailAttestation.ps1 index 1445b22f2723..b76fb5939521 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPEmailAttestation.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPEmailAttestation.ps1 @@ -39,7 +39,15 @@ function Invoke-CIPPStandardSPEmailAttestation { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object -Property _ObjectIdentity_, TenantFilter, EmailAttestationReAuthDays, EmailAttestationRequired + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + Select-Object -Property _ObjectIdentity_, TenantFilter, EmailAttestationReAuthDays, EmailAttestationRequired + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPEmailAttestation state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.EmailAttestationReAuthDays -eq [int]$Settings.Days) -and ($CurrentState.EmailAttestationRequired -eq $true) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPExternalUserExpiration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPExternalUserExpiration.ps1 index 27d90d437ef3..99f563cab686 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPExternalUserExpiration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPExternalUserExpiration.ps1 @@ -38,8 +38,15 @@ function Invoke-CIPPStandardSPExternalUserExpiration { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object -Property _ObjectIdentity_, TenantFilter, ExternalUserExpireInDays, ExternalUserExpirationRequired + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPExternalUserExpiration state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.ExternalUserExpireInDays -eq $Settings.Days) -and ($CurrentState.ExternalUserExpirationRequired -eq $true) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPSyncButtonState.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPSyncButtonState.ps1 index f8a10d66b685..279deaac3ba5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPSyncButtonState.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPSyncButtonState.ps1 @@ -36,7 +36,14 @@ function Invoke-CIPPStandardSPSyncButtonState { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object _ObjectIdentity_, TenantFilter, HideSyncButtonOnDocLib + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object _ObjectIdentity_, TenantFilter, HideSyncButtonOnDocLib + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPSyncButtonState state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Input validation diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 index 0909e492eae6..f3f0dc8c5ce5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 @@ -64,9 +64,16 @@ function Invoke-CIPPStandardSafeAttachmentPolicy { $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 + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeAttachmentPolicy' | + Where-Object -Property Name -EQ $PolicyName | + Select-Object Name, Enable, Action, QuarantineTag, Redirect, RedirectAddress + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SafeAttachmentPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and ($CurrentState.Enable -eq $true) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 index 28fab38d23fe..6ae32ddd6a39 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 @@ -63,9 +63,16 @@ function Invoke-CIPPStandardSafeLinksPolicy { $RuleName = $ExistingRule.Name } - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeLinksPolicy' | + try { + $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, DoNotRewriteUrls + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SafeLinksPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and ($CurrentState.EnableSafeLinksForEmail -eq $true) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSecurityDefaults.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSecurityDefaults.ps1 index a8f7552c87c5..5a9133fa35b5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSecurityDefaults.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSecurityDefaults.ps1 @@ -29,7 +29,14 @@ function Invoke-CIPPStandardSecurityDefaults { param($Tenant, $Settings) - $SecureDefaultsState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -tenantid $tenant) + try { + $SecureDefaultsState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -tenantid $tenant) + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the Security Defaults state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendFromAlias.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendFromAlias.ps1 index 4b8e561fb1a1..28692ae363c5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendFromAlias.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendFromAlias.ps1 @@ -36,7 +36,14 @@ function Invoke-CIPPStandardSendFromAlias { return $true } #we're done. - $CurrentInfo = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').SendFromAliasEnabled + try { + $CurrentInfo = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').SendFromAliasEnabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SendFromAlias state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { if ($CurrentInfo -ne $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendReceiveLimitTenant.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendReceiveLimitTenant.ps1 index 2ee64931533e..958e822a9c9d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendReceiveLimitTenant.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendReceiveLimitTenant.ps1 @@ -49,8 +49,16 @@ function Invoke-CIPPStandardSendReceiveLimitTenant { return } + try { + $AllMailBoxPlans = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxPlan' | + Select-Object DisplayName, MaxSendSize, MaxReceiveSize, GUID + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SendReceiveLimitTenant state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } - $AllMailBoxPlans = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxPlan' | Select-Object DisplayName, MaxSendSize, MaxReceiveSize, GUID $MaxSendSize = [int64]"$($Settings.SendLimit)MB" $MaxReceiveSize = [int64]"$($Settings.ReceiveLimit)MB" diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSharePointMassDeletionAlert.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSharePointMassDeletionAlert.ps1 index b0193ee91b5c..c210a783def6 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSharePointMassDeletionAlert.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSharePointMassDeletionAlert.ps1 @@ -40,9 +40,16 @@ function Invoke-CIPPStandardSharePointMassDeletionAlert { $PolicyName = 'CIPP SharePoint mass deletion of files by a user' - $CurrentState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-ProtectionAlert' -Compliance | - Where-Object { $_.Name -eq $PolicyName } | - Select-Object -Property * + try { + $CurrentState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-ProtectionAlert' -Compliance | + Where-Object { $_.Name -eq $PolicyName } | + Select-Object -Property * + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the sharingCapability state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $EmailsOutsideSettings = $CurrentState.NotifyUser | Where-Object { $_ -notin $Settings.NotifyUser.value } $MissingEmailsInSettings = $Settings.NotifyUser.value | Where-Object { $_ -notin $CurrentState.NotifyUser } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardShortenMeetings.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardShortenMeetings.ps1 index 28d40a8e3881..2d567060ab95 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardShortenMeetings.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardShortenMeetings.ps1 @@ -41,7 +41,16 @@ function Invoke-CIPPStandardShortenMeetings { # Get state value using null-coalescing operator $scopeDefault = $Settings.ShortenEventScopeDefault.value ? $Settings.ShortenEventScopeDefault.value : $Settings.ShortenEventScopeDefault - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig' | Select-Object -Property ShortenEventScopeDefault, DefaultMinutesToReduceShortEventsBy, DefaultMinutesToReduceLongEventsBy + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig' | + Select-Object -Property ShortenEventScopeDefault, DefaultMinutesToReduceShortEventsBy, DefaultMinutesToReduceLongEventsBy + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the ShortenMeetings state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $CorrectState = if ($CurrentState.ShortenEventScopeDefault -eq $scopeDefault -and $CurrentState.DefaultMinutesToReduceShortEventsBy -eq $Settings.DefaultMinutesToReduceShortEventsBy -and $CurrentState.DefaultMinutesToReduceLongEventsBy -eq $Settings.DefaultMinutesToReduceLongEventsBy) { $true } else { $false } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpamFilterPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpamFilterPolicy.ps1 index 485c1b7fb475..3b811a9a712e 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpamFilterPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpamFilterPolicy.ps1 @@ -60,7 +60,16 @@ function Invoke-CIPPStandardSpamFilterPolicy { $PolicyName = 'CIPP Default Spam Filter Policy' - $CurrentState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-HostedContentFilterPolicy' | Where-Object -Property Name -EQ $PolicyName | Select-Object -Property * + try { + $CurrentState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-HostedContentFilterPolicy' | + Where-Object -Property Name -EQ $PolicyName | + Select-Object -Property * + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SpamFilterPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $SpamAction = $Settings.SpamAction.value ?? $Settings.SpamAction $SpamQuarantineTag = $Settings.SpamQuarantineTag.value ?? $Settings.SpamQuarantineTag diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 index 86aee02d78de..15a49616c884 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 @@ -40,7 +40,14 @@ function Invoke-CIPPStandardSpoofWarn { return $true } #we're done. - $CurrentInfo = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ExternalInOutlook') + try { + $CurrentInfo = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ExternalInOutlook') + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SpoofWarn state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Get state value using null-coalescing operator $state = $Settings.state.value ?? $Settings.state diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardStaleEntraDevices.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardStaleEntraDevices.ps1 index 6b648dc70556..ddc412de1fb9 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardStaleEntraDevices.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardStaleEntraDevices.ps1 @@ -40,7 +40,16 @@ function Invoke-CIPPStandardStaleEntraDevices { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $AllDevices = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/devices' -tenantid $Tenant | Where-Object { $null -ne $_.approximateLastSignInDateTime } + + try { + $AllDevices = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/devices' -tenantid $Tenant | Where-Object { $null -ne $_.approximateLastSignInDateTime } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the StaleEntraDevices state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $Date = (Get-Date).AddDays( - [int]$Settings.deviceAgeThreshold) $StaleDevices = $AllDevices | Where-Object { $_.approximateLastSignInDateTime -lt $Date } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 index 22343488189b..734edf0f0ca8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 @@ -32,7 +32,14 @@ function Invoke-CIPPStandardTAP { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'TAP' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TAP state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Get config value using null-coalescing operator $config = $Settings.config.value ?? $Settings.config diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEmailIntegration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEmailIntegration.ps1 index 9fdb94e267f6..8a4e6e7e6dfe 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEmailIntegration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEmailIntegration.ps1 @@ -38,11 +38,19 @@ Function Invoke-CIPPStandardTeamsEmailIntegration { return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' -CmdParams @{Identity = 'Global' } | Select-Object AllowEmailIntoChannel + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' -CmdParams @{Identity = 'Global' } | + Select-Object AllowEmailIntoChannel + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsEmailIntegration state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } - if ($null -eq $Settings.AllowEmailIntoChannel) { $Settings.AllowEmailIntoChannel = $false } + $AllowEmailIntoChannel = $Settings.AllowEmailIntoChannel ?? $false - $StateIsCorrect = ($CurrentState.AllowEmailIntoChannel -eq $Settings.AllowEmailIntoChannel) + $StateIsCorrect = ($CurrentState.AllowEmailIntoChannel -eq $AllowEmailIntoChannel) if ($Settings.remediate -eq $true) { if ($StateIsCorrect -eq $true) { @@ -50,7 +58,7 @@ Function Invoke-CIPPStandardTeamsEmailIntegration { } else { $cmdParams = @{ Identity = 'Global' - AllowEmailIntoChannel = $Settings.AllowEmailIntoChannel + AllowEmailIntoChannel = $AllowEmailIntoChannel } try { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEnrollUser.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEnrollUser.ps1 index b4c5789fdd2f..341f97464d25 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEnrollUser.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEnrollUser.ps1 @@ -39,7 +39,15 @@ function Invoke-CIPPStandardTeamsEnrollUser { } #we're done. $enrollUserOverride = $Settings.EnrollUserOverride.value ?? $Settings.EnrollUserOverride - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -cmdParams @{Identity = 'Global' } | Select-Object EnrollUserOverride + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -cmdParams @{Identity = 'Global' } | + Select-Object EnrollUserOverride + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsEnrollUser state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.EnrollUserOverride -eq $enrollUserOverride) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalAccessPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalAccessPolicy.ps1 index 905d098d093e..b9a4c91bbca7 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalAccessPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalAccessPolicy.ps1 @@ -38,7 +38,15 @@ function Invoke-CIPPStandardTeamsExternalAccessPolicy { return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsExternalAccessPolicy' -CmdParams @{Identity = 'Global' } | Select-Object * + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsExternalAccessPolicy' -CmdParams @{Identity = 'Global' } | + Select-Object * + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsExternalAccessPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $EnableFederationAccess = $Settings.EnableFederationAccess ?? $false $EnableTeamsConsumerAccess = $Settings.EnableTeamsConsumerAccess ?? $false diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalFileSharing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalFileSharing.ps1 index 69c7d08f55c2..5beea221c5dc 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalFileSharing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalFileSharing.ps1 @@ -41,8 +41,16 @@ function Invoke-CIPPStandardTeamsExternalFileSharing { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - Write-Host "TeamsExternalFileSharing: $($Settings | ConvertTo-Json)" - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' | Select-Object AllowGoogleDrive, AllowShareFile, AllowBox, AllowDropBox, AllowEgnyte + + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' | + Select-Object AllowGoogleDrive, AllowShareFile, AllowBox, AllowDropBox, AllowEgnyte + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsExternalFileSharing state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.AllowGoogleDrive -eq $Settings.AllowGoogleDrive ?? $false ) -and ($CurrentState.AllowShareFile -eq $Settings.AllowShareFile ?? $false ) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsFederationConfiguration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsFederationConfiguration.ps1 index 7fcd2d8020b3..30128852abad 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsFederationConfiguration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsFederationConfiguration.ps1 @@ -39,7 +39,15 @@ function Invoke-CIPPStandardTeamsFederationConfiguration { return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTenantFederationConfiguration' -CmdParams @{Identity = 'Global' } | Select-Object * + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTenantFederationConfiguration' -CmdParams @{Identity = 'Global' } | + Select-Object * + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsFederationConfiguration state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $AllowAllKnownDomains = New-CsEdgeAllowAllKnownDomains $DomainControl = $Settings.DomainControl.value ?? $Settings.DomainControl diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGlobalMeetingPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGlobalMeetingPolicy.ps1 index 3c1780fa0dc0..ee30521dafb3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGlobalMeetingPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGlobalMeetingPolicy.ps1 @@ -40,7 +40,16 @@ function Invoke-CIPPStandardTeamsGlobalMeetingPolicy { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' } | Select-Object AllowAnonymousUsersToJoinMeeting, AllowAnonymousUsersToStartMeeting, AutoAdmittedUsers, AllowPSTNUsersToBypassLobby, MeetingChatEnabledType, DesignatedPresenterRoleMode, AllowExternalParticipantGiveRequestControl + + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' } | + Select-Object AllowAnonymousUsersToJoinMeeting, AllowAnonymousUsersToStartMeeting, AutoAdmittedUsers, AllowPSTNUsersToBypassLobby, MeetingChatEnabledType, DesignatedPresenterRoleMode, AllowExternalParticipantGiveRequestControl + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsGlobalMeetingPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $MeetingChatEnabledType = $Settings.MeetingChatEnabledType.value ?? $Settings.MeetingChatEnabledType $DesignatedPresenterRoleMode = $Settings.DesignatedPresenterRoleMode.value ?? $Settings.DesignatedPresenterRoleMode diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGuestAccess.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGuestAccess.ps1 index c4d50f9b9fe4..26fb5519707d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGuestAccess.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGuestAccess.ps1 @@ -36,11 +36,19 @@ function Invoke-CIPPStandardTeamsGuestAccess { return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' -CmdParams @{Identity = 'Global' } | Select-Object AllowGuestUser + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' -CmdParams @{Identity = 'Global' } | + Select-Object AllowGuestUser + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsGuestAccess state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } - if ($null -eq $Settings.AllowGuestUser) { $Settings.AllowGuestUser = $false } + $AllowGuestUser = $Settings.AllowGuestUser ?? $false - $StateIsCorrect = ($CurrentState.AllowGuestUser -eq $Settings.AllowGuestUser) + $StateIsCorrect = ($CurrentState.AllowGuestUser -eq $AllowGuestUser) if ($Settings.remediate -eq $true) { if ($StateIsCorrect -eq $true) { @@ -48,7 +56,7 @@ function Invoke-CIPPStandardTeamsGuestAccess { } else { $cmdParams = @{ Identity = 'Global' - AllowGuestUser = $Settings.AllowGuestUser + AllowGuestUser = $AllowGuestUser } try { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingRecordingExpiration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingRecordingExpiration.ps1 index 28b88c3bcff5..6c1584fb4ecf 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingRecordingExpiration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingRecordingExpiration.ps1 @@ -44,7 +44,15 @@ function Invoke-CIPPStandardTeamsMeetingRecordingExpiration { return } - $CurrentExpirationDays = (New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' }).NewMeetingRecordingExpirationDays + try { + $CurrentExpirationDays = (New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' }).NewMeetingRecordingExpirationDays + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsMeetingRecordingExpiration state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $StateIsCorrect = if ($CurrentExpirationDays -eq $ExpirationDays) { $true } else { $false } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingVerification.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingVerification.ps1 index c051ad79e9ba..95ef3efd57a3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingVerification.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingVerification.ps1 @@ -39,7 +39,17 @@ function Invoke-CIPPStandardTeamsMeetingVerification { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' } | Select-Object CaptchaVerificationForMeetingJoin + + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' } | + Select-Object CaptchaVerificationForMeetingJoin + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsMeetingVerification state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $CaptchaVerificationForMeetingJoin = $Settings.CaptchaVerificationForMeetingJoin.value ?? $Settings.CaptchaVerificationForMeetingJoin $StateIsCorrect = ($CurrentState.CaptchaVerificationForMeetingJoin -eq $CaptchaVerificationForMeetingJoin) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingsByDefault.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingsByDefault.ps1 index 9cb67826b0a0..bcb2881ab356 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingsByDefault.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingsByDefault.ps1 @@ -40,7 +40,15 @@ function Invoke-CIPPStandardTeamsMeetingsByDefault { # Get state value using null-coalescing operator $state = $Settings.state.value ?? $Settings.state - $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').OnlineMeetingsByDefaultEnabled + try { + $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').OnlineMeetingsByDefaultEnabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsMeetingsByDefault state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $WantedState = if ($state -eq 'true') { $true } else { $false } $StateIsCorrect = if ($CurrentState -eq $WantedState) { $true } else { $false } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMessagingPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMessagingPolicy.ps1 index c2775c1542c3..c2d5a4702922 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMessagingPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMessagingPolicy.ps1 @@ -44,7 +44,15 @@ Function Invoke-CIPPStandardTeamsMessagingPolicy { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMessagingPolicy' -CmdParams @{Identity = 'Global' } + + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMessagingPolicy' -CmdParams @{Identity = 'Global' } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsMessagingPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($null -eq $Settings.AllowOwnerDeleteMessage) { $Settings.AllowOwnerDeleteMessage = $CurrentState.AllowOwnerDeleteMessage } if ($null -eq $Settings.AllowUserDeleteMessage) { $Settings.AllowUserDeleteMessage = $CurrentState.AllowUserDeleteMessage } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTenantDefaultTimezone.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTenantDefaultTimezone.ps1 index 64c062713b66..aefe21354eb0 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTenantDefaultTimezone.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTenantDefaultTimezone.ps1 @@ -37,7 +37,15 @@ function Invoke-CIPPStandardTenantDefaultTimezone { return $true } #we're done. - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TenantDefaultTimezone state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $ExpectedTimezone = $Settings.Timezone.value $StateIsCorrect = $CurrentState.tenantDefaultTimezone -eq $ExpectedTimezone diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUndoOauth.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUndoOauth.ps1 index fd43f8ced664..166321bccad1 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUndoOauth.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUndoOauth.ps1 @@ -30,7 +30,15 @@ function Invoke-CIPPStandardUndoOauth { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'UndoOauth' - $CurrentState = New-GraphGetRequest -tenantid $Tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy?$select=permissionGrantPolicyIdsAssignedToDefaultUserRole' + try { + $CurrentState = New-GraphGetRequest -tenantid $Tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy?$select=permissionGrantPolicyIdsAssignedToDefaultUserRole' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the App Consent state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $StateIsCorrect = ($CurrentState.permissionGrantPolicyIdsAssignedToDefaultUserRole -eq 'ManagePermissionGrantsForSelf.microsoft-user-default-legacy') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserPreferredLanguage.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserPreferredLanguage.ps1 index e2b65e9c9bba..f203d1a427d1 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserPreferredLanguage.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserPreferredLanguage.ps1 @@ -31,7 +31,15 @@ function Invoke-CIPPStandardUserPreferredLanguage { param($Tenant, $Settings) $preferredLanguage = $Settings.preferredLanguage.value - $IncorrectUsers = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=userPrincipalName,displayName,preferredLanguage,userType,onPremisesSyncEnabled&`$filter=preferredLanguage ne '$preferredLanguage' and userType eq 'Member' and onPremisesSyncEnabled ne true&`$count=true" -tenantid $Tenant -ComplexFilter + + try { + $IncorrectUsers = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=userPrincipalName,displayName,preferredLanguage,userType,onPremisesSyncEnabled&`$filter=preferredLanguage ne '$preferredLanguage' and userType eq 'Member' and onPremisesSyncEnabled ne true&`$count=true" -tenantid $Tenant -ComplexFilter + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the UserPreferredLanguage state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { if (($IncorrectUsers | Measure-Object).Count -gt 0) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserSubmissions.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserSubmissions.ps1 index 3c5cf3e8a606..66833d43dd9c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserSubmissions.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserSubmissions.ps1 @@ -57,8 +57,14 @@ function Invoke-CIPPStandardUserSubmissions { } } - $PolicyState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ReportSubmissionPolicy' - $RuleState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ReportSubmissionRule' + try { + $PolicyState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ReportSubmissionPolicy' + $RuleState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ReportSubmissionRule' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the UserSubmissions state for $Tenant. Error: $ErrorMessage" -Sev Error + } if ($state -eq 'enable') { if (([string]::IsNullOrWhiteSpace($Email))) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 index 500c0af7ec87..c6103ab615bd 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardallowOAuthTokens { param($Tenant, $Settings) #$Rerun -Type Standard -Tenant $Tenant -API 'AddDKIM' -Settings $Settings - $CurrentState = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the allowOTPTokens state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'enabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 index ce374e86fd14..8aaa1977d17b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardallowOTPTokens { param($Tenant, $Settings) #$Rerun -Type Standard -Tenant $Tenant -API 'allowOTPTokens' -Settings $Settings - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the allowOTPTokens state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { if ($CurrentInfo.isSoftwareOathEnabled) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 index 828e956e49ff..baeaed8e7d18 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 @@ -36,7 +36,14 @@ function Invoke-CIPPStandarddisableMacSync { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableMacSync state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneBrandingProfile.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneBrandingProfile.ps1 index 4d4712823972..b997164726d3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneBrandingProfile.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneBrandingProfile.ps1 @@ -46,7 +46,14 @@ function Invoke-CIPPStandardintuneBrandingProfile { return $true } #we're done. - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/intuneBrandingProfiles/c3a59481-1bf2-46ce-94b3-66eec07a8d60' -tenantid $Tenant -AsApp $true + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/intuneBrandingProfiles/c3a59481-1bf2-46ce-94b3-66eec07a8d60' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the intuneBrandingProfile state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ((-not $Settings.displayName) -or ($CurrentState.displayName -eq $Settings.displayName)) -and ((-not $Settings.showLogo) -or ($CurrentState.showLogo -eq $Settings.showLogo)) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceReg.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceReg.ps1 index 5dc65b27bc64..58eac0249a95 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceReg.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceReg.ps1 @@ -37,7 +37,14 @@ function Invoke-CIPPStandardintuneDeviceReg { return $true } #we're done. - $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant + try { + $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the intuneDeviceReg state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = if ($PreviousSetting.userDeviceQuota -eq $Settings.max) { $true } else { $false } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceRetirementDays.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceRetirementDays.ps1 index 895eaf943222..b1427cb507d1 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceRetirementDays.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceRetirementDays.ps1 @@ -38,7 +38,15 @@ function Invoke-CIPPStandardintuneDeviceRetirementDays { return $true } #we're done. - $CurrentInfo = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupRules' -tenantid $Tenant) + try { + $CurrentInfo = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupRules' -tenantid $Tenant) + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the intuneDeviceRetirementDays state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $StateIsCorrect = if ($CurrentInfo.DeviceInactivityBeforeRetirementInDays -eq $Settings.days) { $true } else { $false } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneRequireMFA.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneRequireMFA.ps1 index f70b182365bc..e59197e0c573 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneRequireMFA.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneRequireMFA.ps1 @@ -35,7 +35,14 @@ function Invoke-CIPPStandardintuneRequireMFA { return $true } #we're done. - $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant + try { + $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the intuneRequireMFA state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { if ($PreviousSetting.multiFactorAuthConfiguration -eq 'required') { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardlaps.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardlaps.ps1 index b6f53ee67fda..d0fcac4280e4 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardlaps.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardlaps.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardlaps { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'laps' - $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant + try { + $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DeviceRegistrationPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { try { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 index 4a54c8ba0270..37b1e7653ffd 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 @@ -39,7 +39,14 @@ function Invoke-CIPPStandardsharingCapability { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the sharingCapability state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.report -eq $true) { Add-CIPPBPAField -FieldName 'sharingCapability' -FieldValue $CurrentInfo.sharingCapability -StoreAs string -Tenant $Tenant diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingDomainRestriction.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingDomainRestriction.ps1 index e1b5a62f4c62..033d49556c15 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingDomainRestriction.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingDomainRestriction.ps1 @@ -38,7 +38,14 @@ function Invoke-CIPPStandardsharingDomainRestriction { return $true } #we're done. - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SharingDomainRestriction state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Get mode value using null-coalescing operator $mode = $Settings.Mode.value ?? $Settings.Mode diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardunmanagedSync.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardunmanagedSync.ps1 index ed8241ce919e..d46a2736d3e6 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardunmanagedSync.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardunmanagedSync.ps1 @@ -38,7 +38,15 @@ function Invoke-CIPPStandardunmanagedSync { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object _ObjectIdentity_, TenantFilter, ConditionalAccessPolicy + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + Select-Object _ObjectIdentity_, TenantFilter, ConditionalAccessPolicy + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the unmanagedSync state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $WantedState = [int]($Settings.state.value ?? 2) # Default to 2 (Block Access) if not set, for pre v8.0.3 standard compatibility $Label = $Settings.state.label ?? 'Block Access' # Default label if not set, for pre v8.0.3 standard compatibility From 8f1801e51af9cfce3f871b977e6a92e041f414af Mon Sep 17 00:00:00 2001 From: Zac Richards <107489668+Zacgoose@users.noreply.github.com> Date: Thu, 31 Jul 2025 20:35:18 +0800 Subject: [PATCH 67/85] Update Invoke-CIPPStandardSPFileRequests.ps1 --- .../Standards/Invoke-CIPPStandardSPFileRequests.ps1 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 index 64ff49d50fef..6290bc0ed0f3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 @@ -34,15 +34,21 @@ function Invoke-CIPPStandardSPFileRequests { $TestResult = Test-CIPPStandardLicense -StandardName 'SPFileRequests' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') if ($TestResult -eq $false) { - Write-Host "We're exiting as the correct license is not present for this standard." + Write-LogMessage -API 'Standards' -tenant $tenant -message 'The tenant is not licenced for this standard SPFileRequests' -sev Error return $true } - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object _ObjectIdentity_, TenantFilter, CoreRequestFilesLinkEnabled, OneDriveRequestFilesLinkEnabled, CoreRequestFilesLinkExpirationInDays, OneDriveRequestFilesLinkExpirationInDays + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object _ObjectIdentity_, TenantFilter, CoreRequestFilesLinkEnabled, OneDriveRequestFilesLinkEnabled, CoreRequestFilesLinkExpirationInDays, OneDriveRequestFilesLinkExpirationInDays + } + catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Failed to get current state of SPO tenant details' -sev Error + return + } # Input validation if (($Settings.state -eq $null) -and ($Settings.remediate -eq $true -or $Settings.alert -eq $true)) { - Write-LogMessage -API 'Standards' -tenant $tenant -message 'SPFileRequests: Invalid state parameter set' -sev Error + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Invalid state parameter set for standard SPFileRequests' -sev Error return } @@ -66,7 +72,6 @@ function Invoke-CIPPStandardSPFileRequests { $AllSettingsCorrect = $StateIsCorrect -and $ExpirationIsCorrect if ($Settings.remediate -eq $true) { - Write-Host 'Time to remediate' if ($AllSettingsCorrect -eq $false) { try { From ebb3bf3188785d151e925f3023405aa3689a6542 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 31 Jul 2025 16:01:37 +0200 Subject: [PATCH 68/85] autoremediate true thing --- .../Public/Standards/Get-CIPPStandards.ps1 | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Modules/CIPPCore/Public/Standards/Get-CIPPStandards.ps1 b/Modules/CIPPCore/Public/Standards/Get-CIPPStandards.ps1 index 7eaf01a80d1e..b639cf088d80 100644 --- a/Modules/CIPPCore/Public/Standards/Get-CIPPStandards.ps1 +++ b/Modules/CIPPCore/Public/Standards/Get-CIPPStandards.ps1 @@ -61,12 +61,21 @@ function Get-CIPPStandards { $CurrentStandard | Add-Member -NotePropertyName 'TemplateId' -NotePropertyValue $Template.GUID -Force if ($CurrentStandard.action.value -contains 'Remediate' -and -not ($CurrentStandard.action.value -contains 'Report')) { + Write-Host 'STRDS: autoRemediate set to true, adding Remediate action' + $reportAction = [pscustomobject]@{ label = 'Report' value = 'Report' } $CurrentStandard.action = @($CurrentStandard.action) + $reportAction } + #If its a drift template, with autoRemediate, we add the Remediate action. + if ($CurrentStandard.autoRemediate -eq $true) { + $CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{ + label = 'Remediate' + value = 'Remediate' + } + } $Actions = $CurrentStandard.action.value if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') { @@ -90,6 +99,14 @@ function Get-CIPPStandards { } $CurrentStandard.action = @($CurrentStandard.action) + $reportAction } + #If its a drift template, with autoRemediate, we add the Remediate action. + if ($CurrentStandard.autoRemediate -eq $true) { + Write-Host 'STRDS: autoRemediate set to true, adding Remediate action' + $CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{ + label = 'Remediate' + value = 'Remediate' + } + } $Actions = $CurrentStandard.action.value if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') { @@ -235,6 +252,14 @@ function Get-CIPPStandards { } $CurrentStandard.action = @($CurrentStandard.action) + $reportAction } + #If its a drift template, with autoRemediate, we add the Remediate action. + if ($CurrentStandard.autoRemediate -eq $true) { + Write-Host 'STRDS: autoRemediate set to true, adding Remediate action' + $CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{ + label = 'Remediate' + value = 'Remediate' + } + } $Actions = $CurrentStandard.action.value if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') { @@ -269,6 +294,14 @@ function Get-CIPPStandards { } $CurrentStandard.action = @($CurrentStandard.action) + $reportAction } + #If its a drift template, with autoRemediate, we add the Remediate action. + if ($CurrentStandard.autoRemediate -eq $true) { + Write-Host 'STRDS: autoRemediate set to true, adding Remediate action' + $CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{ + label = 'Remediate' + value = 'Remediate' + } + } # Filter actions only 'Remediate','warn','Report' $Actions = $CurrentStandard.action.value | Where-Object { $_ -in 'Remediate', 'warn', 'Report' } @@ -292,6 +325,14 @@ function Get-CIPPStandards { } $CurrentStandard.action = @($CurrentStandard.action) + $reportAction } + #If its a drift template, with autoRemediate, we add the Remediate action. + if ($CurrentStandard.autoRemediate -eq $true) { + Write-Host 'STRDS: autoRemediate set to true, adding Remediate action' + $CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{ + label = 'Remediate' + value = 'Remediate' + } + } $Actions = $CurrentStandard.action.value | Where-Object { $_ -in 'Remediate', 'warn', 'Report' } if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') { From 67e22b59508d65f897602211b8e51eca10819d7a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 31 Jul 2025 11:12:09 -0400 Subject: [PATCH 69/85] drift management timer --- CIPPTimers.json | 11 ++- .../Standards/Push-CIPPDriftManagement.ps1 | 12 +-- .../Standards/Invoke-CIPPStandardsRun.ps1 | 88 +++++++++++++------ .../Start-DriftStandardsOrchestrator.ps1 | 13 +++ 4 files changed, 90 insertions(+), 34 deletions(-) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-DriftStandardsOrchestrator.ps1 diff --git a/CIPPTimers.json b/CIPPTimers.json index cbd1366a5363..3532f6fafe42 100644 --- a/CIPPTimers.json +++ b/CIPPTimers.json @@ -80,6 +80,15 @@ "RunOnProcessor": true, "PreferredProcessor": "standards" }, + { + "Id": "4d80205c-674d-4fc1-abeb-a1ec37e0d796", + "Command": "Start-DriftStandardsOrchestrator", + "Description": "Orchestrator to process drift standards", + "Cron": "0 0 */1 * * *", + "Priority": 5, + "RunOnProcessor": true, + "PreferredProcessor": "standards" + }, { "Id": "97145a1d-28f0-4bb2-b929-5a43517d23cc", "Command": "Start-SchedulerOrchestrator", @@ -197,4 +206,4 @@ "RunOnProcessor": true, "IsSystem": true } -] +] \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 index 6f0cd7ddb9c7..c0e94d1e33f5 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 @@ -7,10 +7,10 @@ function Push-CippDriftManagement { $Item ) - Write-Information "Received queue item for $($Item.Tenant)" + Write-Information "Received drift standard item for $($Item.Tenant)" try { - $Drift = Get-CIPPDrift -TenantFilter $item.tenant + $Drift = Get-CIPPDrift -TenantFilter $Item.Tenant if ($Drift.newDeviationsCount -gt 0) { $email = (Get-CIPPTenantAlignment -TenantFilter $Item.Tenant | Where-Object -Property standardType -EQ 'drift').standardSettings.email $webhook = (Get-CIPPTenantAlignment -TenantFilter $Item.Tenant | Where-Object -Property standardType -EQ 'drift').standardSettings.webhook @@ -30,12 +30,12 @@ function Push-CippDriftManagement { Status = $_.status } } - $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data -CIPPURL $CIPPURL -Tenant $item.tenant -InputObject 'driftStandard' + $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data -CIPPURL $CIPPURL -Tenant $Item.tenant -InputObject 'driftStandard' $CIPPAlert = @{ Type = 'email' Title = $GenerateEmail.title HTMLContent = $GenerateEmail.htmlcontent - TenantFilter = $item.Tenant + TenantFilter = $Item.Tenant } Write-Host 'Going to send the mail' Send-CIPPAlert @CIPPAlert -altEmail $email @@ -50,7 +50,7 @@ function Push-CippDriftManagement { Type = 'webhook' Title = $GenerateEmail.title JSONContent = $WebhookData - TenantFilter = $item.tenant + TenantFilter = $Item.tenant } Write-Host 'Sending Webhook Content' Send-CIPPAlert @CippAlert -altWebhook $webhook @@ -70,7 +70,7 @@ function Push-CippDriftManagement { Write-Information "Drift management completed for tenant $($Item.Tenant)" } catch { Write-LogMessage -API 'DriftStandards' -tenant $Item.Tenant -message "Error running Drift Check for tenant $($Item.Tenant) - $($_.Exception.Message)" -sev Error -LogData (Get-CippException -Exception $_) - Write-Warning "Error running standard $($Item.Standard) for tenant $($Item.Tenant) - $($_.Exception.Message)" + Write-Warning "Error running drift standards for tenant $($Item.Tenant) - $($_.Exception.Message)" Write-Information $_.InvocationInfo.PositionMessage throw $_.Exception.Message } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 index a42da2a5d425..347ce7a4eb6d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 @@ -15,38 +15,72 @@ function Invoke-CIPPStandardsRun { [Parameter(Mandatory = $false)] $TemplateID, [Parameter(Mandatory = $false)] - $runManually = $false - + $runManually = $false, + [Parameter(Mandatory = $false)] + [switch]$Drift ) - Write-Host "Starting process for standards - $($tenantFilter). TemplateID: $($TemplateID) RunManually: $($runManually) Force: $($Force.IsPresent)" + Write-Information "Starting process for standards - $($tenantFilter). TemplateID: $($TemplateID) RunManually: $($runManually) Force: $($Force.IsPresent) Drift: $($Drift.IsPresent)" - $AllTasks = Get-CIPPStandards + if ($Drift.IsPresent) { + Write-Information 'Drift Standards Run' + $AllTasks = Get-CIPPTenantAlignment | Where-Object -Property standardtype -EQ 'drift' | Select-Object -Property Tenant | Sort-Object -Unique -Property Tenant - if ($Force.IsPresent) { - Write-Host 'Clearing Rerun Cache' - Test-CIPPRerun -ClearAll -TenantFilter $TenantFilter -Type 'Standard' - } + #For each item in our object, run the queue. + $Queue = New-CippQueueEntry -Name 'Drift Standards' -TotalTasks ($AllTasks | Measure-Object).Count - #For each item in our object, run the queue. - $Queue = New-CippQueueEntry -Name "Applying Standards ($TenantFilter)" -TotalTasks ($AllTasks | Measure-Object).Count - - $InputObject = [PSCustomObject]@{ - OrchestratorName = 'StandardsOrchestrator' - QueueFunction = @{ - FunctionName = 'GetStandards' - QueueId = $Queue.RowKey - StandardParams = @{ - TenantFilter = $TenantFilter - runManually = $runManually + $Batch = foreach ($Task in $AllTasks) { + [PSCustomObject]@{ + FunctionName = 'CIPPDriftManagement' + Tenant = $Task.Tenant } } - SkipLog = $true - } - if ($TemplateID) { - $InputObject.QueueFunction.StandardParams['TemplateId'] = $TemplateID + + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'DriftStandardsOrchestrator' + Batch = @($Batch) + SkipLog = $true + } + + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) + Write-Information "Started orchestration with ID = '$InstanceId' for drift standards run" + #$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId + return + } else { + Write-Information 'Classic Standards Run' + $AllTasks = Get-CIPPStandards + + if ($Force.IsPresent) { + Write-Information 'Clearing Rerun Cache' + Test-CIPPRerun -ClearAll -TenantFilter $TenantFilter -Type 'Standard' + } + + if ($AllTasks.Count -eq 0) { + Write-Information "No standards found for tenant $($TenantFilter)." + return + } + + #For each item in our object, run the queue. + $Queue = New-CippQueueEntry -Name "Applying Standards ($TenantFilter)" -TotalTasks ($AllTasks | Measure-Object).Count + + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'StandardsOrchestrator' + QueueFunction = @{ + FunctionName = 'GetStandards' + QueueId = $Queue.RowKey + StandardParams = @{ + TenantFilter = $TenantFilter + runManually = $runManually + Drift = $Drift.IsPresent + } + } + SkipLog = $true + } + if ($TemplateID) { + $InputObject.QueueFunction.StandardParams['TemplateId'] = $TemplateID + } + Write-Information "InputObject: $($InputObject | ConvertTo-Json -Depth 5 -Compress)" + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) + Write-Information "Started orchestration with ID = '$InstanceId'" + #$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId } - Write-Host "InputObject: $($InputObject | ConvertTo-Json -Depth 5 -Compress)" - $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) - Write-Host "Started orchestration with ID = '$InstanceId'" - #$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId } diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-DriftStandardsOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-DriftStandardsOrchestrator.ps1 new file mode 100644 index 000000000000..f3652e1d4156 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-DriftStandardsOrchestrator.ps1 @@ -0,0 +1,13 @@ +function Start-DriftStandardsOrchestrator { + <# + .SYNOPSIS + Start the Drift Standards Orchestrator + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + + if ($PSCmdlet.ShouldProcess('Start-DriftStandardsOrchestrator', 'Starting Drift Standards Orchestrator')) { + Write-LogMessage -API 'Standards' -message 'Starting Standards Schedule' -sev Info + Invoke-CIPPStandardsRun -Drift + } +} From c84993168429d9682de432b46b355f530a69d7c6 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 31 Jul 2025 11:13:26 -0400 Subject: [PATCH 70/85] log tweak --- .../Orchestrator Functions/Start-DriftStandardsOrchestrator.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-DriftStandardsOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-DriftStandardsOrchestrator.ps1 index f3652e1d4156..7aad617a0c71 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-DriftStandardsOrchestrator.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-DriftStandardsOrchestrator.ps1 @@ -7,7 +7,7 @@ function Start-DriftStandardsOrchestrator { param() if ($PSCmdlet.ShouldProcess('Start-DriftStandardsOrchestrator', 'Starting Drift Standards Orchestrator')) { - Write-LogMessage -API 'Standards' -message 'Starting Standards Schedule' -sev Info + Write-LogMessage -API 'Standards' -message 'Starting Drift Standards Schedule' -sev Info Invoke-CIPPStandardsRun -Drift } } From 4c38523c0ade95b22a0f2348b41fb4e8bf7d4919 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 31 Jul 2025 17:28:03 -0400 Subject: [PATCH 71/85] magic to fix microsoft casing issue apparently some of the setting templates in the catalog have guids in uppercase, when retrieving the policy later, the guid is normalized to lowercase and can not be deployed without fixing the casing --- .../CIPPCore/Public/Set-CIPPIntunePolicy.ps1 | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Modules/CIPPCore/Public/Set-CIPPIntunePolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPIntunePolicy.ps1 index 71564d7a732f..7b7ee7fe629b 100644 --- a/Modules/CIPPCore/Public/Set-CIPPIntunePolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPIntunePolicy.ps1 @@ -109,6 +109,40 @@ function Set-CIPPIntunePolicy { $PlatformType = 'deviceManagement' $TemplateTypeURL = 'configurationPolicies' $DisplayName = ($RawJSON | ConvertFrom-Json).Name + + $Template = $RawJSON | ConvertFrom-Json + if ($Template.templateReference.templateId) { + Write-Information "Checking configuration policy template $($Template.templateReference.templateId) for $($DisplayName)" + # Remove unavailable settings from the template + $AvailableSettings = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicyTemplates('$($Template.templateReference.templateId)')/settingTemplates?`$expand=settingDefinitions&`$top=1000" -tenantid $tenantFilter + + if ($AvailableSettings) { + Write-Information "Available settings for template $($Template.templateReference.templateId): $($AvailableSettings.Count)" + $FilteredSettings = [system.collections.generic.list[psobject]]::new() + foreach ($setting in $Template.settings) { + if ($setting.settingInstance.settingInstanceTemplateReference.settingInstanceTemplateId -in $AvailableSettings.settingInstanceTemplate.settingInstanceTemplateId) { + $AvailableSetting = $AvailableSettings | Where-Object { $_.settingInstanceTemplate.settingInstanceTemplateId -eq $setting.settingInstance.settingInstanceTemplateReference.settingInstanceTemplateId } + + if ($AvailableSetting.settingInstanceTemplate.settingInstanceTemplateId -cnotmatch $setting.settingInstance.settingInstanceTemplateReference.settingInstanceTemplateId) { + # update casing + Write-Information "Fixing casing for setting instance template $($AvailableSetting.settingInstanceTemplate.settingInstanceTemplateId)" + $setting.settingInstance.settingInstanceTemplateReference.settingInstanceTemplateId = $AvailableSetting.settingInstanceTemplate.settingInstanceTemplateId + } + + if ($AvailableSetting.settingInstanceTemplate.choiceSettingValueTemplate -cnotmatch $setting.settingInstance.choiceSettingValue.settingValueTemplateReference.settingValueTemplateId) { + # update choice setting value template + Write-Information "Fixing casing for choice setting value template $($AvailableSetting.settingInstanceTemplate.choiceSettingValueTemplate.settingValueTemplateId)" + $setting.settingInstance.choiceSettingValue.settingValueTemplateReference.settingValueTemplateId = $AvailableSetting.settingInstanceTemplate.choiceSettingValueTemplate.settingValueTemplateId + } + + $FilteredSettings.Add($setting) + } + } + $Template.settings = $FilteredSettings + $RawJSON = $Template | ConvertTo-Json -Depth 100 -Compress + } + } + $CheckExististing = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/$PlatformType/$TemplateTypeURL" -tenantid $tenantFilter if ($DisplayName -in $CheckExististing.name) { $PolicyFile = $RawJSON | ConvertFrom-Json | Select-Object * -ExcludeProperty Platform, PolicyType, CreationSource From 249ce2fa454f38441cb8c016505d5cf47bfb1025 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 31 Jul 2025 17:43:50 -0400 Subject: [PATCH 72/85] fix tenantfilter --- .../Tenant/Standards/Invoke-CIPPStandardsRun.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 index 347ce7a4eb6d..aa73128fe770 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 @@ -23,7 +23,7 @@ function Invoke-CIPPStandardsRun { if ($Drift.IsPresent) { Write-Information 'Drift Standards Run' - $AllTasks = Get-CIPPTenantAlignment | Where-Object -Property standardtype -EQ 'drift' | Select-Object -Property Tenant | Sort-Object -Unique -Property Tenant + $AllTasks = Get-CIPPTenantAlignment | Where-Object -Property standardtype -EQ 'drift' | Select-Object -Property TenantFilter | Sort-Object -Unique -Property TenantFilter #For each item in our object, run the queue. $Queue = New-CippQueueEntry -Name 'Drift Standards' -TotalTasks ($AllTasks | Measure-Object).Count @@ -31,7 +31,7 @@ function Invoke-CIPPStandardsRun { $Batch = foreach ($Task in $AllTasks) { [PSCustomObject]@{ FunctionName = 'CIPPDriftManagement' - Tenant = $Task.Tenant + Tenant = $Task.TenantFilter } } From bd6a52b6aff6410c3b37e2116a0611b3eea462d1 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 31 Jul 2025 18:29:07 -0400 Subject: [PATCH 73/85] add text replacement and max character limits to device rename --- .../Endpoint/MEM/Invoke-ExecDeviceAction.ps1 | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) 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 5600b3984fce..5b18d65cfecf 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 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-ExecDeviceAction { +function Invoke-ExecDeviceAction { <# .FUNCTIONALITY Entrypoint @@ -22,6 +22,16 @@ Function Invoke-ExecDeviceAction { try { switch ($Action) { 'setDeviceName' { + if ($Request.Body.input -match '%') { + $Device = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$DeviceFilter" -tenantid $TenantFilter + $Request.Body.input = Get-CIPPTextReplacement -TenantFilter $TenantFilter -Text $Request.Body.input + $Request.Body.input = $Request.Body.input -replace '%SERIAL%', $Device.serialNumber + # limit to 15 characters + if ($Request.Body.input.Length -gt 15) { + $Request.Body.input = $Request.Body.input.Substring(0, 15) + } + } + $ActionBody = @{ deviceName = $Request.Body.input } | ConvertTo-Json -Compress break } @@ -30,7 +40,7 @@ Function Invoke-ExecDeviceAction { Write-Host "ActionBody: $ActionBody" break } - Default { $ActionBody = $Request.Body | ConvertTo-Json -Compress } + default { $ActionBody = $Request.Body | ConvertTo-Json -Compress } } $cmdParams = @{ From 208ab11162e64a07256cd5b09dda14de2ebe3190 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Fri, 1 Aug 2025 00:39:25 +0200 Subject: [PATCH 74/85] fixes remediation --- .../Standards/Push-CIPPDriftManagement.ps1 | 5 ++- .../Invoke-ExecUpdateDriftDeviation.ps1 | 38 ++++++++++++++----- .../Functions/Get-CIPPTenantAlignment.ps1 | 2 + ...e-CIPPStandardCustomBannedPasswordList.ps1 | 3 +- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 index c0e94d1e33f5..df5743848ed1 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 @@ -12,8 +12,9 @@ function Push-CippDriftManagement { try { $Drift = Get-CIPPDrift -TenantFilter $Item.Tenant if ($Drift.newDeviationsCount -gt 0) { - $email = (Get-CIPPTenantAlignment -TenantFilter $Item.Tenant | Where-Object -Property standardType -EQ 'drift').standardSettings.email - $webhook = (Get-CIPPTenantAlignment -TenantFilter $Item.Tenant | Where-Object -Property standardType -EQ 'drift').standardSettings.webhook + $Settings = (Get-CIPPTenantAlignment -TenantFilter $Item.Tenant | Where-Object -Property standardType -EQ 'drift') + $email = $Settings.driftAlertEmail + $webhook = $Settings.driftAlertWebhook $CippConfigTable = Get-CippTable -tablename Config $CippConfig = Get-CIPPAzDataTableEntity @CippConfigTable -Filter "PartitionKey eq 'InstanceProperties' and RowKey eq 'CIPPURL'" $CIPPURL = 'https://{0}' -f $CippConfig.Value diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 index 6ad626c79a1d..b80a97ea6f7f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 @@ -27,7 +27,7 @@ function Invoke-ExecUpdateDriftDeviation { success = $true result = "All drift customizations removed for tenant $TenantFilter" }) - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed all drift customizations for tenant $TenantFilter" -Sev 'Info' + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed all drift customizations for tenant $TenantFilter" -Sev 'Info' } else { $Deviations = $Request.Body.deviations $Reason = $Request.Body.reason @@ -40,18 +40,36 @@ function Invoke-ExecUpdateDriftDeviation { success = $true result = $Result } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Updated drift deviation status for $($Deviation.standardName) to $($Deviation.status) with reason: $Reason" -Sev 'Info' + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Updated drift deviation status for $($Deviation.standardName) to $($Deviation.status) with reason: $Reason" -Sev 'Info' if ($Deviation.status -eq 'DeniedRemediate') { $Setting = $Deviation.standardName -replace 'standards.', '' $StandardTemplate = Get-CIPPTenantAlignment -TenantFilter $TenantFilter | Where-Object -Property standardType -EQ 'drift' - $StandardTemplate = $StandardTemplate.$Setting - $StandardTemplate.action = @( - @{label = 'Report'; value = 'Report' }, - @{ label = 'Remediate'; value = 'Remediate' } - ) - #idea here is to make a system job that triggers the remediation process, so that users can click on "Deniedremediate" - #That job then launches a single standard run, it gets the same input as an orch, but is just a scheduled job. + $StandardTemplate = $StandardTemplate.standardSettings.$Setting + $StandardTemplate.standards.$Setting | Add-Member -MemberType NoteProperty -Name 'remediate' -Value $true -Force + $StandardTemplate.standards.$Setting | Add-Member -MemberType NoteProperty -Name 'report' -Value $true -Force + + $TaskBody = @{ + TenantFilter = $TenantFilter + Name = "One Off Drift Remediation: $Setting - $TenantFilter" + Command = @{ + value = "Invoke-CIPPStandard$Setting" + label = "Invoke-CIPPStandard$Setting" + } + + Parameters = [pscustomobject]@{ + Tenant = $TenantFilter + Settings = $StandardTemplate.standards.$Setting + } + ScheduledTime = '0' + PostExecution = @{ + Webhook = $false + Email = $false + PSA = $false + } + } + Add-CIPPScheduledTask -Task $TaskBody -hidden $false + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Scheduled drift remediation task for $Setting" -Sev 'Info' } if ($Deviation.status -eq 'deniedDelete') { #Here we look at the policy ID received and the type, and nuke it. @@ -62,7 +80,7 @@ function Invoke-ExecUpdateDriftDeviation { success = $false error = $_.Exception.Message } - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to update drift deviation for $($Deviation.standardName): $($_.Exception.Message)" -Sev 'Error' + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to update drift deviation for $($Deviation.standardName): $($_.Exception.Message)" -Sev 'Error' } } } diff --git a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 index ac71b7633e27..75011dba6cdf 100644 --- a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 +++ b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 @@ -259,6 +259,8 @@ function Get-CIPPTenantAlignment { StandardId = $Template.GUID standardType = $Template.type standardSettings = $Template.Standards + driftAlertEmail = $Template.driftAlertEmail + driftAlertWebhook = $Template.driftAlertWebhook AlignmentScore = $AlignmentPercentage LicenseMissingPercentage = $LicenseMissingPercentage CombinedScore = $AlignmentPercentage + $LicenseMissingPercentage diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCustomBannedPasswordList.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCustomBannedPasswordList.ps1 index 5191bf2cc04c..4231f56270c2 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCustomBannedPasswordList.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCustomBannedPasswordList.ps1 @@ -31,9 +31,8 @@ function Invoke-CIPPStandardCustomBannedPasswordList { #> param($Tenant, $Settings) - + Write-Host "All params received: $Tenant, $tenant, $($Settings | ConvertTo-Json -Depth 10 -Compress)" $PasswordRuleTemplateId = '5cf42378-d67d-4f36-ba46-e8b86229381d' - # Parse and validate banned words from input $BannedWordsInput = $Settings.BannedWords if ([string]::IsNullOrWhiteSpace($BannedWordsInput)) { From 88d169ab5b63eb51e9de4dd1bed9f3f258cf7c5d Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 31 Jul 2025 18:44:55 -0400 Subject: [PATCH 75/85] remove drift param --- .../HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 index aa73128fe770..04a61795e46b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 @@ -70,7 +70,6 @@ function Invoke-CIPPStandardsRun { StandardParams = @{ TenantFilter = $TenantFilter runManually = $runManually - Drift = $Drift.IsPresent } } SkipLog = $true From 6ac8021f31e1f9a6a6dfe5ebc86a6ff73f4c6bea Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Fri, 1 Aug 2025 10:24:37 +0200 Subject: [PATCH 76/85] Add delete CA --- .../Invoke-ExecUpdateDriftDeviation.ps1 | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 index b80a97ea6f7f..0566a0b69435 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 @@ -72,7 +72,21 @@ function Invoke-ExecUpdateDriftDeviation { Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Scheduled drift remediation task for $Setting" -Sev 'Info' } if ($Deviation.status -eq 'deniedDelete') { - #Here we look at the policy ID received and the type, and nuke it. + if ($Deviation.standardName -like 'ConditionalAccessTemplate*') { + $ID = $Deviation.standardName -replace 'ConditionalAccessTemplates.', '' + Write-Host "Going to delete CA Policy with ID $ID. Deviation Name is $($Deviation.standardName)" + $null = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/$($ID)" -type DELETE -tenant $TenantFilter -asapp $true + "Deleted CA Policy $($ID)" + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted Conditional Access Policy with ID $($ID)" -Sev 'Info' + } + + if ($Deviation.standardName -like 'IntuneTemplates*') { + New-GraphPostRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$($UrlName)('$($PolicyId)')" -type DELETE -tenant $TenantFilter + "Deleted Intune Policy $($ID)" + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted Intune Policy with ID $($ID)" -Sev 'Info' + + } + } } catch { [PSCustomObject]@{ From dbfa4ed95f5a8249767ad964c0c08b318746ff0d Mon Sep 17 00:00:00 2001 From: Esco Date: Fri, 1 Aug 2025 14:13:00 +0200 Subject: [PATCH 77/85] chore: update alert object --- .../Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 | 3 ++- .../Standards/Invoke-CIPPStandardEnableOnlineArchiving.ps1 | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 index 56ac2349159d..957455656cfe 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 @@ -63,7 +63,8 @@ function Invoke-CIPPStandardDisableGuests { if ($Settings.alert -eq $true) { if ($GraphRequest.Count -gt 0) { - Write-StandardsAlert -message "Guests accounts with a login longer than 90 days ago: $($GraphRequest.count)" -object $GraphRequest -tenant $tenant -standardName 'DisableGuests' -standardId $Settings.standardId + $Filtered = $GraphRequest | Select-Object -Property UserPrincipalName, id, signInActivity, mail, userType, accountEnabled, externalUserState + Write-StandardsAlert -message "Guests accounts with a login longer than 90 days ago: $($GraphRequest.count)" -object $Filtered -tenant $tenant -standardName 'DisableGuests' -standardId $Settings.standardId Write-LogMessage -API 'Standards' -tenant $tenant -message "Guests accounts with a login longer than 90 days ago: $($GraphRequest.count)" -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'No guests accounts with a login longer than 90 days ago.' -sev Info diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableOnlineArchiving.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableOnlineArchiving.ps1 index fe06e02928a4..6710fce40846 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableOnlineArchiving.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableOnlineArchiving.ps1 @@ -76,7 +76,8 @@ function Invoke-CIPPStandardEnableOnlineArchiving { if ($Settings.alert -eq $true) { if ($MailboxesNoArchive) { - Write-StandardsAlert -message "Mailboxes without Online Archiving: $($MailboxesNoArchive.Count)" -object $MailboxesNoArchive -tenant $Tenant -standardName 'EnableOnlineArchiving' -standardId $Settings.standardId + $Object = $MailboxesNoArchive | Select-Object -Property UserPrincipalName, ArchiveGuid + Write-StandardsAlert -message "Mailboxes without Online Archiving: $($MailboxesNoArchive.Count)" -object $Object -tenant $Tenant -standardName 'EnableOnlineArchiving' -standardId $Settings.standardId Write-LogMessage -API 'Standards' -tenant $Tenant -message "Mailboxes without Online Archiving: $($MailboxesNoArchive.Count)" -sev Info } else { Write-LogMessage -API 'Standards' -tenant $Tenant -message 'All mailboxes have Online Archiving enabled' -sev Info From 7dd55816ac584f31aae76dded6aaa92ec62f036a Mon Sep 17 00:00:00 2001 From: rvdwegen Date: Fri, 1 Aug 2025 15:22:57 +0200 Subject: [PATCH 78/85] LicenseAssignmentErrors alert --- .../Get-CIPPAlertLicenseAssignmentErrors.ps1 | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 Modules/CIPPCore/Public/Alerts/Get-CIPPAlertLicenseAssignmentErrors.ps1 diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertLicenseAssignmentErrors.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertLicenseAssignmentErrors.ps1 new file mode 100644 index 000000000000..f72d83ac37cb --- /dev/null +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertLicenseAssignmentErrors.ps1 @@ -0,0 +1,91 @@ +function Get-CIPPAlertLicenseAssignmentErrors { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + Param ( + [Parameter(Mandatory)] + $TenantFilter, + [Alias('input')] + $InputValue + ) + + # Define error code translations for human-readable messages + $ErrorTranslations = @( + @{ + ErrorCode = "CountViolation" + Description = "Not enough licenses available - the organization has exceeded the number of available licenses for this SKU" + }, + @{ + ErrorCode = "MutuallyExclusiveViolation" + Description = "Conflicting licenses assigned - this license cannot be assigned alongside another license the user already has" + }, + @{ + ErrorCode = "ProhibitedInUsageLocationViolation" + Description = "License not available in user's location - this license cannot be assigned to users in the user's current usage location" + }, + @{ + ErrorCode = "UniquenessViolation" + Description = "Duplicate license assignment - this license can only be assigned once per user" + }, + @{ + ErrorCode = "Unknown" + Description = "Unknown license assignment error - an unspecified error occurred during license assignment" + } + ) + + try { + # Get all users with license assignment states from Graph API + $Users = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$select=id,userPrincipalName,displayName,licenseAssignmentStates&`$top=999" -tenantid $TenantFilter + + # Filter users who have license assignment violations + $UsersWithViolations = $Users | Where-Object { + $_.licenseAssignmentStates -and + ($_.licenseAssignmentStates | Where-Object { + $_.error -and ( + $_.error -like "*CountViolation*" -or + $_.error -like "*MutuallyExclusiveViolation*" -or + $_.error -like "*ProhibitedInUsageLocationViolation*" -or + $_.error -like "*UniquenessViolation*" -or + $_.error -like "*Unknown*" + ) + }) + } + + # Build alert messages for users with violations + $LicenseAssignmentErrors = foreach ($User in $UsersWithViolations) { + $ViolationErrors = $User.licenseAssignmentStates | Where-Object { + $_.error -and ( + $_.error -like "*CountViolation*" -or + $_.error -like "*MutuallyExclusiveViolation*" -or + $_.error -like "*ProhibitedInUsageLocationViolation*" -or + $_.error -like "*UniquenessViolation*" -or + $_.error -like "*Unknown*" + ) + } + + foreach ($Violation in $ViolationErrors) { + # Find matching error translation + $ErrorTranslation = $ErrorTranslations | Where-Object { $Violation.error -like "*$($_.ErrorCode)*" } | Select-Object -First 1 + $HumanReadableError = if ($ErrorTranslation) { + $ErrorTranslation.Description + } else { + "Unknown license assignment error: $($Violation.error)" + } + + $PrettyName = Convert-SKUname -skuID $Violation.skuId + + "$($User.userPrincipalName): $HumanReadableError (License: $PrettyName)" + } + } + + # If errors are found, write alert + if ($LicenseAssignmentErrors) { + Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $LicenseAssignmentErrors + } + + } catch { + Write-LogMessage -message "Failed to check license assignment errors: $($_.exception.message)" -API 'License Assignment Alerts' -tenant $TenantFilter -sev Error + } +} From baa663e25e63b5865f6ea7c4fc1e621e2eb3ad34 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:39:54 +0200 Subject: [PATCH 79/85] prerelease push --- .../Invoke-ExecUpdateDriftDeviation.ps1 | 23 ++- .../Public/Functions/Get-CIPPURLName.ps1 | 155 ++++++++++++++++++ Modules/CIPPCore/Public/Get-CIPPDrift.ps1 | 19 ++- 3 files changed, 176 insertions(+), 21 deletions(-) create mode 100644 Modules/CIPPCore/Public/Functions/Get-CIPPURLName.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 index 0566a0b69435..82b45b6bb37c 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 @@ -72,20 +72,19 @@ function Invoke-ExecUpdateDriftDeviation { Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Scheduled drift remediation task for $Setting" -Sev 'Info' } if ($Deviation.status -eq 'deniedDelete') { - if ($Deviation.standardName -like 'ConditionalAccessTemplate*') { - $ID = $Deviation.standardName -replace 'ConditionalAccessTemplates.', '' - Write-Host "Going to delete CA Policy with ID $ID. Deviation Name is $($Deviation.standardName)" - $null = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/$($ID)" -type DELETE -tenant $TenantFilter -asapp $true - "Deleted CA Policy $($ID)" - Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted Conditional Access Policy with ID $($ID)" -Sev 'Info' + $Policy = $Deviation.receivedValue | ConvertFrom-Json -ErrorAction SilentlyContinue + Write-Host "Policy is $($Policy)" + $URLName = Get-CIPPURLName -Template $Policy + if ($Policy -and $URLName) { + Write-Host "Going to delete Policy with ID $($policy.ID) Deviation Name is $($Deviation.standardName)" + $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/$($URLName)/$($policy.id)" -type DELETE -tenant $TenantFilter + "Deleted Policy $($ID)" + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted Policy with ID $($ID)" -Sev 'Info' + } else { + "could not find policy with ID $($ID)" + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not find Policy with ID $($ID) to delete for remediation" -Sev 'Warning' } - if ($Deviation.standardName -like 'IntuneTemplates*') { - New-GraphPostRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$($UrlName)('$($PolicyId)')" -type DELETE -tenant $TenantFilter - "Deleted Intune Policy $($ID)" - Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted Intune Policy with ID $($ID)" -Sev 'Info' - - } } } catch { diff --git a/Modules/CIPPCore/Public/Functions/Get-CIPPURLName.ps1 b/Modules/CIPPCore/Public/Functions/Get-CIPPURLName.ps1 new file mode 100644 index 000000000000..c9136b55435e --- /dev/null +++ b/Modules/CIPPCore/Public/Functions/Get-CIPPURLName.ps1 @@ -0,0 +1,155 @@ +function Get-CIPPURLName { + <# + .SYNOPSIS + Gets the correct Microsoft Graph URL based on the OData type of a template + .DESCRIPTION + This function examines the @odata.type property of a JSON template object and returns + the appropriate full Microsoft Graph API URL for that resource type. + .PARAMETER Template + The template object containing the @odata.type property to analyze + .FUNCTIONALITY + Internal + .EXAMPLE + Get-CIPPURLName -Template $MyTemplate + .EXAMPLE + $Template | Get-CIPPURLName + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [PSCustomObject]$Template + ) + + # Extract the OData type from the template + $ODataType = $Template.'@odata.type' + if ($Template.urlName) { return $Template.urlName } + + if (-not $ODataType) { + Write-Warning 'No @odata.type property found in template' + return $null + } + + # Determine the full Microsoft Graph URL based on the OData type + $URLName = switch -wildcard ($ODataType) { + # Device Compliance Policies + '*CompliancePolicy' { + 'deviceManagement/deviceCompliancePolicies' + } + '*deviceCompliancePolicy' { + 'deviceManagement/deviceCompliancePolicies' + } + + # Managed App Policies (App Protection) + '*ManagedAppProtection' { + 'deviceAppManagement/managedAppPolicies' + } + '*managedAppPolicies' { + 'deviceAppManagement/managedAppPolicies' + } + '*managedAppPolicy' { + 'deviceAppManagement/managedAppPolicies' + } + '*appProtectionPolicy' { + 'deviceAppManagement/managedAppPolicies' + } + + # Configuration Policies (Settings Catalog) + '*configurationPolicies' { + 'deviceManagement/configurationPolicies' + } + '*deviceManagementConfigurationPolicy' { + 'deviceManagement/configurationPolicies' + } + + # Windows Driver Update Profiles + '*windowsDriverUpdateProfiles' { + 'deviceManagement/windowsDriverUpdateProfiles' + } + '*windowsDriverUpdateProfile' { + 'deviceManagement/windowsDriverUpdateProfiles' + } + + # Device Configurations + '*deviceConfigurations' { + 'deviceManagement/deviceConfigurations' + } + '*deviceConfiguration' { + 'deviceManagement/deviceConfigurations' + } + + # Group Policy Configurations (Administrative Templates) + '*groupPolicyConfigurations' { + 'deviceManagement/groupPolicyConfigurations' + } + '*groupPolicyConfiguration' { + 'deviceManagement/groupPolicyConfigurations' + } + + # Conditional Access Policies + '*conditionalAccessPolicy' { + 'identity/conditionalAccess/policies' + } + + # Device Enrollment Configurations + '*deviceEnrollmentConfiguration' { + 'deviceManagement/deviceEnrollmentConfigurations' + } + '*enrollmentConfiguration' { + 'deviceManagement/deviceEnrollmentConfigurations' + } + + # Mobile App Configurations + '*mobileAppConfiguration' { + 'deviceAppManagement/mobileAppConfigurations' + } + '*appConfiguration' { + 'deviceAppManagement/mobileAppConfigurations' + } + + # Windows Feature Update Profiles + '*windowsFeatureUpdateProfile' { + 'deviceManagement/windowsFeatureUpdateProfiles' + } + + # Device Health Scripts (Remediation Scripts) + '*deviceHealthScript' { + 'deviceManagement/deviceHealthScripts' + } + + # Device Management Scripts (PowerShell Scripts) + '*deviceManagementScript' { + 'deviceManagement/deviceManagementScripts' + } + + # Mobile Applications + '*mobileApp' { + 'deviceAppManagement/mobileApps' + } + '*winGetApp' { + 'deviceAppManagement/mobileApps' + } + '*officeSuiteApp' { + 'deviceAppManagement/mobileApps' + } + + # Named Locations + '*namedLocation' { + 'identity/conditionalAccess/namedLocations' + } + '*ipNamedLocation' { + 'identity/conditionalAccess/namedLocations' + } + '*countryNamedLocation' { + 'identity/conditionalAccess/namedLocations' + } + + # Default fallback + default { + Write-Warning "Unknown OData type: $ODataType" + $null + } + } + + return $URLName +} + diff --git a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 index 36c632428e51..f08cd5e573ff 100644 --- a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 @@ -84,47 +84,47 @@ function Get-CIPPDrift { # Always get live data when not in AllTenants mode $IntuneRequests = @( @{ - id = 'deviceAppManagement' + id = 'deviceAppManagement/managedAppPolicies' url = 'deviceAppManagement/managedAppPolicies' method = 'GET' } @{ - id = 'deviceCompliancePolicies' + id = 'deviceManagement/deviceCompliancePolicies' url = 'deviceManagement/deviceCompliancePolicies' method = 'GET' } @{ - id = 'groupPolicyConfigurations' + id = 'deviceManagement/groupPolicyConfigurations' url = 'deviceManagement/groupPolicyConfigurations' method = 'GET' } @{ - id = 'deviceConfigurations' + id = 'deviceManagement/deviceConfigurations' url = 'deviceManagement/deviceConfigurations' method = 'GET' } @{ - id = 'configurationPolicies' + id = 'deviceManagement/configurationPolicies' url = 'deviceManagement/configurationPolicies' method = 'GET' } @{ - id = 'windowsDriverUpdateProfiles' + id = 'deviceManagement/windowsDriverUpdateProfiles' url = 'deviceManagement/windowsDriverUpdateProfiles' method = 'GET' } @{ - id = 'windowsFeatureUpdateProfiles' + id = 'deviceManagement/windowsFeatureUpdateProfiles' url = 'deviceManagement/windowsFeatureUpdateProfiles' method = 'GET' } @{ - id = 'windowsQualityUpdatePolicies' + id = 'deviceManagement/windowsQualityUpdatePolicies' url = 'deviceManagement/windowsQualityUpdatePolicies' method = 'GET' } @{ - id = 'windowsQualityUpdateProfiles' + id = 'deviceManagement/windowsQualityUpdateProfiles' url = 'deviceManagement/windowsQualityUpdateProfiles' method = 'GET' } @@ -220,6 +220,7 @@ function Get-CIPPDrift { # Check for extra Intune policies not in template foreach ($TenantPolicy in $TenantIntunePolicies) { $PolicyFound = $false + $tenantPolicy.policy | Add-Member -MemberType NoteProperty -Name 'URLName' -Value $TenantPolicy.Type -Force $TenantPolicyName = if ($TenantPolicy.Policy.displayName) { $TenantPolicy.Policy.displayName } else { $TenantPolicy.Policy.name } foreach ($TemplatePolicy in $TemplateIntuneTemplates) { From a812c13adb3427b68cbead9309f89af1eed38539 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:53:07 +0200 Subject: [PATCH 80/85] email bug fix --- .../Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 | 3 ++- Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 index df5743848ed1..378679ad74bf 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 @@ -31,7 +31,8 @@ function Push-CippDriftManagement { Status = $_.status } } - $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data -CIPPURL $CIPPURL -Tenant $Item.tenant -InputObject 'driftStandard' + $Data | Add-Member -MemberType NoteProperty -Name 'CIPPAction' -Value @('driftManagement') + $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data -CIPPURL $CIPPURL -Tenant $Item.tenant -InputObject 'driftStandard' -AuditLogLink $drift.standardId $CIPPAlert = @{ Type = 'email' Title = $GenerateEmail.title diff --git a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 index 338d4abc637b..54185b16994f 100644 --- a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 @@ -31,7 +31,7 @@ function New-CIPPAlertTemplate { $Title = "CIPP Alert - Standard Drift Detected for $($Tenant)" $DataHTML = ($Data | ConvertTo-Html | Out-String).Replace('
', '
') $IntroText = "

You've setup your instance to receive alerts when a tenant is drifting away from your standard. This seems to have happened! We've found the following deviations.

$dataHTML" - $ButtonUrl = "$CIPPURL/cipp/logs" + $ButtonUrl = "$CIPPURL/tenant/standards/manage-drift?tenantFilter=$($Tenant)&templateId=$($AuditLogLink)" $ButtonText = 'Investigate and remediate deviations' $AfterButtonText = 'Click the button above to go to the logbook and investigate the deviations. You can also use the standards page to remediate the deviations.' } From d5238233ff4983e93bdb6f7773b239d2477bcc59 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Fri, 1 Aug 2025 16:04:31 +0200 Subject: [PATCH 81/85] casing --- .../Standards/Push-CIPPDriftManagement.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 index 378679ad74bf..e5d7fe008cbb 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 @@ -31,8 +31,8 @@ function Push-CippDriftManagement { Status = $_.status } } - $Data | Add-Member -MemberType NoteProperty -Name 'CIPPAction' -Value @('driftManagement') - $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data -CIPPURL $CIPPURL -Tenant $Item.tenant -InputObject 'driftStandard' -AuditLogLink $drift.standardId + + $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data -CIPPURL $CIPPURL -Tenant $Item.Tenant -InputObject 'driftStandard' -AuditLogLink $drift.standardId $CIPPAlert = @{ Type = 'email' Title = $GenerateEmail.title @@ -52,7 +52,7 @@ function Push-CippDriftManagement { Type = 'webhook' Title = $GenerateEmail.title JSONContent = $WebhookData - TenantFilter = $Item.tenant + TenantFilter = $Item.Tenant } Write-Host 'Sending Webhook Content' Send-CIPPAlert @CippAlert -altWebhook $webhook @@ -61,7 +61,7 @@ function Push-CippDriftManagement { Type = 'psa' Title = $GenerateEmail.title HTMLContent = $GenerateEmail.htmlcontent - TenantFilter = $TenantFilter + TenantFilter = $Item.Tenant } Send-CIPPAlert @CIPPAlert return $true From ec60ee41790705b52f5f66f54cd93718c8e64c57 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 1 Aug 2025 10:27:58 -0400 Subject: [PATCH 82/85] add %initialdomain% replace --- Modules/CIPPCore/Public/Get-CIPPTextReplacement.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPTextReplacement.ps1 b/Modules/CIPPCore/Public/Get-CIPPTextReplacement.ps1 index 28ad7d8a7343..68e801215b8a 100644 --- a/Modules/CIPPCore/Public/Get-CIPPTextReplacement.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPTextReplacement.ps1 @@ -26,6 +26,7 @@ function Get-CIPPTextReplacement { '%temp%', '%tenantid%', '%tenantfilter%', + '%initialdomain%', '%tenantname%', '%partnertenantid%', '%samappid%', @@ -37,7 +38,7 @@ function Get-CIPPTextReplacement { '%programfiles(x86)%', '%programdata%' ) - + $Tenant = Get-Tenants -TenantFilter $TenantFilter $CustomerId = $Tenant.customerId @@ -70,6 +71,7 @@ function Get-CIPPTextReplacement { #default replacements for all tenants: %tenantid% becomes $tenant.customerId, %tenantfilter% becomes $tenant.defaultDomainName, %tenantname% becomes $tenant.displayName $Text = $Text -replace '%tenantid%', $Tenant.customerId $Text = $Text -replace '%tenantfilter%', $Tenant.defaultDomainName + $Text = $Text -replace '%initialdomain%', $Tenant.initialDomainName $Text = $Text -replace '%tenantname%', $Tenant.displayName # Partner specific replacements From 4a0f2918ee7483daf3da9b567551e55742683186 Mon Sep 17 00:00:00 2001 From: rvdwegen Date: Fri, 1 Aug 2025 17:04:49 +0200 Subject: [PATCH 83/85] Add missing logging --- .../Tenant/GDAP/Invoke-ExecGDAPRoleTemplate.ps1 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPRoleTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPRoleTemplate.ps1 index ec02efcf937d..58f0b556d73c 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPRoleTemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPRoleTemplate.ps1 @@ -20,8 +20,10 @@ Function Invoke-ExecGDAPRoleTemplate { if ($Request.Query.TemplateId) { $Template = $Templates | Where-Object -Property RowKey -EQ $Request.Query.TemplateId if (!$Template) { + Write-LogMessage -headers $Headers -API $APIName -message "GDAP role template '$($Request.Query.TemplateId)' not found" -Sev 'Warning' $Body = @{} } else { + Write-LogMessage -headers $Headers -API $APIName -message "Retrieved GDAP role template '$($Request.Query.TemplateId)'" -Sev 'Info' $Body = @{ TemplateId = $Template.RowKey RoleMappings = @($Template.RoleMappings | ConvertFrom-Json) @@ -38,6 +40,7 @@ Function Invoke-ExecGDAPRoleTemplate { } Write-Information ($RoleMappings | ConvertTo-Json) Add-CIPPGDAPRoleTemplate -TemplateId $RowKey -RoleMappings $RoleMappings + Write-LogMessage -headers $Headers -API $APIName -message "Added role mappings to GDAP template '$RowKey'" -Sev 'Info' $Body = @{ Results = "Added role mappings to template $RowKey" } @@ -48,10 +51,12 @@ Function Invoke-ExecGDAPRoleTemplate { if ($Template) { $RoleMappings = $Request.Body.RoleMappings Add-CIPPGDAPRoleTemplate -TemplateId $RowKey -RoleMappings $RoleMappings -Overwrite + Write-LogMessage -headers $Headers -API $APIName -message "Updated role mappings for GDAP template '$RowKey'" -Sev 'Info' $Body = @{ Results = "Updated role mappings for template $RowKey" } } else { + Write-LogMessage -headers $Headers -API $APIName -message "GDAP role template '$RowKey' not found for editing" -Sev 'Warning' $Body = @{ Results = "Template $RowKey not found" } @@ -62,16 +67,19 @@ Function Invoke-ExecGDAPRoleTemplate { $Template = $Templates | Where-Object -Property RowKey -EQ $RowKey if ($Template) { Remove-AzDataTableEntity -Force @Table -Entity $Template + Write-LogMessage -headers $Headers -API $APIName -message "Deleted GDAP role template '$RowKey'" -Sev 'Info' $Body = @{ Results = "Deleted template $RowKey" } } else { + Write-LogMessage -headers $Headers -API $APIName -message "GDAP role template '$RowKey' not found for deletion" -Sev 'Warning' $Body = @{ Results = "Template $RowKey not found" } } } default { + Write-LogMessage -headers $Headers -API $APIName -message "Retrieved $($Templates.Count) GDAP role templates" -Sev 'Info' $Results = foreach ($Template in $Templates) { [PSCustomObject]@{ TemplateId = $Template.RowKey From 4c393bae01c3954b9ac1245cd6e9531a9ea1e09f Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Fri, 1 Aug 2025 20:26:19 +0200 Subject: [PATCH 84/85] up version --- version_latest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_latest.txt b/version_latest.txt index 6c9616431ff5..2bf50aaf17a6 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -8.2.2 \ No newline at end of file +8.3.0 From bf1b456e80896617bbcacd34a605595c311ab334 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Fri, 1 Aug 2025 20:33:02 +0200 Subject: [PATCH 85/85] push --- Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 index 75011dba6cdf..5fdabf756b71 100644 --- a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 +++ b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 @@ -72,7 +72,7 @@ function Get-CIPPTenantAlignment { if ($FieldValue -is [System.Boolean]) { $FieldValue = [bool]$FieldValue } elseif ($FieldValue -like '*{*') { - $FieldValue = ConvertFrom-Json -InputObject $FieldValue -ErrorAction SilentlyContinue + $FieldValue = ConvertFrom-Json -Depth 100 -InputObject $FieldValue -ErrorAction SilentlyContinue } else { $FieldValue = [string]$FieldValue }