diff --git a/.github/workflows/dev_clouduptest.yml b/.github/workflows/dev_clouduptest.yml new file mode 100644 index 000000000000..2754bc9b6e7b --- /dev/null +++ b/.github/workflows/dev_clouduptest.yml @@ -0,0 +1,32 @@ +# Docs for the Azure Web Apps Deploy action: https://github.com/azure/functions-action +# More GitHub Actions for Azure: https://github.com/Azure/actions + +name: Build and deploy Powershell project to Azure Function App - clouduptest + +on: + push: + branches: + - dev + workflow_dispatch: + +env: + AZURE_FUNCTIONAPP_PACKAGE_PATH: '.' # set this to the path to your web app project, defaults to the repository root + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: 'Checkout GitHub Action' + uses: actions/checkout@v4 + + - name: 'Run Azure Functions Action' + uses: Azure/functions-action@v1 + id: fa + with: + app-name: 'clouduptest' + slot-name: 'Production' + package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} + publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_9B9E8B9A9BBE446188BCA9F126A1B646 }} + sku: 'flexconsumption' + \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 b/Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 index ad3a24553976..d2838016027c 100644 --- a/Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 +++ b/Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1 @@ -19,6 +19,9 @@ function Add-CIPPScheduledTask { [Parameter(Mandatory = $true, ParameterSetName = 'RunNow')] [string]$RowKey, + [Parameter(Mandatory = $false, ParameterSetName = 'Default')] + [string]$DesiredStartTime = $null, + [Parameter(Mandatory = $false, ParameterSetName = 'Default')] [Parameter(Mandatory = $false, ParameterSetName = 'RunNow')] $Headers @@ -119,8 +122,24 @@ function Add-CIPPScheduledTask { $task.Recurrence.value } - if ([int64]$task.ScheduledTime -eq 0 -or [string]::IsNullOrEmpty($task.ScheduledTime)) { - $task.ScheduledTime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds + if ($DesiredStartTime) { + try { + # Parse the epoch time + $epochSeconds = [int64]$DesiredStartTime + # Set ScheduledTime to the desired time + $task.ScheduledTime = $epochSeconds + } catch { + Write-Warning "Failed to parse DesiredStartTime: $DesiredStartTime. Using provided ScheduledTime." + # Fall back to default + if ([int64]$task.ScheduledTime -eq 0 -or [string]::IsNullOrEmpty($task.ScheduledTime)) { + $task.ScheduledTime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds + } + } + } else { + # No DesiredStartTime - use current behavior (immediate execution) + if ([int64]$task.ScheduledTime -eq 0 -or [string]::IsNullOrEmpty($task.ScheduledTime)) { + $task.ScheduledTime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds + } } $excludedTenants = if ($task.excludedTenants.value) { $task.excludedTenants.value -join ',' @@ -166,6 +185,10 @@ function Add-CIPPScheduledTask { Hidden = [bool]$Hidden Results = 'Planned' } + # Always store DesiredStartTime if provided + if ($DesiredStartTime) { + $entity['DesiredStartTime'] = [string]$DesiredStartTime + } # Store the original tenant filter for group expansion during execution if ($originalTenantFilter -is [PSCustomObject] -and $originalTenantFilter.type -eq 'Group') { @@ -190,6 +213,7 @@ function Add-CIPPScheduledTask { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message return "Could not add task: $ErrorMessage" } + Write-LogMessage -headers $Headers -API 'ScheduledTask' -message "Added task $($entity.Name) with ID $($entity.RowKey)" -Sev 'Info' -Tenant $tenantFilter return "Successfully added task: $($entity.Name)" } } catch { diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertDepTokenExpiry.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertDepTokenExpiry.ps1 index ec55c10bb283..23004aa5c307 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertDepTokenExpiry.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertDepTokenExpiry.ps1 @@ -4,7 +4,7 @@ function Get-CIPPAlertDepTokenExpiry { Entrypoint #> [CmdletBinding()] - Param ( + param ( [Parameter(Mandatory = $false)] [Alias('input')] $InputValue, @@ -13,7 +13,7 @@ function Get-CIPPAlertDepTokenExpiry { try { try { - $DepTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings' -tenantid $TenantFilter).value + $DepTokens = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings' -tenantid $TenantFilter $AlertData = foreach ($Dep in $DepTokens) { if ($Dep.tokenExpirationDateTime -lt (Get-Date).AddDays(30) -and $Dep.tokenExpirationDateTime -gt (Get-Date).AddDays(-7)) { $Message = 'Apple Device Enrollment Program token expiring on {0}' -f $Dep.tokenExpirationDateTime diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNoCAConfig.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNoCAConfig.ps1 index 54f34db7ee17..3c731d4d84c6 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNoCAConfig.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNoCAConfig.ps1 @@ -12,8 +12,13 @@ function Get-CIPPAlertNoCAConfig { ) try { - $CAAvailable = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $TenantFilter -ErrorAction Stop).serviceplans - if ('AAD_PREMIUM' -in $CAAvailable.servicePlanName) { + # Only consider CA available when a SKU that grants it has enabled seats (> 0) + $SubscribedSkus = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/subscribedSkus?`$select=prepaidUnits,servicePlans" -tenantid $TenantFilter -ErrorAction Stop + $CAAvailable = foreach ($sku in $SubscribedSkus) { + if ([int]$sku.prepaidUnits.enabled -gt 0) { $sku.servicePlans } + } + + if (('AAD_PREMIUM' -in $CAAvailable.servicePlanName) -or ('AAD_PREMIUM_P2' -in $CAAvailable.servicePlanName)) { $CAPolicies = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies' -tenantid $TenantFilter) if (!$CAPolicies.id) { $AlertData = 'Conditional Access is available, but no policies could be found.' diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertVppTokenExpiry.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertVppTokenExpiry.ps1 index 9767e24fd5c4..2dab259a662a 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertVppTokenExpiry.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertVppTokenExpiry.ps1 @@ -4,7 +4,7 @@ function Get-CIPPAlertVppTokenExpiry { Entrypoint #> [CmdletBinding()] - Param ( + param ( [Parameter(Mandatory = $false)] [Alias('input')] $InputValue, @@ -12,13 +12,12 @@ function Get-CIPPAlertVppTokenExpiry { ) try { try { - $VppTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceAppManagement/vppTokens' -tenantid $TenantFilter).value + $VppTokens = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceAppManagement/vppTokens' -tenantid $TenantFilter $AlertData = foreach ($Vpp in $VppTokens) { if ($Vpp.state -ne 'valid') { $Message = 'Apple Volume Purchase Program Token is not valid, new token required' $Vpp | Select-Object -Property organizationName, appleId, vppTokenAccountType, @{Name = 'Message'; Expression = { $Message } } - } - if ($Vpp.expirationDateTime -lt (Get-Date).AddDays(30) -and $Vpp.expirationDateTime -gt (Get-Date).AddDays(-7)) { + } elseif ($Vpp.expirationDateTime -lt (Get-Date).AddDays(30).ToUniversalTime() -and $Vpp.expirationDateTime -gt (Get-Date).AddDays(-7).ToUniversalTime()) { $Message = 'Apple Volume Purchase Program token expiring on {0}' -f $Vpp.expirationDateTime $Vpp | Select-Object -Property organizationName, appleId, vppTokenAccountType, @{Name = 'Message'; Expression = { $Message } } } diff --git a/Modules/CIPPCore/Public/Authentication/Get-CippAllowedPermissions.ps1 b/Modules/CIPPCore/Public/Authentication/Get-CippAllowedPermissions.ps1 index 0f11f47d8684..a48bf575c136 100644 --- a/Modules/CIPPCore/Public/Authentication/Get-CippAllowedPermissions.ps1 +++ b/Modules/CIPPCore/Public/Authentication/Get-CippAllowedPermissions.ps1 @@ -70,7 +70,6 @@ function Get-CippAllowedPermissions { # For admin and superadmin: Compute permissions from base role include/exclude rules if ($PrimaryRole -in @('admin', 'superadmin')) { - Write-Information "Computing permissions for $PrimaryRole using base role rules" if ($BaseRole) { # Start with all permissions and apply include/exclude rules @@ -143,7 +142,19 @@ function Get-CippAllowedPermissions { } # Restrict base permissions to only those allowed by custom roles - $RestrictedPermissions = $BasePermissions | Where-Object { $CustomRolePermissions -contains $_ } + # Include Read permissions when ReadWrite permissions are present + $RestrictedPermissions = $BasePermissions | Where-Object { + $Permission = $_ + if ($CustomRolePermissions -contains $Permission) { + $true + } elseif ($Permission -match 'Read$') { + # Check if there's a corresponding ReadWrite permission + $ReadWritePermission = $Permission -replace 'Read', 'ReadWrite' + $CustomRolePermissions -contains $ReadWritePermission + } else { + $false + } + } foreach ($Permission in $RestrictedPermissions) { if ($null -ne $Permission -and $Permission -is [string]) { $AllowedPermissions.Add($Permission) @@ -161,8 +172,6 @@ function Get-CippAllowedPermissions { } # Handle users with only custom roles (no base role) elseif ($CustomRoles.Count -gt 0) { - Write-Information 'Computing permissions for custom roles only' - foreach ($CustomRole in $CustomRoles) { try { $RolePermissions = Get-CIPPRolePermissions -RoleName $CustomRole diff --git a/Modules/CIPPCore/Public/CippQueue/Invoke-ListCippQueue.ps1 b/Modules/CIPPCore/Public/CippQueue/Invoke-ListCippQueue.ps1 index f4f80f4cb5dc..e9c33daf0ebf 100644 --- a/Modules/CIPPCore/Public/CippQueue/Invoke-ListCippQueue.ps1 +++ b/Modules/CIPPCore/Public/CippQueue/Invoke-ListCippQueue.ps1 @@ -15,10 +15,10 @@ function Invoke-ListCippQueue { $CippQueue = Get-CippTable -TableName 'CippQueue' $CippQueueTasks = Get-CippTable -TableName 'CippQueueTasks' $3HoursAgo = (Get-Date).ToUniversalTime().AddHours(-3).ToString('yyyy-MM-ddTHH:mm:ssZ') - $CippQueueData = Get-CIPPAzDataTableEntity @CippQueue -Filter "Timestamp ge datetime'$3HoursAgo'" | Sort-Object -Property Timestamp -Descending + $CippQueueData = Get-CIPPAzDataTableEntity @CippQueue -Filter "PartitionKey eq 'CippQueue' and Timestamp ge datetime'$3HoursAgo'" | Sort-Object -Property Timestamp -Descending $QueueData = foreach ($Queue in $CippQueueData) { - $Tasks = Get-CIPPAzDataTableEntity @CippQueueTasks -Filter "QueueId eq '$($Queue.RowKey)'" | Where-Object { $_.Name } | Select-Object @{n = 'Timestamp'; exp = { $_.Timestamp.DateTime.ToUniversalTime() } }, Name, Status + $Tasks = Get-CIPPAzDataTableEntity @CippQueueTasks -Filter "PartitionKey eq 'Task' and QueueId eq '$($Queue.RowKey)'" | Where-Object { $_.Name } | Select-Object @{n = 'Timestamp'; exp = { $_.Timestamp.DateTime.ToUniversalTime() } }, Name, Status $TaskStatus = @{} $Tasks | Group-Object -Property Status | ForEach-Object { $TaskStatus.$($_.Name) = $_.Count diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListGraphRequest.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListGraphRequest.ps1 index 1ad77fdc3ac7..54ad46f0d529 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListGraphRequest.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListGraphRequest.ps1 @@ -33,6 +33,10 @@ function Invoke-ListGraphRequest { $Parameters.'$expand' = $Request.Query.'$expand' } + if ($Request.Query.expand) { + $Parameters.'expand' = $Request.Query.expand + } + if ($Request.Query.'$top') { $Parameters.'$top' = $Request.Query.'$top' } @@ -120,13 +124,13 @@ function Invoke-ListGraphRequest { try { $Results = Get-GraphRequestList @GraphRequestParams - if ($Results.nextLink) { - Write-Host "NextLink: $($Results.nextLink | Select-Object -Last 1)" - if ($Request.Query.TenantFilter -ne 'AllTenants') { - $Metadata['nextLink'] = $Results.nextLink | Select-Object -Last 1 + if ($Results | Where-Object { $_.PSObject.Properties.Name -contains 'nextLink' }) { + if (![string]::IsNullOrEmpty($Results.nextLink) -and $Request.Query.TenantFilter -ne 'AllTenants') { + Write-Host "NextLink: $($Results.nextLink | Where-Object { $_ } | Select-Object -Last 1)" + $Metadata['nextLink'] = $Results.nextLink | Where-Object { $_ } | Select-Object -Last 1 } - #Results is an array of objects, so we need to remove the last object before returning - $Results = $Results | Select-Object -First ($Results.Count - 1) + # Remove nextLink trailing object only if it’s the last item + $Results = $Results | Where-Object { $_.PSObject.Properties.Name -notcontains 'nextLink' } } if ($Request.Query.ListProperties) { $Columns = ($Results | Select-Object -First 1).PSObject.Properties.Name diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-AddScheduledItem.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-AddScheduledItem.ps1 index 1ec5a2d2c70d..89f5a6c586af 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-AddScheduledItem.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-AddScheduledItem.ps1 @@ -31,7 +31,7 @@ function Invoke-AddScheduledItem { $Result = "Error scheduling task: $($_.Exception.Message)" } } else { - $Result = Add-CIPPScheduledTask -Task $Request.Body -Headers $Request.Headers -hidden $hidden -DisallowDuplicateName $Request.Query.DisallowDuplicateName + $Result = Add-CIPPScheduledTask -Task $Request.Body -Headers $Request.Headers -hidden $hidden -DisallowDuplicateName $Request.Query.DisallowDuplicateName -DesiredStartTime $Request.Body.DesiredStartTime Write-LogMessage -headers $Request.Headers -API $APINAME -message $Result -Sev 'Info' } Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ 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 25fbf73dc0ed..2f6136b4f7c6 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 @@ -17,7 +17,7 @@ function Invoke-ExecRestoreBackup { if ($Request.Body.BackupName -like 'CippBackup_*') { $Table = Get-CippTable -tablename 'CIPPBackup' - $Backup = Get-CippAzDataTableEntity @Table -Filter "RowKey eq '$($Request.Body.BackupName)'" + $Backup = Get-CippAzDataTableEntity @Table -Filter "RowKey eq '$($Request.Body.BackupName)' or OriginalEntityId eq '$($Request.Body.BackupName)'" if ($Backup) { $BackupData = $Backup.Backup | ConvertFrom-Json -ErrorAction SilentlyContinue | Select-Object * -ExcludeProperty ETag, Timestamp $BackupData | ForEach-Object { diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecTenantGroup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecTenantGroup.ps1 index 5ab6cf634b28..eb47c838becc 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecTenantGroup.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecTenantGroup.ps1 @@ -43,7 +43,7 @@ function Invoke-ExecTenantGroup { Add-CIPPAzDataTableEntity @Table -Entity $GroupEntity -Force } - $CurrentMembers = Get-CIPPAzDataTableEntity @MembersTable -Filter "GroupId eq '$groupId'" + $CurrentMembers = Get-CIPPAzDataTableEntity @MembersTable -Filter "PartitionKey eq 'Member' and GroupId eq '$groupId'" $Adds = [System.Collections.Generic.List[string]]::new() $Removes = [System.Collections.Generic.List[string]]::new() diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddPolicy.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddPolicy.ps1 index 985b60203f3d..e1accc58772c 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddPolicy.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddPolicy.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-AddPolicy { +function Invoke-AddPolicy { <# .FUNCTIONALITY Entrypoint @@ -14,8 +14,8 @@ Function Invoke-AddPolicy { $Headers = $Request.Headers Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' - $Tenants = ($Request.Body.tenantFilter.value) - if ('AllTenants' -in $Tenants) { $Tenants = (Get-Tenants).defaultDomainName } + $Tenants = $Request.Body.tenantFilter.value ? $Request.Body.tenantFilter.value : $Request.Body.tenantFilter + if ('AllTenants' -in $Tenants) { $Tetnants = (Get-Tenants).defaultDomainName } $displayname = $Request.Body.displayName $description = $Request.Body.Description $AssignTo = if ($Request.Body.AssignTo -ne 'on') { $Request.Body.AssignTo } @@ -25,7 +25,7 @@ Function Invoke-AddPolicy { $results = foreach ($Tenant in $tenants) { if ($Request.Body.replacemap.$tenant) { - ([pscustomobject]$Request.Body.replacemap.$tenant).psobject.properties | ForEach-Object { $RawJson = $RawJson -replace $_.name, $_.value } + ([pscustomobject]$Request.Body.replacemap.$tenant).psobject.properties | ForEach-Object { $RawJson = $RawJson -replace $_.name, $_.value } } try { Write-Host 'Calling Adding policy' diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-ListGroups.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-ListGroups.ps1 index c0256ffdd24a..db016aa2e7df 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-ListGroups.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-ListGroups.ps1 @@ -19,7 +19,14 @@ function Invoke-ListGroups { $GroupType = $Request.Query.groupType $Members = $Request.Query.members $Owners = $Request.Query.owners - $SelectString = 'id,createdDateTime,displayName,description,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule,groupTypes,onPremisesSyncEnabled,resourceProvisioningOptions,userPrincipalName&$expand=members($select=userPrincipalName)' + + $ExpandMembers = $Request.Query.expandMembers ?? $false + + $SelectString = 'id,createdDateTime,displayName,description,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule,groupTypes,onPremisesSyncEnabled,resourceProvisioningOptions,userPrincipalName' + if ($ExpandMembers -ne $false) { + $SelectString = '{0}&$expand=members($select=userPrincipalName)' -f $SelectString + } + $BulkRequestArrayList = [System.Collections.Generic.List[object]]::new() @@ -86,7 +93,7 @@ function Invoke-ListGroups { $RawGraphRequest = New-GraphBulkRequest -tenantid $TenantFilter -scope 'https://graph.microsoft.com/.default' -Requests @($BulkRequestArrayList) -asapp $true $GraphRequest = [PSCustomObject]@{ groupInfo = ($RawGraphRequest | Where-Object { $_.id -eq 1 }).body | Select-Object *, @{ Name = 'primDomain'; Expression = { $_.mail -split '@' | Select-Object -Last 1 } }, - @{Name = 'teamsEnabled'; Expression = { if ($_.resourceProvisioningOptions -Like '*Team*') { $true } else { $false } } }, + @{Name = 'teamsEnabled'; Expression = { if ($_.resourceProvisioningOptions -like '*Team*') { $true } else { $false } } }, @{Name = 'calculatedGroupType'; Expression = { if ($_.mailEnabled -and $_.securityEnabled) { 'Mail-Enabled Security' } if (!$_.mailEnabled -and $_.securityEnabled) { 'Security' } @@ -129,4 +136,4 @@ function Invoke-ListGroups { Body = $GraphRequest }) -} \ No newline at end of file +} diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecJITAdmin.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecJITAdmin.ps1 index ae45ad066f45..2b06bf09a7c7 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecJITAdmin.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecJITAdmin.ps1 @@ -134,19 +134,21 @@ function Invoke-ExecJITAdmin { if ($Request.Body.useraction -eq 'Create') { Write-LogMessage -Headers $User -API $APIName -tenant $TenantFilter -message "Creating JIT Admin user $($Request.Body.Username)" -Sev 'Info' Write-Information "Creating JIT Admin user $($Request.Body.username)" + $Domain = $Request.Body.Domain.value ? $Request.Body.Domain.value : $Request.Body.Domain + $JITAdmin = @{ User = @{ 'FirstName' = $Request.Body.FirstName 'LastName' = $Request.Body.LastName - 'UserPrincipalName' = "$($Request.Body.Username)@$($Request.Body.Domain.value)" + 'UserPrincipalName' = "$($Request.Body.Username)@$($Domain)" } Expiration = $Expiration Action = 'Create' TenantFilter = $TenantFilter } $CreateResult = Set-CIPPUserJITAdmin @JITAdmin - $Username = "$($Request.Body.Username)@$($Request.Body.Domain.value)" - $Results.Add("Created User: $($Request.Body.Username)@$($Request.Body.Domain.value)") + $Username = "$($Request.Body.Username)@$($Domain)" + $Results.Add("Created User: $Username") if (!$Request.Body.UseTAP) { $Results.Add("Password: $($CreateResult.password)") } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecSetSharePointMember.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecSetSharePointMember.ps1 index 0ac9be97d022..99bc8bc0c144 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecSetSharePointMember.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecSetSharePointMember.ps1 @@ -20,7 +20,12 @@ function Invoke-ExecSetSharePointMember { try { if ($Request.Body.SharePointType -eq 'Group') { - $GroupId = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups?`$filter=mail eq '$($Request.Body.GroupID)' or proxyAddresses/any(x:endsWith(x,'$($Request.Body.GroupID)'))&`$count=true" -ComplexFilter -tenantid $TenantFilter).id + if ($Request.Body.GroupID -match '^[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}$') { + $GroupId = $Request.Body.GroupID + } else { + $GroupId = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups?`$filter=mail eq '$($Request.Body.GroupID)' or proxyAddresses/any(x:endsWith(x,'$($Request.Body.GroupID)')) or mailNickname eq '$($Request.Body.GroupID)'" -ComplexFilter -tenantid $TenantFilter).id + } + if ($Request.Body.Add -eq $true) { $Results = Add-CIPPGroupMember -GroupType 'Team' -GroupID $GroupID -Member $Request.Body.user.value -TenantFilter $TenantFilter -Headers $Headers } else { diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ExecOnboardTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ExecOnboardTenant.ps1 index fdf1b97e79ba..a336637f08a8 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ExecOnboardTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Invoke-ExecOnboardTenant.ps1 @@ -3,7 +3,7 @@ using namespace System.Net function Invoke-ExecOnboardTenant { <# .FUNCTIONALITY - Entrypoint + Entrypoint,AnyTenant .ROLE Tenant.Administration.ReadWrite #> 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 04a61795e46b..54fe9aa99036 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 @@ -47,7 +47,17 @@ function Invoke-CIPPStandardsRun { return } else { Write-Information 'Classic Standards Run' - $AllTasks = Get-CIPPStandards + + $GetStandardParams = @{ + TenantFilter = $TenantFilter + runManually = $runManually + } + + if ($TemplateID) { + $GetStandardParams['TemplateId'] = $TemplateID + } + + $AllTasks = Get-CIPPStandards @GetStandardParams if ($Force.IsPresent) { Write-Information 'Clearing Rerun Cache' diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ExecGitHubAction.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ExecGitHubAction.ps1 index a367c35b9cfb..b7c593da6604 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ExecGitHubAction.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tools/GitHub/Invoke-ExecGitHubAction.ps1 @@ -23,7 +23,7 @@ function Invoke-ExecGitHubAction { $SplatParams = $Parameters | Select-Object -ExcludeProperty Action, TenantFilter | ConvertTo-Json | ConvertFrom-Json -AsHashtable $Table = Get-CIPPTable -TableName Extensionsconfig - $Configuration = ((Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json).GitHub + $Configuration = ((Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -ErrorAction SilentlyContinue).GitHub if (!$Configuration.Enabled) { $Response = Invoke-RestMethod -Uri 'https://cippy.azurewebsites.net/api/ExecGitHubAction' -Method POST -Body ($Parameters | ConvertTo-Json -Depth 10) -ContentType 'application/json' diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExternalTenantInfo.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExternalTenantInfo.ps1 index 52cc699df175..3659f27154dc 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExternalTenantInfo.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListExternalTenantInfo.ps1 @@ -14,67 +14,36 @@ Function Invoke-ListExternalTenantInfo { $Headers = $Request.Headers Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' - - - # Interact with query parameters or the body of the request. - $Tenant = $Request.Query.tenant - $TenantFilter = $Request.Query.tenantFilter - - # Normalize to tenantid and determine if tenant exists - $TenantId = (Invoke-RestMethod -Method GET "https://login.windows.net/$Tenant/.well-known/openid-configuration").token_endpoint.Split('/')[3] - - if ($TenantId) { - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='$TenantId')" -NoAuthCheck $true -tenantid $TenantFilter - $StatusCode = [HttpStatusCode]::OK + $HttpResponse = [HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = "Default response, you should never see this" } - if ($GraphRequest) { - - $TenantDefaultDomain = $GraphRequest.defaultDomainName - - $body = @" - - - - http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetFederationInformation - https://autodiscover-s.outlook.com/autodiscover/autodiscover.svc - - http://www.w3.org/2005/08/addressing/anonymous - - - - - - $TenantDefaultDomain - - - - -"@ - - # Create the headers - $AutoDiscoverHeaders = @{ - 'Content-Type' = 'text/xml; charset=utf-8' - 'SOAPAction' = '"http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetFederationInformation"' - 'User-Agent' = 'AutodiscoverClient' + try { + if ($Request.Query.tenant) { + $Tenant = $Request.Query.tenant + + # Normalize to tenantid and determine if tenant exists + $TenantId = (Invoke-RestMethod -Method GET "https://login.windows.net/$Tenant/.well-known/openid-configuration").token_endpoint.Split('/')[3] + + if ($TenantId) { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='$TenantId')" -NoAuthCheck $true -tenantid $env:TenantID + $StatusCode = [HttpStatusCode]::OK + $HttpResponse.Body = [PSCustomObject]@{ + GraphRequest = $GraphRequest + } + } else { + $HttpResponse.StatusCode = [HttpStatusCode]::BadRequest + $HttpResponse.Body = "Tenant $($Tenant) not found" + } + } else { + $HttpResponse.StatusCode = [HttpStatusCode]::BadRequest + $HttpResponse.Body = "Tenant parameter is required" } - - # Invoke - $Response = Invoke-RestMethod -UseBasicParsing -Method Post -Uri 'https://autodiscover-s.outlook.com/autodiscover/autodiscover.svc' -Body $body -Headers $AutoDiscoverHeaders - - # Return - $TenantDomains = $Response.Envelope.body.GetFederationInformationResponseMessage.response.Domains.Domain | Sort-Object + } catch { + $HttpResponse.StatusCode = [HttpStatusCode]::InternalServerError + $HttpResponse.Body = "Something went wrong while trying to get tenant info for tenant $($Tenant): $($_.Exception.Message)" } - $results = [PSCustomObject]@{ - GraphRequest = $GraphRequest - Domains = @($TenantDomains) - } - - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $results - }) - + Push-OutputBinding -Name Response -Value $HttpResponse } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-PublicPhishingCheck.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-PublicPhishingCheck.ps1 index a4f0f64a00b1..58976b122f30 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-PublicPhishingCheck.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-PublicPhishingCheck.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-PublicPhishingCheck { +function Invoke-PublicPhishingCheck { <# .FUNCTIONALITY Entrypoint @@ -11,7 +11,10 @@ Function Invoke-PublicPhishingCheck { #this has been switched to the external free service by cyberdrain at clone.cipp.app due to extreme numbers of executions if selfhosted. param($Request, $TriggerMetadata) - if ($Request.body.Cloned) { + + $Tenant = Get-Tenants -TenantFilter $Request.body.TenantId + + if ($Request.body.Cloned -and $Tenant.customerId -eq $Request.body.TenantId) { Write-AlertMessage -message $Request.body.AlertMessage -sev 'Alert' -tenant $Request.body.TenantId } diff --git a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 index 5fdabf756b71..8d68ef26e5f3 100644 --- a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 +++ b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 @@ -34,7 +34,7 @@ function Get-CIPPTenantAlignment { $JSON = $_.JSON -replace '"Action":', '"action":' try { $RowKey = $_.RowKey - $Data = $JSON | ConvertFrom-Json -Depth 100 -ErrorAction SilentlyContinue + $Data = $JSON | ConvertFrom-Json -Depth 100 -ErrorAction Stop } catch { Write-Warning "$($RowKey) standard could not be loaded: $($_.Exception.Message)" return @@ -52,7 +52,7 @@ function Get-CIPPTenantAlignment { # Get standards comparison data $StandardsTable = Get-CIPPTable -TableName 'CippStandardsReports' - $AllStandards = Get-CIPPAzDataTableEntity @StandardsTable -Filter "PartitionKey ne 'StandardReport'" + $AllStandards = Get-CIPPAzDataTableEntity @StandardsTable -Filter "PartitionKey ne 'StandardReport' and PartitionKey ne ''" # Filter by tenant if specified $Standards = if ($TenantFilter) { @@ -71,8 +71,16 @@ function Get-CIPPTenantAlignment { # Process field value if ($FieldValue -is [System.Boolean]) { $FieldValue = [bool]$FieldValue - } elseif ($FieldValue -like '*{*') { - $FieldValue = ConvertFrom-Json -Depth 100 -InputObject $FieldValue -ErrorAction SilentlyContinue + } elseif (Test-Json -Json $FieldValue -ErrorAction SilentlyContinue) { + try { + $FieldValue = ConvertFrom-Json -Depth 100 -InputObject $FieldValue -ErrorAction Stop + } catch { + Write-Warning "$($FieldName) standard report could not be loaded: $($_.Exception.Message)" + $FieldValue = [PSCustomObject]@{ + Error = "Invalid JSON format: $($_.Exception.Message)" + OriginalValue = $FieldValue + } + } } else { $FieldValue = [string]$FieldValue } diff --git a/Modules/CIPPCore/Public/GraphHelper/Write-AlertTrace.ps1 b/Modules/CIPPCore/Public/GraphHelper/Write-AlertTrace.ps1 index 9408d9e5dca0..abe5236b8295 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Write-AlertTrace.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Write-AlertTrace.ps1 @@ -3,7 +3,7 @@ function Write-AlertTrace { .FUNCTIONALITY Internal function. Pleases most of Write-AlertTrace for alerting purposes #> - Param( + param( $cmdletName, $data, $tenantFilter @@ -20,6 +20,8 @@ function Write-AlertTrace { $TableRow = @{ 'PartitionKey' = $PartitionKey 'RowKey' = "$($tenantFilter)-$($cmdletName)" + 'CmdletName' = "$cmdletName" + 'Tenant' = "$tenantFilter" 'LogData' = [string]$LogData } $Table.Entity = $TableRow @@ -31,6 +33,8 @@ function Write-AlertTrace { $TableRow = @{ 'PartitionKey' = $PartitionKey 'RowKey' = "$($tenantFilter)-$($cmdletName)" + 'CmdletName' = "$cmdletName" + 'Tenant' = "$tenantFilter" 'LogData' = [string]$LogData } $Table.Entity = $TableRow diff --git a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 index e14bf71d6999..88feab4169e2 100644 --- a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 @@ -153,7 +153,7 @@ function New-CIPPCAPolicy { } foreach ($location in $JSONObj.conditions.locations.includeLocations) { - Write-Information "Replacing $location" + Write-Information "Replacing named location - $location" $lookup = $LocationLookupTable | Where-Object -Property name -EQ $location Write-Information "Found $lookup" if (!$lookup) { continue } @@ -198,6 +198,11 @@ function New-CIPPCAPolicy { } } + if ($JSONObj.conditions.users.includeUsers.Count -eq 0) { + Write-Information 'No users matched in this policy, setting to none' + $JSONObj.conditions.users.includeUsers = 'none' + } + } catch { $ErrorMessage = Get-CippException -Exception $_ Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to replace displayNames for conditional access rule $($JSONObj.displayName). Error: $($ErrorMessage.NormalizedError)" -sev 'Error' -LogData $ErrorMessage @@ -229,14 +234,19 @@ function New-CIPPCAPolicy { if ($DisableSD -eq $true) { #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 'Create CA Policy' -tenant $($Tenant) -message "Disabled Security Defaults for tenant $($TenantFilter)" -Sev 'Info' - Start-Sleep 3 + try { + $null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -Type patch -Body $body -asApp $true -ContentType 'application/json' + Write-LogMessage -Headers $User -API 'Create CA Policy' -tenant $($Tenant) -message "Disabled Security Defaults for tenant $($TenantFilter)" -Sev 'Info' + Start-Sleep 3 + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-Information "Failed to disable security defaults for tenant $($TenantFilter): $($ErrorMessage.NormalizedError)" + } } $RawJSON = ConvertTo-Json -InputObject $JSONObj -Depth 10 -Compress Write-Information $RawJSON try { - Write-Information 'Checking' + Write-Information 'Checking for existing policies' $CheckExististing = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies' -tenantid $TenantFilter -asApp $true | Where-Object -Property displayName -EQ $displayname if ($CheckExististing) { if ($Overwrite -ne $true) { @@ -249,7 +259,7 @@ function New-CIPPCAPolicy { return "Updated policy $displayname for $tenantfilter" } } else { - Write-Information 'Creating' + Write-Information 'Creating new policy' if ($JSONobj.GrantControls.authenticationStrength.policyType -or $JSONObj.$jsonobj.LocationInfo) { Start-Sleep 3 } @@ -260,6 +270,10 @@ function New-CIPPCAPolicy { } catch { $ErrorMessage = Get-CippException -Exception $_ Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to create or update conditional access rule $($JSONObj.displayName): $($ErrorMessage.NormalizedError) " -sev 'Error' -LogData $ErrorMessage + + Write-Warning "Failed to create or update conditional access rule $($JSONObj.displayName): $($ErrorMessage.NormalizedError)" + Write-Information $_.InvocationInfo.PositionMessage + Write-Information ($JSONObj | ConvertTo-Json -Depth 10) throw "Failed to create or update conditional access rule $($JSONObj.displayName): $($ErrorMessage.NormalizedError)" } } diff --git a/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 b/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 index e18f016429a6..c733ad7df034 100644 --- a/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 @@ -30,9 +30,9 @@ function New-CIPPUserTask { value = 'Set-CIPPUserLicense' } Parameters = [pscustomobject]@{ - UserId = $UserObj.id + UserId = $CreationResults.Username APIName = 'Sherweb License Assignment' - AddLicenses = $licenses + AddLicenses = $UserObj.licenses.value } ScheduledTime = 0 #right now, which is in the next 15 minutes and should cover most cases. PostExecution = @{ diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAtpPolicyForO365.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAtpPolicyForO365.ps1 index b570a9206e41..e786db8130b2 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAtpPolicyForO365.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAtpPolicyForO365.ps1 @@ -31,7 +31,7 @@ function Invoke-CIPPStandardAtpPolicyForO365 { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'AtpPolicyForO365' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'AtpPolicyForO365' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'AtpPolicyForO365' if ($TestResult -eq $false) { @@ -40,7 +40,7 @@ function Invoke-CIPPStandardAtpPolicyForO365 { } #we're done. try { $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AtpPolicyForO365' | - Select-Object EnableATPForSPOTeamsODB, EnableSafeDocs, AllowSafeDocsOpen + Select-Object EnableATPForSPOTeamsODB, EnableSafeDocs, AllowSafeDocsOpen } catch { $CurrentState = @{ License = 'This tenant might not be licensed for this feature' diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 index 406f02485f1a..8ff864589e35 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 @@ -32,6 +32,7 @@ function Invoke-CIPPStandardConditionalAccessTemplate { ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'ConditionalAccess' $Table = Get-CippTable -tablename 'templates' $TestResult = Test-CIPPStandardLicense -StandardName 'ConditionalAccessTemplate_general' -TenantFilter $Tenant -RequiredCapabilities @('AAD_PREMIUM', 'AAD_PREMIUM_P2') + $TestP2 = Test-CIPPStandardLicense -StandardName 'ConditionalAccessTemplate_p2' -TenantFilter $Tenant -RequiredCapabilities @('AAD_PREMIUM_P2') if ($TestResult -eq $false) { #writing to each item that the license is not present. $settings.TemplateList | ForEach-Object { @@ -42,9 +43,8 @@ function Invoke-CIPPStandardConditionalAccessTemplate { } #we're done. try { - $AllCAPolicies = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies?$top=999' -tenantid $Tenant - } - catch { + $AllCAPolicies = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies?$top=999' -tenantid $Tenant -asApp $true + } 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 @@ -55,6 +55,13 @@ function Invoke-CIPPStandardConditionalAccessTemplate { try { $Filter = "PartitionKey eq 'CATemplate' and RowKey eq '$($Setting.TemplateList.value)'" $JSONObj = (Get-CippAzDataTableEntity @Table -Filter $Filter).JSON + $Policy = $JSONObj | ConvertFrom-Json + if ($Policy.conditions.userRiskLevels.count -gt 0 -or $Policy.conditions.signInRiskLevels.count -gt 0) { + if (!$TestP2) { + Write-Information "Skipping policy $($Policy.displayName) as it requires AAD Premium P2 license." + continue + } + } $null = New-CIPPCAPolicy -replacePattern 'displayName' -TenantFilter $tenant -state $Setting.state -RawJSON $JSONObj -Overwrite $true -APIName $APIName -Headers $Request.Headers -DisableSD $Setting.DisableSD } catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message @@ -70,7 +77,15 @@ function Invoke-CIPPStandardConditionalAccessTemplate { $policy = $Policies | Where-Object { $_.displayName -eq $Setting.label } $CheckExististing = $AllCAPolicies | Where-Object -Property displayName -EQ $Setting.label if (!$CheckExististing) { - Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue "Policy $($Setting.label) is missing from this tenant." -Tenant $Tenant + if ($Setting.conditions.userRiskLevels.Count -gt 0 -or $Setting.conditions.signInRiskLevels.Count -gt 0) { + if (!$TestP2) { + Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue "Policy $($Setting.label) requires AAD Premium P2 license." -Tenant $Tenant + } else { + Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue "Policy $($Setting.label) is missing from this tenant." -Tenant $Tenant + } + } else { + Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue "Policy $($Setting.label) is missing from this tenant." -Tenant $Tenant + } } else { $CompareObj = ConvertFrom-Json -ErrorAction SilentlyContinue -InputObject (New-CIPPCATemplate -TenantFilter $tenant -JSON $CheckExististing) $Compare = Compare-CIPPIntuneObject -ReferenceObject $policy -DifferenceObject $CompareObj diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultSharingLink.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultSharingLink.ps1 index ae4c238ce051..309027904a55 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultSharingLink.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultSharingLink.ps1 @@ -30,7 +30,7 @@ function Invoke-CIPPStandardDefaultSharingLink { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'DefaultSharingLink' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'DefaultSharingLink' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') # Determine the desired sharing link type (default to Internal if not specified) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 index aca77117f96b..8bb7c18f8ac1 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 @@ -29,7 +29,7 @@ function Invoke-CIPPStandardDeletedUserRentention { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'DeletedUserRentention' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'DeletedUserRentention' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DeletedUserRetention' if ($TestResult -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAddShortcutsToOneDrive.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAddShortcutsToOneDrive.ps1 index d926b5a69270..a8b5229912e5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAddShortcutsToOneDrive.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAddShortcutsToOneDrive.ps1 @@ -29,7 +29,7 @@ function Invoke-CIPPStandardDisableAddShortcutsToOneDrive { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'DisableAddShortcutsToOneDrive' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'DisableAddShortcutsToOneDrive' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableAddShortcutsToOneDrive' if ($TestResult -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 index ad06e06ce457..a57c5a7ffbf7 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 @@ -28,7 +28,7 @@ function Invoke-CIPPStandardDisableM365GroupUsers { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'DisableM365GroupUsers' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'DisableM365GroupUsers' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableM365GroupUsers' if ($TestResult -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 index ffb0dba555dc..e9d8e63be8d0 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 @@ -31,7 +31,7 @@ function Invoke-CIPPStandardDisableReshare { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'DisableReshare' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'DisableReshare' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableReshare' if ($TestResult -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharePointLegacyAuth.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharePointLegacyAuth.ps1 index eda6a2a4014f..14a106926ba5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharePointLegacyAuth.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharePointLegacyAuth.ps1 @@ -32,7 +32,7 @@ function Invoke-CIPPStandardDisableSharePointLegacyAuth { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'DisableSharePointLegacyAuth' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'DisableSharePointLegacyAuth' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableSharePointLegacyAuth' if ($TestResult -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 index 74e2628e219e..20eaf6719e11 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 @@ -28,7 +28,7 @@ function Invoke-CIPPStandardDisableUserSiteCreate { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'DisableUserSiteCreate' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'DisableUserSiteCreate' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableUserSiteCreate' if ($TestResult -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExConnector.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExchangeConnectorTemplate.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExConnector.ps1 rename to Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExchangeConnectorTemplate.ps1 diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 index fac3406a91f8..63ae676c4509 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 @@ -29,7 +29,7 @@ function Invoke-CIPPStandardExcludedfileExt { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'ExcludedfileExt' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'ExcludedfileExt' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'ExcludedfileExt' if ($TestResult -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 index 1cd033c26a67..c3177bd9fb2c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 @@ -37,8 +37,7 @@ function Invoke-CIPPStandardMailContacts { 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 { + } 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 @@ -46,11 +45,12 @@ function Invoke-CIPPStandardMailContacts { $contacts = $settings $TechAndSecurityContacts = @($Contacts.SecurityContact, $Contacts.TechContact) + $state = $CurrentInfo.marketingNotificationEmails -eq $Contacts.MarketingContact -and ` + ($CurrentInfo.securityComplianceNotificationMails -in $TechAndSecurityContacts -or + $CurrentInfo.technicalNotificationMails -in $TechAndSecurityContacts) -and ` + $CurrentInfo.privacyProfile.contactEmail -eq $Contacts.GeneralContact + if ($Settings.remediate -eq $true) { - $state = $CurrentInfo.marketingNotificationEmails -eq $Contacts.MarketingContact -and ` - ($CurrentInfo.securityComplianceNotificationMails -in $TechAndSecurityContacts -or - $CurrentInfo.technicalNotificationMails -in $TechAndSecurityContacts) -and ` - $CurrentInfo.privacyProfile.contactEmail -eq $Contacts.GeneralContact if ($state) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Contact emails are already set.' -sev Info } else { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRestrictThirdPartyStorageServices.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRestrictThirdPartyStorageServices.ps1 index 2b446afcfe9c..a13214d3306f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRestrictThirdPartyStorageServices.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRestrictThirdPartyStorageServices.ps1 @@ -31,7 +31,7 @@ function Invoke-CIPPStandardRestrictThirdPartyStorageServices { param ($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'RestrictThirdPartyStorageServices' - $TestResult = Test-CIPPStandardLicense -StandardName 'ThirdPartyStorageServicesRestricted' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'ThirdPartyStorageServicesRestricted' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') if ($TestResult -eq $false) { Write-Host "We're exiting as the correct license is not present for this standard." diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPAzureB2B.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPAzureB2B.ps1 index 7112e90148c1..736a16f25480 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPAzureB2B.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPAzureB2B.ps1 @@ -30,7 +30,7 @@ function Invoke-CIPPStandardSPAzureB2B { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'SPAzureB2B' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'SPAzureB2B' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') if ($TestResult -eq $false) { Write-Host "We're exiting as the correct license is not present for this standard." diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDirectSharing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDirectSharing.ps1 index b30e945af564..b3acea0aaf26 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDirectSharing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDirectSharing.ps1 @@ -31,7 +31,7 @@ function Invoke-CIPPStandardSPDirectSharing { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'SPDirectSharing' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'SPDirectSharing' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') if ($TestResult -eq $false) { Write-Host "We're exiting as the correct license is not present for this standard." diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisableLegacyWorkflows.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisableLegacyWorkflows.ps1 index 59d08d079528..2d932a3facd3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisableLegacyWorkflows.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisableLegacyWorkflows.ps1 @@ -27,7 +27,7 @@ function Invoke-CIPPStandardSPDisableLegacyWorkflows { https://docs.cipp.app/user-documentation/tenant/standards/list-standards #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'SPDisableLegacyWorkflows' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'SPDisableLegacyWorkflows' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') if ($TestResult -eq $false) { Write-Host "We're exiting as the correct license is not present for this standard." diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisallowInfectedFiles.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisallowInfectedFiles.ps1 index f1883357e1c3..c1d42395b43d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisallowInfectedFiles.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisallowInfectedFiles.ps1 @@ -31,7 +31,7 @@ function Invoke-CIPPStandardSPDisallowInfectedFiles { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'SPDisallowInfectedFiles' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'SPDisallowInfectedFiles' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') if ($TestResult -eq $false) { Write-Host "We're exiting as the correct license is not present for this standard." diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPEmailAttestation.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPEmailAttestation.ps1 index b76fb5939521..6baa5015cdde 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPEmailAttestation.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPEmailAttestation.ps1 @@ -32,7 +32,7 @@ function Invoke-CIPPStandardSPEmailAttestation { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'SPEmailAttestation' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'SPEmailAttestation' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') if ($TestResult -eq $false) { Write-Host "We're exiting as the correct license is not present for this standard." diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPExternalUserExpiration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPExternalUserExpiration.ps1 index 99f563cab686..d8860e81a57d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPExternalUserExpiration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPExternalUserExpiration.ps1 @@ -31,7 +31,7 @@ function Invoke-CIPPStandardSPExternalUserExpiration { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'SPExternalUserExpiration' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'SPExternalUserExpiration' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') if ($TestResult -eq $false) { Write-Host "We're exiting as the correct license is not present for this standard." diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 index 6290bc0ed0f3..68fbff1f4898 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 @@ -31,7 +31,7 @@ function Invoke-CIPPStandardSPFileRequests { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'SPFileRequests' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'SPFileRequests' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') if ($TestResult -eq $false) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'The tenant is not licenced for this standard SPFileRequests' -sev Error diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPSyncButtonState.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPSyncButtonState.ps1 index 279deaac3ba5..eee6e26dec70 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPSyncButtonState.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPSyncButtonState.ps1 @@ -29,7 +29,7 @@ function Invoke-CIPPStandardSPSyncButtonState { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'SPSyncButtonState' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'SPSyncButtonState' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') if ($TestResult -eq $false) { Write-Host "We're exiting as the correct license is not present for this standard." diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTenantDefaultTimezone.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTenantDefaultTimezone.ps1 index aefe21354eb0..67e2223ef016 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTenantDefaultTimezone.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTenantDefaultTimezone.ps1 @@ -29,7 +29,7 @@ function Invoke-CIPPStandardTenantDefaultTimezone { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'TenantDefaultTimezone' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'TenantDefaultTimezone' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'TenantDefaultTimezone' if ($TestResult -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 index baeaed8e7d18..3ec4b1544145 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 @@ -28,7 +28,7 @@ function Invoke-CIPPStandarddisableMacSync { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'disableMacSync' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'disableMacSync' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'disableMacSync' if ($TestResult -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 index 37b1e7653ffd..0b119ef8c0cf 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 @@ -32,7 +32,7 @@ function Invoke-CIPPStandardsharingCapability { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'sharingCapability' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'sharingCapability' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') if ($TestResult -eq $false) { Write-Host "We're exiting as the correct license is not present for this standard." diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingDomainRestriction.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingDomainRestriction.ps1 index 033d49556c15..646c6be19311 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingDomainRestriction.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingDomainRestriction.ps1 @@ -31,7 +31,7 @@ function Invoke-CIPPStandardsharingDomainRestriction { #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'sharingDomainRestriction' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + $TestResult = Test-CIPPStandardLicense -StandardName 'sharingDomainRestriction' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'SHAREPOINTENTERPRISE_EDU','ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') if ($TestResult -eq $false) { Write-Host "We're exiting as the correct license is not present for this standard." diff --git a/Modules/CIPPCore/Public/Test-CIPPGDAPRelationships.ps1 b/Modules/CIPPCore/Public/Test-CIPPGDAPRelationships.ps1 index b9b16bbc0db6..18748ca7e5f4 100644 --- a/Modules/CIPPCore/Public/Test-CIPPGDAPRelationships.ps1 +++ b/Modules/CIPPCore/Public/Test-CIPPGDAPRelationships.ps1 @@ -30,7 +30,7 @@ function Test-CIPPGDAPRelationships { Issue = 'The relationship has global administrator access. Auto-Extend is not available.' Tenant = [string]$Group.customer.displayName Relationship = [string]$Group.displayName - Link = 'https://docs.cipp.app/setup/gdap/troubleshooting#autoextend' + Link = 'https://docs.cipp.app/setup/installation/recommended-roles' }) | Out-Null } diff --git a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 index 3456ae75bd08..1e13a1d5f25c 100644 --- a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 +++ b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 @@ -7,427 +7,431 @@ function Test-CIPPAuditLogRules { $Rows ) - # 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 = '' - ) + try { + # 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" - $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 + # Check partner users for this object GUID foreach ($PartnerUser in $PartnerUsers) { - if ($PartnerUser.id -eq $guid) { + 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 } } } - } - # 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)" + # 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 } } - } - - # 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 ($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 ($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 + 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 - MatchedLogs = 0 - MatchedRules = @() - DataToProcess = @() - } + #$FunctionStartTime = Get-Date - # Get the CacheWebhooks table for removing processed rows - $CacheWebhooksTable = Get-CippTable -TableName 'CacheWebhooks' + $Results = [PSCustomObject]@{ + TotalLogs = 0 + MatchedLogs = 0 + MatchedRules = @() + DataToProcess = @() + } - $ExtendedPropertiesIgnoreList = @( - 'OAuth2:Authorize' - 'OAuth2:Token' - 'SAS:EndAuth' - 'SAS:ProcessAuth' - 'deviceAuth:ReprocessTls' - 'Consent:Set' - ) + # Get the CacheWebhooks table for removing processed rows + $CacheWebhooksTable = Get-CippTable -TableName 'CacheWebhooks' - $TrustedIPTable = Get-CIPPTable -TableName 'trustedIps' - $ConfigTable = Get-CIPPTable -TableName 'WebhookRules' - $ConfigEntries = Get-CIPPAzDataTableEntity @ConfigTable - $Configuration = $ConfigEntries | Where-Object { ($_.Tenants -match $TenantFilter -or $_.Tenants -match 'AllTenants') } | ForEach-Object { - [pscustomobject]@{ - Tenants = ($_.Tenants | ConvertFrom-Json) - Excluded = ($_.excludedTenants | ConvertFrom-Json -ErrorAction SilentlyContinue) - Conditions = $_.Conditions - Actions = $_.Actions - LogType = $_.Type - } - } + $ExtendedPropertiesIgnoreList = @( + 'OAuth2:Authorize' + 'OAuth2:Token' + 'SAS:EndAuth' + 'SAS:ProcessAuth' + 'deviceAuth:ReprocessTls' + 'Consent:Set' + ) - # 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' + $TrustedIPTable = Get-CIPPTable -TableName 'trustedIps' + $ConfigTable = Get-CIPPTable -TableName 'WebhookRules' + $ConfigEntries = Get-CIPPAzDataTableEntity @ConfigTable + $Configuration = $ConfigEntries | Where-Object { ($_.Tenants -match $TenantFilter -or $_.Tenants -match 'AllTenants') } | ForEach-Object { + [pscustomobject]@{ + Tenants = ($_.Tenants | ConvertFrom-Json) + Excluded = ($_.excludedTenants | ConvertFrom-Json -ErrorAction SilentlyContinue) + Conditions = $_.Conditions + Actions = $_.Actions + LogType = $_.Type + } } - ) - $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 + # 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 + $Users = ($Response | Where-Object { $_.id -eq 'users' }).body.value + $Groups = ($Response | Where-Object { $_.id -eq 'groups' }).body.value ?? @() + $Devices = ($Response | Where-Object { $_.id -eq 'devices' }).body.value ?? @() + $ServicePrincipals = ($Response | Where-Object { $_.id -eq 'servicePrincipals' }).body.value - Write-Warning '## Audit Log Configuration ##' - Write-Information ($Configuration | ConvertTo-Json -Depth 10) + Write-Warning '## Audit Log Configuration ##' + Write-Information ($Configuration | ConvertTo-Json -Depth 10) - try { - $LogCount = $Rows.count - $RunGuid = (New-Guid).Guid - Write-Warning "Logs to process: $LogCount - RunGuid: $($RunGuid) - $($TenantFilter)" - $Results.TotalLogs = $LogCount - Write-Information "RunGuid: $RunGuid - Collecting logs" - $SearchResults = $Rows - } catch { - Write-Warning "Error getting audit logs: $($_.Exception.Message)" - Write-LogMessage -API 'Webhooks' -message 'Error Processing Audit logs' -LogData (Get-CippException -Exception $_) -sev Error -tenant $TenantFilter - throw $_ - } + try { + $LogCount = $Rows.count + $RunGuid = (New-Guid).Guid + Write-Warning "Logs to process: $LogCount - RunGuid: $($RunGuid) - $($TenantFilter)" + $Results.TotalLogs = $LogCount + Write-Information "RunGuid: $RunGuid - Collecting logs" + $SearchResults = $Rows + } catch { + Write-Warning "Error getting audit logs: $($_.Exception.Message)" + Write-LogMessage -API 'Webhooks' -message 'Error Processing Audit logs' -LogData (Get-CippException -Exception $_) -sev Error -tenant $TenantFilter + throw $_ + } - if ($LogCount -gt 0) { - $LocationTable = Get-CIPPTable -TableName 'knownlocationdbv2' - $ProcessedData = foreach ($AuditRecord in $SearchResults) { - $RecordStartTime = Get-Date - 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 { - if ($_.Value -in $ExtendedPropertiesIgnoreList) { - #write-warning "No need to process this operation as its in our ignore list. Some extended information: $($data.operation):$($_.Value) - $($TenantFilter)" - continue + if ($LogCount -gt 0) { + $LocationTable = Get-CIPPTable -TableName 'knownlocationdbv2' + $ProcessedData = foreach ($AuditRecord in $SearchResults) { + $RecordStartTime = Get-Date + 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 { + if ($_.Value -in $ExtendedPropertiesIgnoreList) { + #write-warning "No need to process this operation as its in our ignore list. Some extended information: $($data.operation):$($_.Value) - $($TenantFilter)" + continue + } + $Data | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -Force -ErrorAction SilentlyContinue } - $Data | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -Force -ErrorAction SilentlyContinue } - } - if ($Data.DeviceProperties) { - $Data.CIPPDeviceProperties = ($Data.DeviceProperties | ConvertTo-Json -Compress) - $Data.DeviceProperties | ForEach-Object { $Data | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -Force -ErrorAction SilentlyContinue } - } - if ($Data.parameters) { - $Data.CIPPParameters = ($Data.parameters | ConvertTo-Json -Compress) - $Data.parameters | ForEach-Object { $Data | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -Force -ErrorAction SilentlyContinue } - } - if ($Data.ModifiedProperties) { - $Data.CIPPModifiedProperties = ($Data.ModifiedProperties | ConvertTo-Json -Compress) - try { - $Data.ModifiedProperties | ForEach-Object { $Data | Add-Member -NotePropertyName "$($_.Name)" -NotePropertyValue "$($_.NewValue)" -Force -ErrorAction SilentlyContinue } - } catch { - ##write-warning ($Data.ModifiedProperties | ConvertTo-Json -Depth 10) + if ($Data.DeviceProperties) { + $Data.CIPPDeviceProperties = ($Data.DeviceProperties | ConvertTo-Json -Compress) + $Data.DeviceProperties | ForEach-Object { $Data | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -Force -ErrorAction SilentlyContinue } } - try { - $Data.ModifiedProperties | ForEach-Object { $Data | Add-Member -NotePropertyName $("Previous_Value_$($_.Name)") -NotePropertyValue "$($_.OldValue)" -Force -ErrorAction SilentlyContinue } - } catch { - ##write-warning ($Data.ModifiedProperties | ConvertTo-Json -Depth 10) + if ($Data.parameters) { + $Data.CIPPParameters = ($Data.parameters | ConvertTo-Json -Compress) + $Data.parameters | ForEach-Object { $Data | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -Force -ErrorAction SilentlyContinue } + } + if ($Data.ModifiedProperties) { + $Data.CIPPModifiedProperties = ($Data.ModifiedProperties | ConvertTo-Json -Compress) + try { + $Data.ModifiedProperties | ForEach-Object { $Data | Add-Member -NotePropertyName "$($_.Name)" -NotePropertyValue "$($_.NewValue)" -Force -ErrorAction SilentlyContinue } + } catch { + ##write-warning ($Data.ModifiedProperties | ConvertTo-Json -Depth 10) + } + try { + $Data.ModifiedProperties | ForEach-Object { $Data | Add-Member -NotePropertyName $("Previous_Value_$($_.Name)") -NotePropertyValue "$($_.OldValue)" -Force -ErrorAction SilentlyContinue } + } catch { + ##write-warning ($Data.ModifiedProperties | ConvertTo-Json -Depth 10) + } } - } - - - $HasLocationData = $false - if (![string]::IsNullOrEmpty($Data.clientip) -and $Data.clientip -notmatch '[X]+') { - # Ignore IP addresses that have been redacted - $IPRegex = '^(?(?:\d{1,3}(?:\.\d{1,3}){3}|\[[0-9a-fA-F:]+\]|[0-9a-fA-F:]+))(?::\d+)?$' - $Data.clientip = $Data.clientip -replace $IPRegex, '$1' -replace '[\[\]]', '' - # Check if IP is on trusted IP list - $TrustedIP = Get-CIPPAzDataTableEntity @TrustedIPTable -Filter "((PartitionKey eq '$TenantFilter') or (PartitionKey eq 'AllTenants')) and RowKey eq '$($Data.clientip)' and state eq 'Trusted'" - if ($TrustedIP) { - #write-warning "IP $($Data.clientip) is trusted" - $Trusted = $true - } - if (!$Trusted) { - $CacheLookupStartTime = Get-Date - $Location = Get-AzDataTableEntity @LocationTable -Filter "PartitionKey eq 'ip' and RowKey eq '$($Data.clientIp)'" | Select-Object -ExcludeProperty Tenant - $CacheLookupEndTime = Get-Date - $CacheLookupSeconds = ($CacheLookupEndTime - $CacheLookupStartTime).TotalSeconds - Write-Warning "Cache lookup for IP $($Data.clientip) took $CacheLookupSeconds seconds" - - if ($Location) { - $Country = $Location.CountryOrRegion - $City = $Location.City - $Proxy = $Location.Proxy - $hosting = $Location.Hosting - $ASName = $Location.ASName - } else { - try { - $IPLookupStartTime = Get-Date - $Location = Get-CIPPGeoIPLocation -IP $Data.clientip - $IPLookupEndTime = Get-Date - $IPLookupSeconds = ($IPLookupEndTime - $IPLookupStartTime).TotalSeconds - Write-Warning "IP lookup for $($Data.clientip) took $IPLookupSeconds seconds" - } catch { - #write-warning "Unable to get IP location for $($Data.clientip): $($_.Exception.Message)" - } - $Country = if ($Location.countryCode) { $Location.countryCode } else { 'Unknown' } - $City = if ($Location.city) { $Location.city } else { 'Unknown' } - $Proxy = if ($Location.proxy -ne $null) { $Location.proxy } else { 'Unknown' } - $hosting = if ($Location.hosting -ne $null) { $Location.hosting } else { 'Unknown' } - $ASName = if ($Location.asname) { $Location.asname } else { 'Unknown' } - $IP = $Data.ClientIP - $LocationInfo = @{ - RowKey = [string]$Data.clientip - PartitionKey = 'ip' - Tenant = [string]$TenantFilter - CountryOrRegion = "$Country" - City = "$City" - Proxy = "$Proxy" - Hosting = "$hosting" - ASName = "$ASName" - } + $HasLocationData = $false + if (![string]::IsNullOrEmpty($Data.clientip) -and $Data.clientip -notmatch '[X]+') { + # Ignore IP addresses that have been redacted - try { - $null = Add-CIPPAzDataTableEntity @LocationTable -Entity $LocationInfo -Force - } catch { - #write-warning "Failed to add location info for $($Data.clientip) to cache: $($_.Exception.Message)" + $IPRegex = '^(?(?:\d{1,3}(?:\.\d{1,3}){3}|\[[0-9a-fA-F:]+\]|[0-9a-fA-F:]+))(?::\d+)?$' + $Data.clientip = $Data.clientip -replace $IPRegex, '$1' -replace '[\[\]]', '' + # Check if IP is on trusted IP list + $TrustedIP = Get-CIPPAzDataTableEntity @TrustedIPTable -Filter "((PartitionKey eq '$TenantFilter') or (PartitionKey eq 'AllTenants')) and RowKey eq '$($Data.clientip)' and state eq 'Trusted'" + if ($TrustedIP) { + #write-warning "IP $($Data.clientip) is trusted" + $Trusted = $true + } + if (!$Trusted) { + $CacheLookupStartTime = Get-Date + $Location = Get-AzDataTableEntity @LocationTable -Filter "PartitionKey eq 'ip' and RowKey eq '$($Data.clientIp)'" | Select-Object -ExcludeProperty Tenant + $CacheLookupEndTime = Get-Date + $CacheLookupSeconds = ($CacheLookupEndTime - $CacheLookupStartTime).TotalSeconds + Write-Warning "Cache lookup for IP $($Data.clientip) took $CacheLookupSeconds seconds" + + if ($Location) { + $Country = $Location.CountryOrRegion + $City = $Location.City + $Proxy = $Location.Proxy + $hosting = $Location.Hosting + $ASName = $Location.ASName + } else { + try { + $IPLookupStartTime = Get-Date + $Location = Get-CIPPGeoIPLocation -IP $Data.clientip + $IPLookupEndTime = Get-Date + $IPLookupSeconds = ($IPLookupEndTime - $IPLookupStartTime).TotalSeconds + Write-Warning "IP lookup for $($Data.clientip) took $IPLookupSeconds seconds" + } catch { + #write-warning "Unable to get IP location for $($Data.clientip): $($_.Exception.Message)" + } + $Country = if ($Location.countryCode) { $Location.countryCode } else { 'Unknown' } + $City = if ($Location.city) { $Location.city } else { 'Unknown' } + $Proxy = if ($Location.proxy -ne $null) { $Location.proxy } else { 'Unknown' } + $hosting = if ($Location.hosting -ne $null) { $Location.hosting } else { 'Unknown' } + $ASName = if ($Location.asname) { $Location.asname } else { 'Unknown' } + $IP = $Data.ClientIP + $LocationInfo = @{ + RowKey = [string]$Data.clientip + PartitionKey = 'ip' + Tenant = [string]$TenantFilter + CountryOrRegion = "$Country" + City = "$City" + Proxy = "$Proxy" + Hosting = "$hosting" + ASName = "$ASName" + } + + try { + $null = Add-CIPPAzDataTableEntity @LocationTable -Entity $LocationInfo -Force + } catch { + #write-warning "Failed to add location info for $($Data.clientip) to cache: $($_.Exception.Message)" + + } } + $Data.CIPPGeoLocation = $Country + $Data.CIPPBadRepIP = $Proxy + $Data.CIPPHostedIP = $hosting + $Data.CIPPIPDetected = $IP + $Data.CIPPLocationInfo = ($Location | ConvertTo-Json -Compress) + $HasLocationData = $true } - $Data.CIPPGeoLocation = $Country - $Data.CIPPBadRepIP = $Proxy - $Data.CIPPHostedIP = $hosting - $Data.CIPPIPDetected = $IP - $Data.CIPPLocationInfo = ($Location | ConvertTo-Json -Compress) - $HasLocationData = $true } + $Data.AuditRecord = [string]($RootProperties | ConvertTo-Json -Compress) + $Data | Select-Object *, + @{n = 'HasLocationData'; exp = { $HasLocationData } } -ExcludeProperty ExtendedProperties, DeviceProperties, parameters + } catch { + #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 } - $Data.AuditRecord = [string]($RootProperties | ConvertTo-Json -Compress) - $Data | Select-Object *, - @{n = 'HasLocationData'; exp = { $HasLocationData } } -ExcludeProperty ExtendedProperties, DeviceProperties, parameters - } catch { - #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 - } - 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)'" - Remove-AzDataTableEntity @CacheWebhooksTable -Entity $RowEntity -Force - 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-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)'" + Remove-AzDataTableEntity @CacheWebhooksTable -Entity $RowEntity -Force + 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." - #write-warning "Creating filters - $(($ProcessedData.operation | Sort-Object -Unique) -join ',') - $($TenantFilter)" + #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." + #write-warning "Creating filters - $(($ProcessedData.operation | Sort-Object -Unique) -join ',') - $($TenantFilter)" - try { - $Where = foreach ($Config in $Configuration) { - if ($TenantFilter -in $Config.Excluded.value) { - continue - } - $conditions = $Config.Conditions | ConvertFrom-Json | Where-Object { $Config.Input.value -ne '' } - $actions = $Config.Actions - $conditionStrings = [System.Collections.Generic.List[string]]::new() - $CIPPClause = [System.Collections.Generic.List[string]]::new() - $AddedLocationCondition = $false - foreach ($condition in $conditions) { - if ($condition.Property.label -eq 'CIPPGeoLocation' -and !$AddedLocationCondition) { - $conditionStrings.Add("`$_.HasLocationData -eq `$true") - $CIPPClause.Add('HasLocationData is true') - $AddedLocationCondition = $true + try { + $Where = foreach ($Config in $Configuration) { + if ($TenantFilter -in $Config.Excluded.value) { + continue } - $value = if ($condition.Input.value -is [array]) { - $arrayAsString = $condition.Input.value | ForEach-Object { - "'$_'" + $conditions = $Config.Conditions | ConvertFrom-Json | Where-Object { $Config.Input.value -ne '' } + $actions = $Config.Actions + $conditionStrings = [System.Collections.Generic.List[string]]::new() + $CIPPClause = [System.Collections.Generic.List[string]]::new() + $AddedLocationCondition = $false + foreach ($condition in $conditions) { + if ($condition.Property.label -eq 'CIPPGeoLocation' -and !$AddedLocationCondition) { + $conditionStrings.Add("`$_.HasLocationData -eq `$true") + $CIPPClause.Add('HasLocationData is true') + $AddedLocationCondition = $true } - "@($($arrayAsString -join ', '))" - } else { "'$($condition.Input.value)'" } + $value = if ($condition.Input.value -is [array]) { + $arrayAsString = $condition.Input.value | ForEach-Object { + "'$_'" + } + "@($($arrayAsString -join ', '))" + } else { "'$($condition.Input.value)'" } - $conditionStrings.Add("`$(`$_.$($condition.Property.label)) -$($condition.Operator.value) $value") - $CIPPClause.Add("$($condition.Property.label) is $($condition.Operator.label) $value") - } - $finalCondition = $conditionStrings -join ' -AND ' + $conditionStrings.Add("`$(`$_.$($condition.Property.label)) -$($condition.Operator.value) $value") + $CIPPClause.Add("$($condition.Property.label) is $($condition.Operator.label) $value") + } + $finalCondition = $conditionStrings -join ' -AND ' - [PSCustomObject]@{ - clause = $finalCondition - expectedAction = $actions - CIPPClause = $CIPPClause + [PSCustomObject]@{ + clause = $finalCondition + expectedAction = $actions + CIPPClause = $CIPPClause + } } + } catch { + Write-Warning "Error creating where clause: $($_.Exception.Message)" + Write-Information $_.InvocationInfo.PositionMessage + #Write-LogMessage -API 'Webhooks' -message 'Error creating where clause' -LogData (Get-CippException -Exception $_) -sev Error -tenant $TenantFilter + throw $_ } - } catch { - Write-Warning "Error creating where clause: $($_.Exception.Message)" - Write-Information $_.InvocationInfo.PositionMessage - #Write-LogMessage -API 'Webhooks' -message 'Error creating where clause' -LogData (Get-CippException -Exception $_) -sev Error -tenant $TenantFilter - throw $_ - } - $MatchedRules = [System.Collections.Generic.List[string]]::new() - $DataToProcess = foreach ($clause in $Where) { - $ClauseStartTime = Get-Date - Write-Warning "Webhook: Processing clause: $($clause.clause)" - $ReturnedData = $ProcessedData | Where-Object { Invoke-Expression $clause.clause } - if ($ReturnedData) { - Write-Warning "Webhook: There is matching data: $(($ReturnedData.operation | Select-Object -Unique) -join ', ')" - $ReturnedData = foreach ($item in $ReturnedData) { - $item.CIPPAction = $clause.expectedAction - $item.CIPPClause = $clause.CIPPClause -join ' and ' - $MatchedRules.Add($clause.CIPPClause -join ' and ') - $item + $MatchedRules = [System.Collections.Generic.List[string]]::new() + $DataToProcess = foreach ($clause in $Where) { + $ClauseStartTime = Get-Date + Write-Warning "Webhook: Processing clause: $($clause.clause)" + $ReturnedData = $ProcessedData | Where-Object { Invoke-Expression $clause.clause } + if ($ReturnedData) { + Write-Warning "Webhook: There is matching data: $(($ReturnedData.operation | Select-Object -Unique) -join ', ')" + $ReturnedData = foreach ($item in $ReturnedData) { + $item.CIPPAction = $clause.expectedAction + $item.CIPPClause = $clause.CIPPClause -join ' and ' + $MatchedRules.Add($clause.CIPPClause -join ' and ') + $item + } } + $ClauseEndTime = Get-Date + $ClauseSeconds = ($ClauseEndTime - $ClauseStartTime).TotalSeconds + Write-Warning "Task took $ClauseSeconds seconds for clause: $($clause.clause)" + $ReturnedData } - $ClauseEndTime = Get-Date - $ClauseSeconds = ($ClauseEndTime - $ClauseStartTime).TotalSeconds - Write-Warning "Task took $ClauseSeconds seconds for clause: $($clause.clause)" - $ReturnedData + $Results.MatchedRules = @($MatchedRules | Select-Object -Unique) + $Results.MatchedLogs = ($DataToProcess | Measure-Object).Count + $Results.DataToProcess = $DataToProcess } - $Results.MatchedRules = @($MatchedRules | Select-Object -Unique) - $Results.MatchedLogs = ($DataToProcess | Measure-Object).Count - $Results.DataToProcess = $DataToProcess - } - if ($DataToProcess) { - $CippConfigTable = Get-CippTable -tablename Config - $CippConfig = Get-CIPPAzDataTableEntity @CippConfigTable -Filter "PartitionKey eq 'InstanceProperties' and RowKey eq 'CIPPURL'" - $CIPPURL = 'https://{0}' -f $CippConfig.Value - foreach ($AuditLog in $DataToProcess) { - Write-Information "Processing $($AuditLog.operation)" - $Webhook = @{ - Data = $AuditLog - CIPPURL = [string]$CIPPURL - TenantFilter = $TenantFilter - } - try { - Invoke-CippWebhookProcessing @Webhook - } catch { - Write-Warning "Error sending final step of auditlog processing: $($_.Exception.Message)" - Write-Information $_.InvocationInfo.PositionMessage + if ($DataToProcess) { + $CippConfigTable = Get-CippTable -tablename Config + $CippConfig = Get-CIPPAzDataTableEntity @CippConfigTable -Filter "PartitionKey eq 'InstanceProperties' and RowKey eq 'CIPPURL'" + $CIPPURL = 'https://{0}' -f $CippConfig.Value + foreach ($AuditLog in $DataToProcess) { + Write-Information "Processing $($AuditLog.operation)" + $Webhook = @{ + Data = $AuditLog + CIPPURL = [string]$CIPPURL + TenantFilter = $TenantFilter + } + try { + Invoke-CippWebhookProcessing @Webhook + } catch { + Write-Warning "Error sending final step of auditlog processing: $($_.Exception.Message)" + Write-Information $_.InvocationInfo.PositionMessage + } } } - } - try { - Write-Information 'Removing processed rows from cache' - foreach ($Row in $Rows) { - if ($Row.id) { - $RowEntity = Get-CIPPAzDataTableEntity @CacheWebhooksTable -Filter "PartitionKey eq '$TenantFilter' and RowKey eq '$($Row.id)'" - if ($RowEntity) { - Remove-AzDataTableEntity @CacheWebhooksTable -Entity $RowEntity -Force - Write-Information "Removed row $($Row.id) from cache at final pass." + try { + Write-Information 'Removing processed rows from cache' + foreach ($Row in $Rows) { + if ($Row.id) { + $RowEntity = Get-CIPPAzDataTableEntity @CacheWebhooksTable -Filter "PartitionKey eq '$TenantFilter' and RowKey eq '$($Row.id)'" + if ($RowEntity) { + Remove-AzDataTableEntity @CacheWebhooksTable -Entity $RowEntity -Force + Write-Information "Removed row $($Row.id) from cache at final pass." + } } } + } catch { + Write-Information "Error removing rows from cache: $($_.Exception.Message)" } + } catch { - Write-Information "Error removing rows from cache: $($_.Exception.Message)" + Write-Warning "An error occurred during the Test-CIPPAuditLogRules execution: $($_.Exception.Message)" + Write-Information $_.InvocationInfo.PositionMessage } - return $Results } diff --git a/Modules/CippExtensions/Public/HIBP/New-BreachTenantSearch.ps1 b/Modules/CippExtensions/Public/HIBP/New-BreachTenantSearch.ps1 index 971111aa4003..e387161ad89e 100644 --- a/Modules/CippExtensions/Public/HIBP/New-BreachTenantSearch.ps1 +++ b/Modules/CippExtensions/Public/HIBP/New-BreachTenantSearch.ps1 @@ -6,24 +6,28 @@ function New-BreachTenantSearch { ) $Table = Get-CIPPTable -TableName UserBreaches - $LatestBreach = Get-BreachInfo -TenantFilter $TenantFilter + $LatestBreach = Get-BreachInfo -TenantFilter $TenantFilter | Group-Object -Property clientDomain $usersResults = foreach ($domain in $LatestBreach) { - $ExistingBreaches = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$TenantFilter'" - if ($null -eq $domain.result) { - Write-Host "No breaches found for domain $($domain.domain)" + $ExistingBreaches = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$($domain.name)'" + if ($null -eq $domain.Group) { + Write-Host "No breaches found for domain $($domain.name)" continue } - $SumOfBreaches = ($LatestBreach | Measure-Object -Sum -Property found).sum - if ($ExistingBreaches.sum -eq $SumOfBreaches -and $Force.IsPresent -eq $false) { - Write-Host "No new breaches found for tenant $TenantFilter" - continue + $SumOfBreaches = $domain.Count + if ($ExistingBreaches.sum -eq $SumOfBreaches) { + if ($Force.IsPresent -eq $true) { + Write-Host "Forcing update for tenant $TenantFilter" + } else { + Write-Host "No new breaches found for tenant $TenantFilter" + continue + } } @{ - RowKey = $domain.domain + RowKey = $domain.name PartitionKey = $TenantFilter - breaches = "$($LatestBreach.Result | ConvertTo-Json -Depth 10 -Compress)" + breaches = "$($domain.Group | ConvertTo-Json -Depth 10 -Compress)" sum = $SumOfBreaches } } @@ -32,7 +36,7 @@ function New-BreachTenantSearch { if ($usersResults) { try { $null = Add-CIPPAzDataTableEntity @Table -Entity $usersResults -Force - return $LatestBreach.Result + return $LatestBreach.Group } catch { Write-Error "Failed to add breaches to table: $($_.Exception.Message)" return $null diff --git a/version_latest.txt b/version_latest.txt index 2bf50aaf17a6..56b6be4ebb2f 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -8.3.0 +8.3.1